@slock-ai/daemon 0.47.0 → 0.48.0-play.20260513145259
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-77TJIZSE.js → chunk-DOUUVDEA.js} +764 -128
- package/dist/cli/index.js +70 -2
- package/dist/core.js +6 -2
- package/dist/index.js +5 -3
- package/package.json +2 -1
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
} from "./chunk-B7XIMLOT.js";
|
|
12
12
|
|
|
13
13
|
// src/core.ts
|
|
14
|
-
import
|
|
14
|
+
import path16 from "path";
|
|
15
15
|
import os7 from "os";
|
|
16
16
|
import { createRequire } from "module";
|
|
17
17
|
import { accessSync } from "fs";
|
|
18
|
-
import { fileURLToPath } from "url";
|
|
18
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
19
19
|
|
|
20
20
|
// ../shared/src/tracing/index.ts
|
|
21
21
|
var DEFAULT_TRACE_FLAGS = "00";
|
|
@@ -640,6 +640,22 @@ var actionCardActionSchema = z.discriminatedUnion("type", [
|
|
|
640
640
|
channelAddMemberOperationSchema
|
|
641
641
|
]);
|
|
642
642
|
|
|
643
|
+
// ../shared/src/translationLanguages.ts
|
|
644
|
+
var SUPPORTED_TRANSLATION_LANGUAGE_CODES = [
|
|
645
|
+
"en",
|
|
646
|
+
"zh-cn",
|
|
647
|
+
"zh-tw",
|
|
648
|
+
"ja",
|
|
649
|
+
"ko",
|
|
650
|
+
"es",
|
|
651
|
+
"fr",
|
|
652
|
+
"de",
|
|
653
|
+
"pt-br"
|
|
654
|
+
];
|
|
655
|
+
var SUPPORTED_TRANSLATION_LANGUAGE_SET = new Set(
|
|
656
|
+
SUPPORTED_TRANSLATION_LANGUAGE_CODES
|
|
657
|
+
);
|
|
658
|
+
|
|
643
659
|
// ../shared/src/testing/failpoints.ts
|
|
644
660
|
var NoopFailpointRegistry = class {
|
|
645
661
|
get enabled() {
|
|
@@ -709,6 +725,7 @@ var SERVER_CAPABILITY_MATRIX = {
|
|
|
709
725
|
var RUNTIMES = [
|
|
710
726
|
{ id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
|
|
711
727
|
{ id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
|
|
728
|
+
{ id: "pi", displayName: "Pi", binary: "pi", supported: true },
|
|
712
729
|
{ id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
|
|
713
730
|
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
714
731
|
{ id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
|
|
@@ -750,10 +767,10 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
750
767
|
};
|
|
751
768
|
|
|
752
769
|
// src/agentProcessManager.ts
|
|
753
|
-
import { mkdirSync as
|
|
770
|
+
import { mkdirSync as mkdirSync5, readdirSync as readdirSync3, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
754
771
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
755
772
|
import { createHash as createHash2 } from "crypto";
|
|
756
|
-
import
|
|
773
|
+
import path12 from "path";
|
|
757
774
|
import os5 from "os";
|
|
758
775
|
|
|
759
776
|
// src/drivers/claude.ts
|
|
@@ -846,22 +863,23 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
|
|
|
846
863
|
6. **\`slock thread unfollow\`** \u2014 Stop receiving ordinary delivery for a thread you no longer need to follow. This only affects your own agent attention state.
|
|
847
864
|
7. **\`slock message read\`** \u2014 Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\` pagination and \`around\` for centered context.
|
|
848
865
|
8. **\`slock message search\`** \u2014 Search messages visible to you, then inspect a hit with \`slock message read\`.
|
|
849
|
-
9. **\`slock
|
|
850
|
-
10. **\`slock task
|
|
851
|
-
11. **\`slock task
|
|
852
|
-
12. **\`slock task
|
|
853
|
-
13. **\`slock task
|
|
854
|
-
14. **\`slock
|
|
855
|
-
15. **\`slock attachment
|
|
856
|
-
16. **\`slock
|
|
857
|
-
17. **\`slock profile
|
|
858
|
-
18. **\`slock
|
|
859
|
-
19. **\`slock reminder
|
|
860
|
-
20. **\`slock reminder
|
|
861
|
-
21. **\`slock reminder
|
|
862
|
-
22. **\`slock reminder
|
|
863
|
-
23. **\`slock reminder
|
|
864
|
-
24. **\`slock
|
|
866
|
+
9. **\`slock message react\`** \u2014 Add or remove your reaction on a message. Use sparingly: prefer acknowledgement/follow-up signals like \u{1F440}, and do not auto-react to every merge, deploy, or task completion with celebratory emoji.
|
|
867
|
+
10. **\`slock task list\`** \u2014 View a channel's task board.
|
|
868
|
+
11. **\`slock task create\`** \u2014 Create new task-messages in a channel (supports batch titles; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
|
|
869
|
+
12. **\`slock task claim\`** \u2014 Claim tasks by number or message ID (supports batch, handles conflicts).
|
|
870
|
+
13. **\`slock task unclaim\`** \u2014 Release your claim on a task.
|
|
871
|
+
14. **\`slock task update\`** \u2014 Change a task's status (e.g. to in_review or done).
|
|
872
|
+
15. **\`slock attachment upload\`** \u2014 Upload a file to attach to a message. Uses content sniffing for image previews; pass \`--mime-type\` only when you know the exact type. Returns an attachment ID to pass to \`slock message send\`.
|
|
873
|
+
16. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
874
|
+
17. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
|
|
875
|
+
18. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--display-name <name>\`, and \`--description <text>\`. Values must be non-empty. Provide at least one flag per call; multiple flags can be combined.
|
|
876
|
+
19. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
877
|
+
20. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
|
|
878
|
+
21. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
|
|
879
|
+
22. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
|
|
880
|
+
23. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
|
|
881
|
+
24. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
|
|
882
|
+
25. **\`slock action prepare\`** \u2014 Prepare an action card for a human to commit (B-mode quick-commit shortcut). Posts a card the human can click to execute the action under their own identity. Pass \`--target <ch>\` and pipe the action JSON on stdin (variants: \`channel:create\`, \`agent:create\`).
|
|
865
883
|
|
|
866
884
|
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:
|
|
867
885
|
- failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
|
|
@@ -1159,6 +1177,17 @@ Keep the user informed. They cannot see your internal reasoning, so:
|
|
|
1159
1177
|
- For multi-step work, send short progress updates (e.g. "Working on step 2/3\u2026").
|
|
1160
1178
|
- When done, summarize the result.
|
|
1161
1179
|
- Keep updates concise \u2014 one or two sentences. Don't flood the chat.
|
|
1180
|
+
- For long answers where users need the conclusion first but details still matter, put the conclusion and next action outside any collapse, then use sanitized HTML details blocks for optional depth:
|
|
1181
|
+
|
|
1182
|
+
\`\`\`html
|
|
1183
|
+
<details>
|
|
1184
|
+
<summary>Evidence, logs, or edge cases</summary>
|
|
1185
|
+
|
|
1186
|
+
Detailed notes go here.
|
|
1187
|
+
</details>
|
|
1188
|
+
\`\`\`
|
|
1189
|
+
|
|
1190
|
+
Do not hide the main recommendation, blocker, or required action inside \`<details>\`; only fold supporting evidence, logs, alternatives, or extended rationale.
|
|
1162
1191
|
|
|
1163
1192
|
### Conversation etiquette
|
|
1164
1193
|
|
|
@@ -1292,6 +1321,19 @@ function buildMcpSystemPrompt(config, opts) {
|
|
|
1292
1321
|
return buildPrompt(config, "mcp", opts);
|
|
1293
1322
|
}
|
|
1294
1323
|
|
|
1324
|
+
// src/authEnv.ts
|
|
1325
|
+
var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
|
|
1326
|
+
var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
|
|
1327
|
+
function scrubDaemonAuthEnv(env) {
|
|
1328
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1329
|
+
return env;
|
|
1330
|
+
}
|
|
1331
|
+
function scrubDaemonChildEnv(env) {
|
|
1332
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1333
|
+
delete env[SLOCK_AGENT_TOKEN_ENV];
|
|
1334
|
+
return env;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1295
1337
|
// src/drivers/cliTransport.ts
|
|
1296
1338
|
var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1297
1339
|
function runtimeContextEnv(config) {
|
|
@@ -1345,7 +1387,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)}
|
|
|
1345
1387
|
SLOCK_AGENT_TOKEN_FILE: tokenFile,
|
|
1346
1388
|
PATH: `${slockDir}${path.delimiter}${process.env.PATH ?? ""}`
|
|
1347
1389
|
};
|
|
1348
|
-
|
|
1390
|
+
scrubDaemonChildEnv(spawnEnv);
|
|
1349
1391
|
return {
|
|
1350
1392
|
slockDir,
|
|
1351
1393
|
tokenFile,
|
|
@@ -1382,7 +1424,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn) {
|
|
|
1382
1424
|
}
|
|
1383
1425
|
function resolveCommandOnPath(command, deps = {}) {
|
|
1384
1426
|
const platform = deps.platform ?? process.platform;
|
|
1385
|
-
const env = deps.env ?? process.env;
|
|
1427
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
1386
1428
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
1387
1429
|
if (platform === "win32") {
|
|
1388
1430
|
return resolveCommandOnWindows(command, env, execFileSyncFn);
|
|
@@ -1407,7 +1449,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
1407
1449
|
return null;
|
|
1408
1450
|
}
|
|
1409
1451
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
1410
|
-
const env = deps.env ?? process.env;
|
|
1452
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
1411
1453
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
1412
1454
|
try {
|
|
1413
1455
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -1772,7 +1814,7 @@ var ClaudeDriver = class {
|
|
|
1772
1814
|
|
|
1773
1815
|
// src/drivers/codex.ts
|
|
1774
1816
|
import { spawn as spawn2, execSync } from "child_process";
|
|
1775
|
-
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
1817
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
1776
1818
|
import os2 from "os";
|
|
1777
1819
|
import path4 from "path";
|
|
1778
1820
|
function getCodexNotificationErrorMessage(params) {
|
|
@@ -1801,11 +1843,29 @@ function ensureGitRepoForCodex(workingDirectory, deps = {}) {
|
|
|
1801
1843
|
);
|
|
1802
1844
|
}
|
|
1803
1845
|
var CODEX_DESKTOP_BUNDLE_PATH = "/Applications/Codex.app/Contents/Resources/codex";
|
|
1846
|
+
function resolveWindowsSandboxRunner(deps = {}) {
|
|
1847
|
+
const homeDir = deps.homeDir ?? os2.homedir();
|
|
1848
|
+
const sandboxBinDir = path4.join(homeDir, ".codex", ".sandbox-bin");
|
|
1849
|
+
if (!(deps.existsSyncFn ?? existsSync3)(sandboxBinDir)) return null;
|
|
1850
|
+
try {
|
|
1851
|
+
const files = readdirSync2(sandboxBinDir);
|
|
1852
|
+
const runners = files.filter((f) => f.startsWith("codex-command-runner") && f.endsWith(".exe")).sort((a, b) => b.localeCompare(a));
|
|
1853
|
+
if (runners.length > 0) return path4.join(sandboxBinDir, runners[0]);
|
|
1854
|
+
} catch {
|
|
1855
|
+
}
|
|
1856
|
+
return null;
|
|
1857
|
+
}
|
|
1804
1858
|
function resolveCodexCommand(deps = {}) {
|
|
1805
1859
|
const pathCommand = resolveCommandOnPath("codex", deps);
|
|
1806
1860
|
if (pathCommand) return pathCommand;
|
|
1807
|
-
|
|
1808
|
-
|
|
1861
|
+
const platform = deps.platform ?? process.platform;
|
|
1862
|
+
if (platform === "darwin") {
|
|
1863
|
+
return firstExistingPath([CODEX_DESKTOP_BUNDLE_PATH], deps);
|
|
1864
|
+
}
|
|
1865
|
+
if (platform === "win32") {
|
|
1866
|
+
return resolveWindowsSandboxRunner(deps);
|
|
1867
|
+
}
|
|
1868
|
+
return null;
|
|
1809
1869
|
}
|
|
1810
1870
|
function probeCodex(deps = {}) {
|
|
1811
1871
|
if ((deps.platform ?? process.platform) === "win32") {
|
|
@@ -1830,22 +1890,32 @@ function resolveCodexSpawn(commandArgs, deps = {}) {
|
|
|
1830
1890
|
if ((deps.platform ?? process.platform) !== "win32") {
|
|
1831
1891
|
return { command: resolveCodexCommand(deps) ?? "codex", args: commandArgs };
|
|
1832
1892
|
}
|
|
1893
|
+
const execSyncFn = deps.execSyncFn ?? execSync;
|
|
1894
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
1895
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
1833
1896
|
let codexEntry = null;
|
|
1834
1897
|
try {
|
|
1835
|
-
const globalRoot =
|
|
1898
|
+
const globalRoot = execSyncFn("npm root -g", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], env }).trim();
|
|
1836
1899
|
const candidate = path4.join(globalRoot, "@openai", "codex", "bin", "codex.js");
|
|
1837
|
-
if (
|
|
1900
|
+
if (existsSyncFn(candidate)) codexEntry = candidate;
|
|
1838
1901
|
} catch {
|
|
1839
1902
|
}
|
|
1840
1903
|
if (!codexEntry) {
|
|
1841
1904
|
try {
|
|
1842
|
-
const cmdPath =
|
|
1905
|
+
const cmdPath = execSyncFn("where codex", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], env }).trim().split(/\r?\n/)[0];
|
|
1843
1906
|
const candidate = path4.join(path4.dirname(cmdPath), "node_modules", "@openai", "codex", "bin", "codex.js");
|
|
1844
|
-
if (
|
|
1907
|
+
if (existsSyncFn(candidate)) codexEntry = candidate;
|
|
1845
1908
|
} catch {
|
|
1846
1909
|
}
|
|
1847
1910
|
}
|
|
1848
1911
|
if (!codexEntry) {
|
|
1912
|
+
const sandboxRunner = resolveWindowsSandboxRunner(deps);
|
|
1913
|
+
if (sandboxRunner) {
|
|
1914
|
+
return {
|
|
1915
|
+
command: sandboxRunner,
|
|
1916
|
+
args: commandArgs
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1849
1919
|
throw new Error(
|
|
1850
1920
|
"Cannot resolve Codex CLI entry point on Windows. Ensure @openai/codex is installed globally via npm (npm i -g @openai/codex)."
|
|
1851
1921
|
);
|
|
@@ -2296,13 +2366,13 @@ import { spawn as spawn3 } from "child_process";
|
|
|
2296
2366
|
import path5 from "path";
|
|
2297
2367
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
2298
2368
|
function buildCopilotSpawnEnv(ctx) {
|
|
2299
|
-
return {
|
|
2369
|
+
return scrubDaemonChildEnv({
|
|
2300
2370
|
...process.env,
|
|
2301
2371
|
FORCE_COLOR: "0",
|
|
2302
2372
|
NO_COLOR: "1",
|
|
2303
2373
|
...ctx.config.envVars || {},
|
|
2304
2374
|
[SLOCK_HOME_ENV]: resolveSlockHome()
|
|
2305
|
-
};
|
|
2375
|
+
});
|
|
2306
2376
|
}
|
|
2307
2377
|
var CopilotDriver = class {
|
|
2308
2378
|
id = "copilot";
|
|
@@ -2460,13 +2530,13 @@ import { spawn as spawn4, spawnSync } from "child_process";
|
|
|
2460
2530
|
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
2461
2531
|
import path6 from "path";
|
|
2462
2532
|
function buildCursorSpawnEnv(ctx) {
|
|
2463
|
-
return {
|
|
2533
|
+
return scrubDaemonChildEnv({
|
|
2464
2534
|
...process.env,
|
|
2465
2535
|
FORCE_COLOR: "0",
|
|
2466
2536
|
NO_COLOR: "1",
|
|
2467
2537
|
...ctx.config.envVars || {},
|
|
2468
2538
|
[SLOCK_HOME_ENV]: resolveSlockHome()
|
|
2469
|
-
};
|
|
2539
|
+
});
|
|
2470
2540
|
}
|
|
2471
2541
|
var CursorDriver = class {
|
|
2472
2542
|
id = "cursor";
|
|
@@ -2636,7 +2706,7 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
2636
2706
|
}
|
|
2637
2707
|
function runCursorModelsCommand() {
|
|
2638
2708
|
return spawnSync("cursor-agent", ["models"], {
|
|
2639
|
-
env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
2709
|
+
env: scrubDaemonChildEnv({ ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
2640
2710
|
encoding: "utf8",
|
|
2641
2711
|
timeout: 5e3
|
|
2642
2712
|
});
|
|
@@ -2683,7 +2753,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
2683
2753
|
}
|
|
2684
2754
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
|
|
2685
2755
|
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
2686
|
-
const env = deps.env ?? process.env;
|
|
2756
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
2687
2757
|
const winPath = path7.win32;
|
|
2688
2758
|
let geminiEntry = null;
|
|
2689
2759
|
try {
|
|
@@ -2857,13 +2927,16 @@ var GeminiDriver = class {
|
|
|
2857
2927
|
// src/drivers/kimi.ts
|
|
2858
2928
|
import { randomUUID } from "crypto";
|
|
2859
2929
|
import { spawn as spawn6 } from "child_process";
|
|
2860
|
-
import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2930
|
+
import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2861
2931
|
import os3 from "os";
|
|
2862
2932
|
import path8 from "path";
|
|
2863
2933
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
2864
2934
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
2865
2935
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
2866
2936
|
var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
|
|
2937
|
+
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
2938
|
+
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
2939
|
+
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
2867
2940
|
function parseToolArguments(raw) {
|
|
2868
2941
|
if (typeof raw !== "string") return raw;
|
|
2869
2942
|
try {
|
|
@@ -2872,6 +2945,73 @@ function parseToolArguments(raw) {
|
|
|
2872
2945
|
return raw;
|
|
2873
2946
|
}
|
|
2874
2947
|
}
|
|
2948
|
+
function readKimiConfigSource(home = os3.homedir(), env = process.env) {
|
|
2949
|
+
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
2950
|
+
if (inlineConfig && inlineConfig.trim()) {
|
|
2951
|
+
return {
|
|
2952
|
+
raw: inlineConfig,
|
|
2953
|
+
explicitPath: null,
|
|
2954
|
+
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
2955
|
+
};
|
|
2956
|
+
}
|
|
2957
|
+
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
2958
|
+
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path8.join(home, ".kimi", "config.toml");
|
|
2959
|
+
try {
|
|
2960
|
+
return {
|
|
2961
|
+
raw: readFileSync3(configPath, "utf8"),
|
|
2962
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
2963
|
+
sourcePath: configPath
|
|
2964
|
+
};
|
|
2965
|
+
} catch {
|
|
2966
|
+
return {
|
|
2967
|
+
raw: null,
|
|
2968
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
2969
|
+
sourcePath: configPath
|
|
2970
|
+
};
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
function buildKimiSpawnEnv(env = process.env) {
|
|
2974
|
+
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
2975
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
2976
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
2977
|
+
return scrubDaemonChildEnv(spawnEnv);
|
|
2978
|
+
}
|
|
2979
|
+
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
2980
|
+
return {
|
|
2981
|
+
...process.env,
|
|
2982
|
+
...ctx.config.envVars || {},
|
|
2983
|
+
...overrideEnv || {}
|
|
2984
|
+
};
|
|
2985
|
+
}
|
|
2986
|
+
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
2987
|
+
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
2988
|
+
const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
|
|
2989
|
+
const args = [];
|
|
2990
|
+
let configFilePath = null;
|
|
2991
|
+
let configContent = null;
|
|
2992
|
+
if (source.explicitPath) {
|
|
2993
|
+
configFilePath = source.explicitPath;
|
|
2994
|
+
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
2995
|
+
configFilePath = path8.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
2996
|
+
configContent = source.raw;
|
|
2997
|
+
if (opts.writeGeneratedConfig !== false) {
|
|
2998
|
+
writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
2999
|
+
chmodSync(configFilePath, 384);
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
if (configFilePath) {
|
|
3003
|
+
args.push("--config-file", configFilePath);
|
|
3004
|
+
}
|
|
3005
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
3006
|
+
args.push("--model", ctx.config.model);
|
|
3007
|
+
}
|
|
3008
|
+
return {
|
|
3009
|
+
args,
|
|
3010
|
+
env: buildKimiSpawnEnv(env),
|
|
3011
|
+
configFilePath,
|
|
3012
|
+
configContent
|
|
3013
|
+
};
|
|
3014
|
+
}
|
|
2875
3015
|
var KimiDriver = class {
|
|
2876
3016
|
id = "kimi";
|
|
2877
3017
|
lifecycle = {
|
|
@@ -2888,7 +3028,25 @@ var KimiDriver = class {
|
|
|
2888
3028
|
};
|
|
2889
3029
|
model = {
|
|
2890
3030
|
detectedModelsVerifiedAs: "launchable",
|
|
2891
|
-
toLaunchSpec: (modelId) =>
|
|
3031
|
+
toLaunchSpec: (modelId, ctx, opts) => {
|
|
3032
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
3033
|
+
const launchCtx = {
|
|
3034
|
+
...ctx,
|
|
3035
|
+
config: {
|
|
3036
|
+
...ctx.config,
|
|
3037
|
+
model: modelId
|
|
3038
|
+
}
|
|
3039
|
+
};
|
|
3040
|
+
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
3041
|
+
home: opts?.home,
|
|
3042
|
+
writeGeneratedConfig: false
|
|
3043
|
+
});
|
|
3044
|
+
return {
|
|
3045
|
+
args: launch.args,
|
|
3046
|
+
env: launch.env,
|
|
3047
|
+
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
2892
3050
|
};
|
|
2893
3051
|
supportsStdinNotification = true;
|
|
2894
3052
|
mcpToolPrefix = "";
|
|
@@ -2940,6 +3098,7 @@ var KimiDriver = class {
|
|
|
2940
3098
|
}
|
|
2941
3099
|
}
|
|
2942
3100
|
}), "utf8");
|
|
3101
|
+
const launch = buildKimiLaunchOptions(ctx);
|
|
2943
3102
|
const args = [
|
|
2944
3103
|
"--wire",
|
|
2945
3104
|
"--yolo",
|
|
@@ -2948,16 +3107,13 @@ var KimiDriver = class {
|
|
|
2948
3107
|
"--mcp-config-file",
|
|
2949
3108
|
mcpConfigPath,
|
|
2950
3109
|
"--session",
|
|
2951
|
-
this.sessionId
|
|
3110
|
+
this.sessionId,
|
|
3111
|
+
...launch.args
|
|
2952
3112
|
];
|
|
2953
|
-
if (ctx.config.model && ctx.config.model !== "default") {
|
|
2954
|
-
args.push("--model", ctx.config.model);
|
|
2955
|
-
}
|
|
2956
|
-
const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
2957
3113
|
const proc = spawn6("kimi", args, {
|
|
2958
3114
|
cwd: ctx.workingDirectory,
|
|
2959
3115
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2960
|
-
env:
|
|
3116
|
+
env: launch.env,
|
|
2961
3117
|
shell: process.platform === "win32"
|
|
2962
3118
|
});
|
|
2963
3119
|
proc.stdin?.write(JSON.stringify({
|
|
@@ -3074,14 +3230,9 @@ var KimiDriver = class {
|
|
|
3074
3230
|
return detectKimiModels();
|
|
3075
3231
|
}
|
|
3076
3232
|
};
|
|
3077
|
-
function detectKimiModels(home = os3.homedir()) {
|
|
3078
|
-
const
|
|
3079
|
-
|
|
3080
|
-
try {
|
|
3081
|
-
raw = readFileSync3(configPath, "utf8");
|
|
3082
|
-
} catch {
|
|
3083
|
-
return null;
|
|
3084
|
-
}
|
|
3233
|
+
function detectKimiModels(home = os3.homedir(), opts = {}) {
|
|
3234
|
+
const raw = readKimiConfigSource(home, opts.env).raw;
|
|
3235
|
+
if (raw === null) return null;
|
|
3085
3236
|
const models = [];
|
|
3086
3237
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
3087
3238
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -3328,7 +3479,7 @@ function detectOpenCodeModels(home = os4.homedir(), runCommand = runOpenCodeMode
|
|
|
3328
3479
|
}
|
|
3329
3480
|
function runOpenCodeModelsCommand(home) {
|
|
3330
3481
|
const result = spawnSync2("opencode", ["models"], {
|
|
3331
|
-
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
3482
|
+
env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
3332
3483
|
encoding: "utf8",
|
|
3333
3484
|
timeout: 5e3
|
|
3334
3485
|
});
|
|
@@ -3484,6 +3635,305 @@ var OpenCodeDriver = class {
|
|
|
3484
3635
|
}
|
|
3485
3636
|
};
|
|
3486
3637
|
|
|
3638
|
+
// src/drivers/pi.ts
|
|
3639
|
+
import { spawn as spawn8, spawnSync as spawnSync3 } from "child_process";
|
|
3640
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
3641
|
+
import { fileURLToPath } from "url";
|
|
3642
|
+
import path10 from "path";
|
|
3643
|
+
var CHAT_MCP_TOOL_PREFIX2 = "chat_";
|
|
3644
|
+
var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
|
|
3645
|
+
var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
|
|
3646
|
+
var MIN_BUNDLED_PI_VERSION = "0.74.0";
|
|
3647
|
+
function defaultPackageResolver(specifier) {
|
|
3648
|
+
return import.meta.resolve(specifier);
|
|
3649
|
+
}
|
|
3650
|
+
function parseSemver2(version) {
|
|
3651
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
3652
|
+
if (!match) return null;
|
|
3653
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
3654
|
+
}
|
|
3655
|
+
function isSupportedPiVersion(version) {
|
|
3656
|
+
if (!version) return true;
|
|
3657
|
+
const actual = parseSemver2(version);
|
|
3658
|
+
const minimum = parseSemver2(MIN_BUNDLED_PI_VERSION);
|
|
3659
|
+
if (!actual || !minimum) return true;
|
|
3660
|
+
for (let i = 0; i < 3; i += 1) {
|
|
3661
|
+
if (actual[i] > minimum[i]) return true;
|
|
3662
|
+
if (actual[i] < minimum[i]) return false;
|
|
3663
|
+
}
|
|
3664
|
+
return true;
|
|
3665
|
+
}
|
|
3666
|
+
function resolveBundledPiCliPath(resolver = defaultPackageResolver) {
|
|
3667
|
+
try {
|
|
3668
|
+
const mainPath = fileURLToPath(resolver("@earendil-works/pi-coding-agent"));
|
|
3669
|
+
const cliPath = path10.join(path10.dirname(mainPath), "cli.js");
|
|
3670
|
+
return existsSync7(cliPath) ? cliPath : null;
|
|
3671
|
+
} catch {
|
|
3672
|
+
return null;
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
function readBundledPiVersion(cliPath) {
|
|
3676
|
+
const result = spawnSync3(process.execPath, [cliPath, "--version"], {
|
|
3677
|
+
env: scrubDaemonChildEnv({ ...process.env }),
|
|
3678
|
+
encoding: "utf8",
|
|
3679
|
+
timeout: 5e3
|
|
3680
|
+
});
|
|
3681
|
+
if (result.error || result.status !== 0) return null;
|
|
3682
|
+
return String(result.stdout || result.stderr || "").trim() || null;
|
|
3683
|
+
}
|
|
3684
|
+
function unsupportedPiVersionMessage(version) {
|
|
3685
|
+
if (!version || isSupportedPiVersion(version)) return null;
|
|
3686
|
+
return `Bundled Pi CLI ${version} is unsupported; requires Pi >= ${MIN_BUNDLED_PI_VERSION}.`;
|
|
3687
|
+
}
|
|
3688
|
+
function buildPiLaunchOptions(ctx, opts = {}) {
|
|
3689
|
+
const cliPath = opts.cliPath ?? resolveBundledPiCliPath();
|
|
3690
|
+
if (!cliPath) {
|
|
3691
|
+
throw new Error("Bundled Pi CLI not found in @earendil-works/pi-coding-agent");
|
|
3692
|
+
}
|
|
3693
|
+
const piDir = path10.join(ctx.workingDirectory, ".slock", "pi");
|
|
3694
|
+
const sessionDir = path10.join(piDir, "sessions");
|
|
3695
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
3696
|
+
const slock = prepareCliTransport(ctx, {
|
|
3697
|
+
NO_COLOR: "1",
|
|
3698
|
+
PI_CODING_AGENT_DIR: piDir,
|
|
3699
|
+
PI_CODING_AGENT_SESSION_DIR: sessionDir
|
|
3700
|
+
});
|
|
3701
|
+
const systemPromptPath = path10.join(slock.slockDir, "pi-system-prompt.md");
|
|
3702
|
+
writeFileSync7(systemPromptPath, ctx.standingPrompt, { encoding: "utf8", mode: 384 });
|
|
3703
|
+
const args = [
|
|
3704
|
+
cliPath,
|
|
3705
|
+
"--mode",
|
|
3706
|
+
"json",
|
|
3707
|
+
"--session-dir",
|
|
3708
|
+
sessionDir,
|
|
3709
|
+
"--append-system-prompt",
|
|
3710
|
+
systemPromptPath,
|
|
3711
|
+
"--no-context-files",
|
|
3712
|
+
"--no-extensions",
|
|
3713
|
+
"--no-skills",
|
|
3714
|
+
"--no-prompt-templates",
|
|
3715
|
+
"--no-themes"
|
|
3716
|
+
];
|
|
3717
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
3718
|
+
args.push("--model", ctx.config.model);
|
|
3719
|
+
}
|
|
3720
|
+
if (ctx.config.sessionId) {
|
|
3721
|
+
args.push("--session", ctx.config.sessionId);
|
|
3722
|
+
}
|
|
3723
|
+
const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
|
|
3724
|
+
args.push(turnPrompt);
|
|
3725
|
+
return {
|
|
3726
|
+
command: process.execPath,
|
|
3727
|
+
args,
|
|
3728
|
+
env: slock.spawnEnv,
|
|
3729
|
+
sessionDir,
|
|
3730
|
+
systemPromptPath
|
|
3731
|
+
};
|
|
3732
|
+
}
|
|
3733
|
+
function isSystemFirstMessageTask2(message) {
|
|
3734
|
+
return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
|
|
3735
|
+
}
|
|
3736
|
+
function buildPiSystemPrompt(config) {
|
|
3737
|
+
return buildCliTransportSystemPrompt(config, {
|
|
3738
|
+
toolPrefix: CHAT_MCP_TOOL_PREFIX2,
|
|
3739
|
+
extraCriticalRules: [
|
|
3740
|
+
"- Runtime Profile migration controls are not available in the Pi runtime yet. If asked to acknowledge a runtime migration, explain the blocker instead of inventing a command."
|
|
3741
|
+
],
|
|
3742
|
+
postStartupNotes: [
|
|
3743
|
+
"**Pi 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."
|
|
3744
|
+
],
|
|
3745
|
+
includeStdinNotificationSection: false,
|
|
3746
|
+
messageNotificationStyle: "poll"
|
|
3747
|
+
});
|
|
3748
|
+
}
|
|
3749
|
+
function contentText(content) {
|
|
3750
|
+
if (!content) return "";
|
|
3751
|
+
const chunks = [];
|
|
3752
|
+
for (const item of content) {
|
|
3753
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
3754
|
+
chunks.push(item.text);
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
return chunks.join("");
|
|
3758
|
+
}
|
|
3759
|
+
function apiKeyErrorMessage(line) {
|
|
3760
|
+
const trimmed = line.trim();
|
|
3761
|
+
if (!trimmed) return null;
|
|
3762
|
+
if (/no api key found/i.test(trimmed)) return trimmed;
|
|
3763
|
+
if (/api key.+required/i.test(trimmed)) return trimmed;
|
|
3764
|
+
if (/no models available/i.test(trimmed)) return trimmed;
|
|
3765
|
+
return null;
|
|
3766
|
+
}
|
|
3767
|
+
var PiDriver = class {
|
|
3768
|
+
id = "pi";
|
|
3769
|
+
lifecycle = {
|
|
3770
|
+
kind: "per_turn",
|
|
3771
|
+
start: "defer_until_concrete_message",
|
|
3772
|
+
exit: "terminate_on_turn_end",
|
|
3773
|
+
inFlightWake: "coalesce_into_pending"
|
|
3774
|
+
};
|
|
3775
|
+
communication = {
|
|
3776
|
+
chat: "slock_cli",
|
|
3777
|
+
runtimeControl: "none"
|
|
3778
|
+
};
|
|
3779
|
+
session = {
|
|
3780
|
+
recovery: "resume_or_fresh"
|
|
3781
|
+
};
|
|
3782
|
+
model = {
|
|
3783
|
+
detectedModelsVerifiedAs: "launchable",
|
|
3784
|
+
toLaunchSpec: (modelId, ctx) => {
|
|
3785
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
3786
|
+
const launchCtx = {
|
|
3787
|
+
...ctx,
|
|
3788
|
+
config: {
|
|
3789
|
+
...ctx.config,
|
|
3790
|
+
model: modelId
|
|
3791
|
+
}
|
|
3792
|
+
};
|
|
3793
|
+
const launch = buildPiLaunchOptions(launchCtx);
|
|
3794
|
+
return {
|
|
3795
|
+
args: launch.args,
|
|
3796
|
+
env: launch.env,
|
|
3797
|
+
configFiles: [launch.systemPromptPath]
|
|
3798
|
+
};
|
|
3799
|
+
}
|
|
3800
|
+
};
|
|
3801
|
+
supportsStdinNotification = false;
|
|
3802
|
+
mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
|
|
3803
|
+
busyDeliveryMode = "none";
|
|
3804
|
+
terminateProcessOnTurnEnd = true;
|
|
3805
|
+
deferSpawnUntilMessage = true;
|
|
3806
|
+
usesSlockCliForCommunication = true;
|
|
3807
|
+
sessionId = null;
|
|
3808
|
+
sessionAnnounced = false;
|
|
3809
|
+
apiKeyErrorAnnounced = false;
|
|
3810
|
+
turnEnded = false;
|
|
3811
|
+
assistantTextByMessageId = /* @__PURE__ */ new Map();
|
|
3812
|
+
shouldDeferWakeMessage(message) {
|
|
3813
|
+
return isSystemFirstMessageTask2(message);
|
|
3814
|
+
}
|
|
3815
|
+
probe() {
|
|
3816
|
+
const cliPath = resolveBundledPiCliPath();
|
|
3817
|
+
if (!cliPath) return { available: false };
|
|
3818
|
+
const version = readBundledPiVersion(cliPath) || void 0;
|
|
3819
|
+
const unsupportedMessage = unsupportedPiVersionMessage(version);
|
|
3820
|
+
if (unsupportedMessage) {
|
|
3821
|
+
return {
|
|
3822
|
+
available: false,
|
|
3823
|
+
version: `${version} (requires >= ${MIN_BUNDLED_PI_VERSION})`
|
|
3824
|
+
};
|
|
3825
|
+
}
|
|
3826
|
+
return { available: true, version };
|
|
3827
|
+
}
|
|
3828
|
+
async detectModels() {
|
|
3829
|
+
return null;
|
|
3830
|
+
}
|
|
3831
|
+
spawn(ctx) {
|
|
3832
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
3833
|
+
this.sessionAnnounced = false;
|
|
3834
|
+
this.apiKeyErrorAnnounced = false;
|
|
3835
|
+
this.turnEnded = false;
|
|
3836
|
+
this.assistantTextByMessageId.clear();
|
|
3837
|
+
const cliPath = resolveBundledPiCliPath();
|
|
3838
|
+
if (!cliPath) throw new Error("Bundled Pi CLI not found in @earendil-works/pi-coding-agent");
|
|
3839
|
+
const unsupportedMessage = unsupportedPiVersionMessage(readBundledPiVersion(cliPath));
|
|
3840
|
+
if (unsupportedMessage) throw new Error(unsupportedMessage);
|
|
3841
|
+
const launch = buildPiLaunchOptions(ctx, { cliPath });
|
|
3842
|
+
const proc = spawn8(launch.command, launch.args, {
|
|
3843
|
+
cwd: ctx.workingDirectory,
|
|
3844
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
3845
|
+
env: launch.env,
|
|
3846
|
+
shell: process.platform === "win32"
|
|
3847
|
+
});
|
|
3848
|
+
proc.stdin?.end();
|
|
3849
|
+
return { process: proc };
|
|
3850
|
+
}
|
|
3851
|
+
parseLine(line) {
|
|
3852
|
+
let event;
|
|
3853
|
+
try {
|
|
3854
|
+
event = JSON.parse(line);
|
|
3855
|
+
} catch {
|
|
3856
|
+
if (this.apiKeyErrorAnnounced) return [];
|
|
3857
|
+
const message = apiKeyErrorMessage(line);
|
|
3858
|
+
if (!message) return [];
|
|
3859
|
+
this.apiKeyErrorAnnounced = true;
|
|
3860
|
+
this.turnEnded = true;
|
|
3861
|
+
return [
|
|
3862
|
+
{ kind: "error", message },
|
|
3863
|
+
{ kind: "turn_end", sessionId: this.sessionId || void 0 }
|
|
3864
|
+
];
|
|
3865
|
+
}
|
|
3866
|
+
const events = [];
|
|
3867
|
+
if (event.type === "session" && event.id) {
|
|
3868
|
+
this.sessionId = event.id;
|
|
3869
|
+
}
|
|
3870
|
+
if (!this.sessionAnnounced && this.sessionId) {
|
|
3871
|
+
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
3872
|
+
this.sessionAnnounced = true;
|
|
3873
|
+
}
|
|
3874
|
+
switch (event.type) {
|
|
3875
|
+
case "agent_start":
|
|
3876
|
+
case "turn_start":
|
|
3877
|
+
events.push({ kind: "thinking", text: "" });
|
|
3878
|
+
break;
|
|
3879
|
+
case "message_update":
|
|
3880
|
+
case "message_end":
|
|
3881
|
+
if (event.message?.role === "assistant") {
|
|
3882
|
+
const key = event.message.id || "current";
|
|
3883
|
+
const currentText = contentText(event.message.content);
|
|
3884
|
+
const previousText = this.assistantTextByMessageId.get(key) ?? "";
|
|
3885
|
+
if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
|
|
3886
|
+
events.push({ kind: "text", text: currentText.slice(previousText.length) });
|
|
3887
|
+
} else if (currentText && currentText !== previousText) {
|
|
3888
|
+
events.push({ kind: "text", text: currentText });
|
|
3889
|
+
}
|
|
3890
|
+
this.assistantTextByMessageId.set(key, currentText);
|
|
3891
|
+
if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
|
|
3892
|
+
events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
break;
|
|
3896
|
+
case "tool_execution_start":
|
|
3897
|
+
events.push({
|
|
3898
|
+
kind: "tool_call",
|
|
3899
|
+
name: event.toolName || "unknown_tool",
|
|
3900
|
+
input: event.args
|
|
3901
|
+
});
|
|
3902
|
+
break;
|
|
3903
|
+
case "tool_execution_end":
|
|
3904
|
+
events.push({
|
|
3905
|
+
kind: "tool_output",
|
|
3906
|
+
name: event.toolName || "unknown_tool"
|
|
3907
|
+
});
|
|
3908
|
+
if (event.isError) {
|
|
3909
|
+
events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
|
|
3910
|
+
}
|
|
3911
|
+
break;
|
|
3912
|
+
case "compaction_start":
|
|
3913
|
+
events.push({ kind: "compaction_started" });
|
|
3914
|
+
break;
|
|
3915
|
+
case "compaction_end":
|
|
3916
|
+
events.push({ kind: "compaction_finished" });
|
|
3917
|
+
if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
|
|
3918
|
+
break;
|
|
3919
|
+
case "turn_end":
|
|
3920
|
+
case "agent_end":
|
|
3921
|
+
if (!this.turnEnded) {
|
|
3922
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
3923
|
+
this.turnEnded = true;
|
|
3924
|
+
}
|
|
3925
|
+
break;
|
|
3926
|
+
}
|
|
3927
|
+
return events;
|
|
3928
|
+
}
|
|
3929
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
3930
|
+
return null;
|
|
3931
|
+
}
|
|
3932
|
+
buildSystemPrompt(config, _agentId) {
|
|
3933
|
+
return buildPiSystemPrompt(config);
|
|
3934
|
+
}
|
|
3935
|
+
};
|
|
3936
|
+
|
|
3487
3937
|
// src/drivers/index.ts
|
|
3488
3938
|
var driverFactories = {
|
|
3489
3939
|
claude: () => new ClaudeDriver(),
|
|
@@ -3492,7 +3942,8 @@ var driverFactories = {
|
|
|
3492
3942
|
cursor: () => new CursorDriver(),
|
|
3493
3943
|
gemini: () => new GeminiDriver(),
|
|
3494
3944
|
kimi: () => new KimiDriver(),
|
|
3495
|
-
opencode: () => new OpenCodeDriver()
|
|
3945
|
+
opencode: () => new OpenCodeDriver(),
|
|
3946
|
+
pi: () => new PiDriver()
|
|
3496
3947
|
};
|
|
3497
3948
|
function getDriver(runtimeId) {
|
|
3498
3949
|
const createDriver = driverFactories[runtimeId];
|
|
@@ -3505,7 +3956,7 @@ function getDriver(runtimeId) {
|
|
|
3505
3956
|
|
|
3506
3957
|
// src/workspaces.ts
|
|
3507
3958
|
import { readdir, rm, stat } from "fs/promises";
|
|
3508
|
-
import
|
|
3959
|
+
import path11 from "path";
|
|
3509
3960
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
3510
3961
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
3511
3962
|
}
|
|
@@ -3513,7 +3964,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
3513
3964
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
3514
3965
|
return null;
|
|
3515
3966
|
}
|
|
3516
|
-
return
|
|
3967
|
+
return path11.join(dataDir, directoryName);
|
|
3517
3968
|
}
|
|
3518
3969
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
3519
3970
|
return {
|
|
@@ -3562,7 +4013,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
3562
4013
|
return summary;
|
|
3563
4014
|
}
|
|
3564
4015
|
const childSummaries = await Promise.all(
|
|
3565
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
4016
|
+
entries.map((entry) => summarizeWorkspaceEntry(path11.join(dirPath, entry.name), entry))
|
|
3566
4017
|
);
|
|
3567
4018
|
for (const childSummary of childSummaries) {
|
|
3568
4019
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -3581,7 +4032,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
3581
4032
|
if (!entry.isDirectory()) {
|
|
3582
4033
|
return null;
|
|
3583
4034
|
}
|
|
3584
|
-
const dirPath =
|
|
4035
|
+
const dirPath = path11.join(dataDir, entry.name);
|
|
3585
4036
|
try {
|
|
3586
4037
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
3587
4038
|
return {
|
|
@@ -3772,19 +4223,19 @@ function findSessionJsonl(root, predicate) {
|
|
|
3772
4223
|
if (depth < 0 || visited >= maxEntries) return null;
|
|
3773
4224
|
let entries;
|
|
3774
4225
|
try {
|
|
3775
|
-
entries =
|
|
4226
|
+
entries = readdirSync3(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
|
|
3776
4227
|
} catch {
|
|
3777
4228
|
return null;
|
|
3778
4229
|
}
|
|
3779
4230
|
for (const entry of entries) {
|
|
3780
4231
|
if (++visited > maxEntries) return null;
|
|
3781
4232
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
3782
|
-
return
|
|
4233
|
+
return path12.join(dir, entry.name);
|
|
3783
4234
|
}
|
|
3784
4235
|
for (const entry of entries) {
|
|
3785
4236
|
if (++visited > maxEntries) return null;
|
|
3786
4237
|
if (!entry.isDirectory()) continue;
|
|
3787
|
-
const found = visit(
|
|
4238
|
+
const found = visit(path12.join(dir, entry.name), depth - 1);
|
|
3788
4239
|
if (found) return found;
|
|
3789
4240
|
}
|
|
3790
4241
|
return null;
|
|
@@ -3797,10 +4248,10 @@ function safeSessionFilename(value) {
|
|
|
3797
4248
|
}
|
|
3798
4249
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
3799
4250
|
try {
|
|
3800
|
-
const dir =
|
|
3801
|
-
|
|
3802
|
-
const filePath =
|
|
3803
|
-
|
|
4251
|
+
const dir = path12.join(fallbackDir, ".slock", "runtime-sessions");
|
|
4252
|
+
mkdirSync5(dir, { recursive: true });
|
|
4253
|
+
const filePath = path12.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
4254
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
3804
4255
|
type: "runtime_session_handoff",
|
|
3805
4256
|
runtime,
|
|
3806
4257
|
sessionId,
|
|
@@ -3819,7 +4270,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
3819
4270
|
}
|
|
3820
4271
|
}
|
|
3821
4272
|
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
|
|
3822
|
-
const directPath =
|
|
4273
|
+
const directPath = path12.isAbsolute(sessionId) ? sessionId : null;
|
|
3823
4274
|
if (directPath) {
|
|
3824
4275
|
try {
|
|
3825
4276
|
if (statSync2(directPath).isFile()) {
|
|
@@ -3828,7 +4279,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), f
|
|
|
3828
4279
|
} catch {
|
|
3829
4280
|
}
|
|
3830
4281
|
}
|
|
3831
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
4282
|
+
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;
|
|
3832
4283
|
if (!resolvedPath && fallbackDir) {
|
|
3833
4284
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
3834
4285
|
if (fallback) return fallback;
|
|
@@ -4368,6 +4819,17 @@ Success = user starts useful collaboration and setup progresses,
|
|
|
4368
4819
|
not finishing a long onboarding conversation in one channel.
|
|
4369
4820
|
`;
|
|
4370
4821
|
}
|
|
4822
|
+
function createRuntimeTraceCounters() {
|
|
4823
|
+
return {
|
|
4824
|
+
events: 0,
|
|
4825
|
+
toolCalls: 0,
|
|
4826
|
+
toolOutputs: 0,
|
|
4827
|
+
compactionStarts: 0,
|
|
4828
|
+
compactionFinishes: 0,
|
|
4829
|
+
textEvents: 0,
|
|
4830
|
+
thinkingEvents: 0
|
|
4831
|
+
};
|
|
4832
|
+
}
|
|
4371
4833
|
function createGatedSteeringState() {
|
|
4372
4834
|
return {
|
|
4373
4835
|
phase: "idle",
|
|
@@ -4546,8 +5008,111 @@ function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
|
|
|
4546
5008
|
outstandingToolUses: ap.gatedSteering.outstandingToolUses,
|
|
4547
5009
|
compacting: ap.gatedSteering.compacting,
|
|
4548
5010
|
recentStderrCount: ap.recentStderr.length,
|
|
4549
|
-
recentStdoutCount: ap.recentStdout.length
|
|
5011
|
+
recentStdoutCount: ap.recentStdout.length,
|
|
5012
|
+
...runtimeTraceCounterAttrs(ap)
|
|
5013
|
+
}
|
|
5014
|
+
};
|
|
5015
|
+
}
|
|
5016
|
+
function bucketBytes(value) {
|
|
5017
|
+
const bytes = typeof value === "string" ? Buffer.byteLength(value, "utf8") : Math.max(0, Math.floor(value ?? 0));
|
|
5018
|
+
if (bytes === 0) return "0";
|
|
5019
|
+
if (bytes < 1024) return "<1KB";
|
|
5020
|
+
if (bytes < 10 * 1024) return "1KB-10KB";
|
|
5021
|
+
if (bytes < 100 * 1024) return "10KB-100KB";
|
|
5022
|
+
if (bytes < 1024 * 1024) return "100KB-1MB";
|
|
5023
|
+
return "1MB+";
|
|
5024
|
+
}
|
|
5025
|
+
function attachmentBytesBucket(bytes, knownCount) {
|
|
5026
|
+
return knownCount > 0 ? bucketBytes(bytes) : "unknown";
|
|
5027
|
+
}
|
|
5028
|
+
function summarizeMessageInputBytes(messages) {
|
|
5029
|
+
if (!messages || messages.length === 0) {
|
|
5030
|
+
return {
|
|
5031
|
+
runtime_input_messages_count: 0,
|
|
5032
|
+
runtime_input_messages_content_bytes_bucket: "0",
|
|
5033
|
+
runtime_input_attachments_count: 0,
|
|
5034
|
+
runtime_input_image_attachments_count: 0,
|
|
5035
|
+
runtime_input_attachments_size_known_count: 0,
|
|
5036
|
+
runtime_input_attachments_bytes_bucket: "0",
|
|
5037
|
+
runtime_input_image_attachments_size_known_count: 0,
|
|
5038
|
+
runtime_input_image_attachments_bytes_bucket: "0",
|
|
5039
|
+
runtime_input_largest_attachment_bytes_bucket: "0",
|
|
5040
|
+
runtime_input_thread_context_messages_count: 0,
|
|
5041
|
+
runtime_input_thread_context_content_bytes_bucket: "0"
|
|
5042
|
+
};
|
|
5043
|
+
}
|
|
5044
|
+
let contentBytes = 0;
|
|
5045
|
+
let attachmentCount = 0;
|
|
5046
|
+
let imageAttachmentCount = 0;
|
|
5047
|
+
let attachmentSizeKnownCount = 0;
|
|
5048
|
+
let attachmentBytes = 0;
|
|
5049
|
+
let imageAttachmentSizeKnownCount = 0;
|
|
5050
|
+
let imageAttachmentBytes = 0;
|
|
5051
|
+
let largestAttachmentBytes = 0;
|
|
5052
|
+
let threadContextMessages = 0;
|
|
5053
|
+
let threadContextContentBytes = 0;
|
|
5054
|
+
for (const message of messages) {
|
|
5055
|
+
contentBytes += Buffer.byteLength(message.content || "", "utf8");
|
|
5056
|
+
for (const attachment of message.attachments || []) {
|
|
5057
|
+
attachmentCount++;
|
|
5058
|
+
if (typeof attachment.sizeBytes === "number" && Number.isFinite(attachment.sizeBytes) && attachment.sizeBytes >= 0) {
|
|
5059
|
+
attachmentSizeKnownCount++;
|
|
5060
|
+
attachmentBytes += attachment.sizeBytes;
|
|
5061
|
+
largestAttachmentBytes = Math.max(largestAttachmentBytes, attachment.sizeBytes);
|
|
5062
|
+
}
|
|
5063
|
+
if (attachment.mimeType?.startsWith("image/")) {
|
|
5064
|
+
imageAttachmentCount++;
|
|
5065
|
+
if (typeof attachment.sizeBytes === "number" && Number.isFinite(attachment.sizeBytes) && attachment.sizeBytes >= 0) {
|
|
5066
|
+
imageAttachmentSizeKnownCount++;
|
|
5067
|
+
imageAttachmentBytes += attachment.sizeBytes;
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
4550
5070
|
}
|
|
5071
|
+
const joinContext = message.thread_join_context;
|
|
5072
|
+
if (joinContext) {
|
|
5073
|
+
const contextMessages = [joinContext.parent_message, ...joinContext.recent_messages];
|
|
5074
|
+
threadContextMessages += contextMessages.length;
|
|
5075
|
+
for (const contextMessage of contextMessages) {
|
|
5076
|
+
threadContextContentBytes += Buffer.byteLength(contextMessage.content || "", "utf8");
|
|
5077
|
+
}
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
return {
|
|
5081
|
+
runtime_input_messages_count: messages.length,
|
|
5082
|
+
runtime_input_messages_content_bytes_bucket: bucketBytes(contentBytes),
|
|
5083
|
+
runtime_input_attachments_count: attachmentCount,
|
|
5084
|
+
runtime_input_image_attachments_count: imageAttachmentCount,
|
|
5085
|
+
runtime_input_attachments_size_known_count: attachmentSizeKnownCount,
|
|
5086
|
+
runtime_input_attachments_bytes_bucket: attachmentCount > 0 ? attachmentBytesBucket(attachmentBytes, attachmentSizeKnownCount) : "0",
|
|
5087
|
+
runtime_input_image_attachments_size_known_count: imageAttachmentSizeKnownCount,
|
|
5088
|
+
runtime_input_image_attachments_bytes_bucket: imageAttachmentCount > 0 ? attachmentBytesBucket(imageAttachmentBytes, imageAttachmentSizeKnownCount) : "0",
|
|
5089
|
+
runtime_input_largest_attachment_bytes_bucket: attachmentCount > 0 ? attachmentBytesBucket(largestAttachmentBytes, attachmentSizeKnownCount) : "0",
|
|
5090
|
+
runtime_input_thread_context_messages_count: threadContextMessages,
|
|
5091
|
+
runtime_input_thread_context_content_bytes_bucket: bucketBytes(threadContextContentBytes)
|
|
5092
|
+
};
|
|
5093
|
+
}
|
|
5094
|
+
function buildRuntimeInputTraceAttrs(opts) {
|
|
5095
|
+
return {
|
|
5096
|
+
runtime_input_source: opts.source,
|
|
5097
|
+
runtime_input_prompt_bytes_bucket: bucketBytes(opts.prompt),
|
|
5098
|
+
runtime_input_standing_prompt_bytes_bucket: bucketBytes(opts.standingPrompt),
|
|
5099
|
+
runtime_input_resume_prompt_present: Boolean(opts.resumePrompt),
|
|
5100
|
+
runtime_input_resume_prompt_bytes_bucket: bucketBytes(opts.resumePrompt),
|
|
5101
|
+
runtime_input_session_present: opts.sessionIdPresent,
|
|
5102
|
+
runtime_input_native_standing_prompt_present: opts.nativeStandingPrompt,
|
|
5103
|
+
runtime_input_unread_channels_count: opts.unreadSummary ? Object.keys(opts.unreadSummary).length : 0,
|
|
5104
|
+
...summarizeMessageInputBytes(opts.messages)
|
|
5105
|
+
};
|
|
5106
|
+
}
|
|
5107
|
+
function runtimeTraceCounterAttrs(ap) {
|
|
5108
|
+
return {
|
|
5109
|
+
runtime_events_count: ap.runtimeTraceCounters.events,
|
|
5110
|
+
runtime_tool_calls_count: ap.runtimeTraceCounters.toolCalls,
|
|
5111
|
+
runtime_tool_outputs_count: ap.runtimeTraceCounters.toolOutputs,
|
|
5112
|
+
runtime_compaction_starts_count: ap.runtimeTraceCounters.compactionStarts,
|
|
5113
|
+
runtime_compaction_finishes_count: ap.runtimeTraceCounters.compactionFinishes,
|
|
5114
|
+
runtime_text_events_count: ap.runtimeTraceCounters.textEvents,
|
|
5115
|
+
runtime_thinking_events_count: ap.runtimeTraceCounters.thinkingEvents
|
|
4551
5116
|
};
|
|
4552
5117
|
}
|
|
4553
5118
|
function getMessageDeliveryText(driver) {
|
|
@@ -4847,26 +5412,26 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
4847
5412
|
this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
|
|
4848
5413
|
try {
|
|
4849
5414
|
const driver = this.driverResolver(config.runtime || "claude");
|
|
4850
|
-
const agentDataDir =
|
|
5415
|
+
const agentDataDir = path12.join(this.dataDir, agentId);
|
|
4851
5416
|
await mkdir(agentDataDir, { recursive: true });
|
|
4852
5417
|
const runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
4853
|
-
const memoryMdPath =
|
|
5418
|
+
const memoryMdPath = path12.join(agentDataDir, "MEMORY.md");
|
|
4854
5419
|
try {
|
|
4855
5420
|
await access(memoryMdPath);
|
|
4856
5421
|
} catch {
|
|
4857
5422
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
4858
5423
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
4859
5424
|
}
|
|
4860
|
-
const notesDir =
|
|
5425
|
+
const notesDir = path12.join(agentDataDir, "notes");
|
|
4861
5426
|
await mkdir(notesDir, { recursive: true });
|
|
4862
5427
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
4863
5428
|
const seedFiles = buildOnboardingSeedFiles();
|
|
4864
5429
|
for (const { relativePath, content } of seedFiles) {
|
|
4865
|
-
const fullPath =
|
|
5430
|
+
const fullPath = path12.join(agentDataDir, relativePath);
|
|
4866
5431
|
try {
|
|
4867
5432
|
await access(fullPath);
|
|
4868
5433
|
} catch {
|
|
4869
|
-
await mkdir(
|
|
5434
|
+
await mkdir(path12.dirname(fullPath), { recursive: true });
|
|
4870
5435
|
await writeFile(fullPath, content);
|
|
4871
5436
|
}
|
|
4872
5437
|
}
|
|
@@ -4874,17 +5439,21 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
4874
5439
|
const isResume = !!runtimeConfig.sessionId;
|
|
4875
5440
|
const standingPrompt = driver.buildSystemPrompt(runtimeConfig, agentId);
|
|
4876
5441
|
let prompt;
|
|
5442
|
+
let promptSource;
|
|
4877
5443
|
if (runtimeConfig.runtimeProfileControl && !wakeMessage) {
|
|
4878
5444
|
prompt = driver.supportsNativeStandingPrompt ? NATIVE_STANDING_PROMPT_STARTUP_INPUT : formatRuntimeProfileControlStartupInput(runtimeConfig.runtimeProfileControl, driver);
|
|
5445
|
+
promptSource = "runtime_profile_control";
|
|
4879
5446
|
} else if (isResume && resumePrompt) {
|
|
4880
5447
|
prompt = resumePrompt;
|
|
4881
5448
|
prompt += getBusyDeliveryNote(driver);
|
|
5449
|
+
promptSource = "resume_prompt";
|
|
4882
5450
|
} else if (wakeMessage) {
|
|
4883
5451
|
const runtimeProfileControlPrompt = formatRuntimeProfileControlPrompt([wakeMessage]);
|
|
4884
5452
|
const channelLabel = formatChannelLabel(wakeMessage);
|
|
4885
5453
|
prompt = runtimeProfileControlPrompt ?? `New message received:
|
|
4886
5454
|
|
|
4887
5455
|
${formatIncomingMessage(wakeMessage, driver)}`;
|
|
5456
|
+
promptSource = runtimeProfileControlPrompt ? "runtime_profile_control_message" : "wake_message";
|
|
4888
5457
|
if (!runtimeProfileControlPrompt && unreadSummary && Object.keys(unreadSummary).length > 0) {
|
|
4889
5458
|
const otherUnread = Object.entries(unreadSummary).filter(([key]) => key !== channelLabel);
|
|
4890
5459
|
if (otherUnread.length > 0) {
|
|
@@ -4918,12 +5487,25 @@ IMPORTANT: If the message requires multi-step work (e.g. research, code changes,
|
|
|
4918
5487
|
prompt += `
|
|
4919
5488
|
|
|
4920
5489
|
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)}`;
|
|
5490
|
+
promptSource = "resume_unread_summary";
|
|
4921
5491
|
} else if (isResume) {
|
|
4922
5492
|
prompt = `No new messages while you were away. Nothing to do \u2014 just stop. ${getMessageDeliveryText(driver)}`;
|
|
4923
5493
|
prompt += getBusyDeliveryNote(driver);
|
|
5494
|
+
promptSource = "resume_empty";
|
|
4924
5495
|
} else {
|
|
4925
5496
|
prompt = driver.supportsNativeStandingPrompt ? NATIVE_STANDING_PROMPT_STARTUP_INPUT : standingPrompt;
|
|
5497
|
+
promptSource = "cold_start";
|
|
4926
5498
|
}
|
|
5499
|
+
const runtimeInputTraceAttrs = buildRuntimeInputTraceAttrs({
|
|
5500
|
+
source: promptSource,
|
|
5501
|
+
prompt,
|
|
5502
|
+
standingPrompt,
|
|
5503
|
+
resumePrompt,
|
|
5504
|
+
messages: wakeMessage ? [wakeMessage] : void 0,
|
|
5505
|
+
unreadSummary,
|
|
5506
|
+
sessionIdPresent: isResume,
|
|
5507
|
+
nativeStandingPrompt: Boolean(driver.supportsNativeStandingPrompt)
|
|
5508
|
+
});
|
|
4927
5509
|
const effectiveConfig = await this.buildSpawnConfig(agentId, runtimeConfig);
|
|
4928
5510
|
const canDeferEmptyStart = driver.deferSpawnUntilMessage === true && !wakeMessage && !runtimeConfig.runtimeProfileControl && (!unreadSummary || Object.keys(unreadSummary).length === 0);
|
|
4929
5511
|
if (canDeferEmptyStart) {
|
|
@@ -4984,6 +5566,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
4984
5566
|
lastRuntimeEventAt: Date.now(),
|
|
4985
5567
|
runtimeProgressStaleSince: null,
|
|
4986
5568
|
runtimeTraceSpan: null,
|
|
5569
|
+
runtimeTraceCounters: createRuntimeTraceCounters(),
|
|
4987
5570
|
lastActivity: "",
|
|
4988
5571
|
lastActivityDetail: "",
|
|
4989
5572
|
activityClientSeq: 0,
|
|
@@ -5005,7 +5588,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5005
5588
|
sessionId: effectiveConfig.sessionId || null,
|
|
5006
5589
|
launchId: launchId || null
|
|
5007
5590
|
});
|
|
5008
|
-
this.startRuntimeTrace(agentId, agentProcess, "spawn", wakeMessage ? [wakeMessage] : void 0);
|
|
5591
|
+
this.startRuntimeTrace(agentId, agentProcess, "spawn", wakeMessage ? [wakeMessage] : void 0, runtimeInputTraceAttrs);
|
|
5009
5592
|
this.agentsStarting.delete(agentId);
|
|
5010
5593
|
if (config.runtimeProfileControl) {
|
|
5011
5594
|
this.ackInjectedRuntimeProfileControl(agentId, config.runtimeProfileControl, agentProcess.launchId);
|
|
@@ -5099,6 +5682,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5099
5682
|
expectedTerminationReason: ap.expectedTerminationReason || void 0,
|
|
5100
5683
|
exitCode: finalCode,
|
|
5101
5684
|
exitSignal: finalSignal,
|
|
5685
|
+
...runtimeTraceCounterAttrs(ap),
|
|
5102
5686
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "process_exit")
|
|
5103
5687
|
});
|
|
5104
5688
|
if (processEndedCleanly) {
|
|
@@ -5526,7 +6110,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5526
6110
|
return true;
|
|
5527
6111
|
}
|
|
5528
6112
|
async resetWorkspace(agentId) {
|
|
5529
|
-
const agentDataDir =
|
|
6113
|
+
const agentDataDir = path12.join(this.dataDir, agentId);
|
|
5530
6114
|
try {
|
|
5531
6115
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
5532
6116
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -5563,7 +6147,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5563
6147
|
return result;
|
|
5564
6148
|
}
|
|
5565
6149
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
5566
|
-
const workspacePath =
|
|
6150
|
+
const workspacePath = path12.join(this.dataDir, agentId);
|
|
5567
6151
|
return {
|
|
5568
6152
|
agentId,
|
|
5569
6153
|
launchId,
|
|
@@ -5811,7 +6395,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5811
6395
|
}
|
|
5812
6396
|
// Workspace file browsing
|
|
5813
6397
|
async getFileTree(agentId, dirPath) {
|
|
5814
|
-
const agentDir =
|
|
6398
|
+
const agentDir = path12.join(this.dataDir, agentId);
|
|
5815
6399
|
try {
|
|
5816
6400
|
await stat2(agentDir);
|
|
5817
6401
|
} catch {
|
|
@@ -5819,8 +6403,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5819
6403
|
}
|
|
5820
6404
|
let targetDir = agentDir;
|
|
5821
6405
|
if (dirPath) {
|
|
5822
|
-
const resolved =
|
|
5823
|
-
if (!resolved.startsWith(agentDir +
|
|
6406
|
+
const resolved = path12.resolve(agentDir, dirPath);
|
|
6407
|
+
if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
|
|
5824
6408
|
return [];
|
|
5825
6409
|
}
|
|
5826
6410
|
targetDir = resolved;
|
|
@@ -5828,14 +6412,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5828
6412
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
5829
6413
|
}
|
|
5830
6414
|
async readFile(agentId, filePath) {
|
|
5831
|
-
const agentDir =
|
|
5832
|
-
const resolved =
|
|
5833
|
-
if (!resolved.startsWith(agentDir +
|
|
6415
|
+
const agentDir = path12.join(this.dataDir, agentId);
|
|
6416
|
+
const resolved = path12.resolve(agentDir, filePath);
|
|
6417
|
+
if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
|
|
5834
6418
|
throw new Error("Access denied");
|
|
5835
6419
|
}
|
|
5836
6420
|
const info = await stat2(resolved);
|
|
5837
6421
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
5838
|
-
const ext =
|
|
6422
|
+
const ext = path12.extname(resolved).toLowerCase();
|
|
5839
6423
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
5840
6424
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
5841
6425
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -5870,13 +6454,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5870
6454
|
const agent = this.agents.get(agentId);
|
|
5871
6455
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
5872
6456
|
const home = os5.homedir();
|
|
5873
|
-
const workspaceDir =
|
|
6457
|
+
const workspaceDir = path12.join(this.dataDir, agentId);
|
|
5874
6458
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
5875
6459
|
const globalResults = await Promise.all(
|
|
5876
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
6460
|
+
paths.global.map((p) => this.scanSkillsDir(path12.join(home, p)))
|
|
5877
6461
|
);
|
|
5878
6462
|
const workspaceResults = await Promise.all(
|
|
5879
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
6463
|
+
paths.workspace.map((p) => this.scanSkillsDir(path12.join(workspaceDir, p)))
|
|
5880
6464
|
);
|
|
5881
6465
|
const dedup = (skills) => {
|
|
5882
6466
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -5905,7 +6489,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5905
6489
|
const skills = [];
|
|
5906
6490
|
for (const entry of entries) {
|
|
5907
6491
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
5908
|
-
const skillMd =
|
|
6492
|
+
const skillMd = path12.join(dir, entry.name, "SKILL.md");
|
|
5909
6493
|
try {
|
|
5910
6494
|
const content = await readFile(skillMd, "utf-8");
|
|
5911
6495
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -5916,7 +6500,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5916
6500
|
} else if (entry.name.endsWith(".md")) {
|
|
5917
6501
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
5918
6502
|
try {
|
|
5919
|
-
const content = await readFile(
|
|
6503
|
+
const content = await readFile(path12.join(dir, entry.name), "utf-8");
|
|
5920
6504
|
const skill = this.parseSkillMd(cmdName, content);
|
|
5921
6505
|
skill.sourcePath = dir;
|
|
5922
6506
|
skills.push(skill);
|
|
@@ -5951,6 +6535,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5951
6535
|
/**
|
|
5952
6536
|
* Broadcast an activity change — emits a single agent:activity event that carries
|
|
5953
6537
|
* both the status (for the dot indicator) and trajectory entries (for the activity log).
|
|
6538
|
+
*
|
|
6539
|
+
* TODO(lifecycle-v2/daemon-protocol): split this legacy frame into
|
|
6540
|
+
* structured lifecycle producers. Runtime progress, transient heartbeat,
|
|
6541
|
+
* provider/runtime error, process exit, and user-visible activity entries
|
|
6542
|
+
* should carry explicit reason/correlation/window attrs so the server no
|
|
6543
|
+
* longer infers lifecycle semantics from generic activity strings.
|
|
5954
6544
|
*/
|
|
5955
6545
|
broadcastActivity(agentId, activity, detail, extraTrajectory = [], launchIdOverride) {
|
|
5956
6546
|
const ap = this.agents.get(agentId);
|
|
@@ -6176,8 +6766,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6176
6766
|
runtime_profile_turn_outcome: control.kind === "migration" ? control.migrationDoneToolObserved ? "migration_done_observed" : "missing_migration_done" : "notice_only"
|
|
6177
6767
|
};
|
|
6178
6768
|
}
|
|
6179
|
-
startRuntimeTrace(agentId, ap, reason, messages) {
|
|
6769
|
+
startRuntimeTrace(agentId, ap, reason, messages, inputTraceAttrs = {}) {
|
|
6180
6770
|
if (ap.runtimeTraceSpan) return ap.runtimeTraceSpan;
|
|
6771
|
+
ap.runtimeTraceCounters = createRuntimeTraceCounters();
|
|
6181
6772
|
const messageControl = this.runtimeProfileTurnControlFromMessages(messages);
|
|
6182
6773
|
if (messageControl) {
|
|
6183
6774
|
this.activateRuntimeProfileTurnControl(ap, messageControl);
|
|
@@ -6192,12 +6783,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6192
6783
|
reason,
|
|
6193
6784
|
hasSession: Boolean(ap.sessionId),
|
|
6194
6785
|
...this.messagesTraceAttrs(messages),
|
|
6786
|
+
...inputTraceAttrs,
|
|
6195
6787
|
...this.runtimeProfileTurnControlTraceAttrs(ap.runtimeProfileTurnControl)
|
|
6196
6788
|
}
|
|
6197
6789
|
});
|
|
6198
6790
|
span.addEvent("daemon.turn.started", {
|
|
6199
6791
|
reason,
|
|
6200
6792
|
...this.messagesTraceAttrs(messages),
|
|
6793
|
+
...inputTraceAttrs,
|
|
6201
6794
|
...this.runtimeProfileTurnControlTraceAttrs(ap.runtimeProfileTurnControl)
|
|
6202
6795
|
});
|
|
6203
6796
|
ap.runtimeTraceSpan = span;
|
|
@@ -6306,6 +6899,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6306
6899
|
lastActivity: ap.lastActivity,
|
|
6307
6900
|
lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
|
|
6308
6901
|
lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
|
|
6902
|
+
...runtimeTraceCounterAttrs(ap),
|
|
6309
6903
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
|
|
6310
6904
|
});
|
|
6311
6905
|
this.broadcastActivity(agentId, "error", diagnostic.detail);
|
|
@@ -6338,6 +6932,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6338
6932
|
lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
|
|
6339
6933
|
pendingMessages: ap.inbox.length,
|
|
6340
6934
|
recovery: "terminate_for_queued_message",
|
|
6935
|
+
...runtimeTraceCounterAttrs(ap),
|
|
6341
6936
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
|
|
6342
6937
|
});
|
|
6343
6938
|
ap.expectedTerminationReason = "stalled_recovery";
|
|
@@ -6365,6 +6960,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6365
6960
|
const ap = this.agents.get(agentId);
|
|
6366
6961
|
if (ap) {
|
|
6367
6962
|
const wasStalled = Boolean(ap.runtimeProgressStaleSince);
|
|
6963
|
+
this.noteRuntimeTraceCounter(ap, event);
|
|
6368
6964
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.event.received", { kind: event.kind });
|
|
6369
6965
|
if (wasStalled) {
|
|
6370
6966
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.observed", { afterStall: true });
|
|
@@ -6492,6 +7088,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6492
7088
|
}
|
|
6493
7089
|
this.endRuntimeTrace(ap, "ok", {
|
|
6494
7090
|
outcome: "turn-completed",
|
|
7091
|
+
...runtimeTraceCounterAttrs(ap),
|
|
6495
7092
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "turn_end")
|
|
6496
7093
|
});
|
|
6497
7094
|
if (ap.driver.terminateProcessOnTurnEnd) {
|
|
@@ -6533,10 +7130,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6533
7130
|
`[Agent ${agentId}] Disabled Claude tool-boundary gated steering after thinking-block mutation error; lastFlushReason=${ap.gatedSteering.lastFlushReason || "none"}`
|
|
6534
7131
|
);
|
|
6535
7132
|
}
|
|
6536
|
-
this.recordRuntimeTraceEvent(agentId, ap, "runtime.error",
|
|
7133
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.error", {
|
|
7134
|
+
...runtimeErrorDiagnostics.eventAttrs,
|
|
7135
|
+
...runtimeTraceCounterAttrs(ap)
|
|
7136
|
+
});
|
|
6537
7137
|
this.endRuntimeTrace(ap, "error", {
|
|
6538
7138
|
outcome: "runtime-error",
|
|
6539
7139
|
...runtimeErrorDiagnostics.spanAttrs,
|
|
7140
|
+
...runtimeTraceCounterAttrs(ap),
|
|
6540
7141
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
|
|
6541
7142
|
});
|
|
6542
7143
|
if (ap.driver.supportsStdinNotification && classifyTerminalFailure(ap)) {
|
|
@@ -6559,6 +7160,29 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6559
7160
|
sendAgentStatus(agentId, status, launchId) {
|
|
6560
7161
|
this.sendToServer({ type: "agent:status", agentId, status, launchId: launchId || void 0 });
|
|
6561
7162
|
}
|
|
7163
|
+
noteRuntimeTraceCounter(ap, event) {
|
|
7164
|
+
ap.runtimeTraceCounters.events++;
|
|
7165
|
+
switch (event.kind) {
|
|
7166
|
+
case "tool_call":
|
|
7167
|
+
ap.runtimeTraceCounters.toolCalls++;
|
|
7168
|
+
break;
|
|
7169
|
+
case "tool_output":
|
|
7170
|
+
ap.runtimeTraceCounters.toolOutputs++;
|
|
7171
|
+
break;
|
|
7172
|
+
case "compaction_started":
|
|
7173
|
+
ap.runtimeTraceCounters.compactionStarts++;
|
|
7174
|
+
break;
|
|
7175
|
+
case "compaction_finished":
|
|
7176
|
+
ap.runtimeTraceCounters.compactionFinishes++;
|
|
7177
|
+
break;
|
|
7178
|
+
case "text":
|
|
7179
|
+
ap.runtimeTraceCounters.textEvents++;
|
|
7180
|
+
break;
|
|
7181
|
+
case "thinking":
|
|
7182
|
+
ap.runtimeTraceCounters.thinkingEvents++;
|
|
7183
|
+
break;
|
|
7184
|
+
}
|
|
7185
|
+
}
|
|
6562
7186
|
/** Send a batched notification to the agent via stdin about pending messages */
|
|
6563
7187
|
sendStdinNotification(agentId) {
|
|
6564
7188
|
const ap = this.agents.get(agentId);
|
|
@@ -6661,6 +7285,14 @@ ${messages.map((message) => formatIncomingMessage(message, ap.driver)).join("\n"
|
|
|
6661
7285
|
|
|
6662
7286
|
Respond as appropriate. Complete all your work before stopping.
|
|
6663
7287
|
${RESPONSE_TARGET_HINT}`);
|
|
7288
|
+
const inputTraceAttrs = buildRuntimeInputTraceAttrs({
|
|
7289
|
+
source: `stdin_${mode}_delivery`,
|
|
7290
|
+
prompt,
|
|
7291
|
+
messages,
|
|
7292
|
+
sessionIdPresent: Boolean(ap.sessionId),
|
|
7293
|
+
nativeStandingPrompt: Boolean(ap.driver.supportsNativeStandingPrompt)
|
|
7294
|
+
});
|
|
7295
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.input.prepared", inputTraceAttrs);
|
|
6664
7296
|
const encoded = ap.driver.encodeStdinMessage(prompt, ap.sessionId, { mode });
|
|
6665
7297
|
if (!encoded) {
|
|
6666
7298
|
ap.inbox.unshift(...messages);
|
|
@@ -6672,6 +7304,7 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
6672
7304
|
);
|
|
6673
7305
|
this.recordDaemonTrace("daemon.agent.stdin_delivery", {
|
|
6674
7306
|
...traceAttrs,
|
|
7307
|
+
...inputTraceAttrs,
|
|
6675
7308
|
outcome: "encode_failed",
|
|
6676
7309
|
requeued_messages_count: messages.length
|
|
6677
7310
|
}, "error");
|
|
@@ -6688,6 +7321,7 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
6688
7321
|
this.ackInjectedRuntimeProfileMessages(agentId, messages, ap.launchId);
|
|
6689
7322
|
this.recordDaemonTrace("daemon.agent.stdin_delivery", {
|
|
6690
7323
|
...traceAttrs,
|
|
7324
|
+
...inputTraceAttrs,
|
|
6691
7325
|
outcome: "written",
|
|
6692
7326
|
stdin_write_attempted: true
|
|
6693
7327
|
});
|
|
@@ -6709,8 +7343,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
6709
7343
|
const nodes = [];
|
|
6710
7344
|
for (const entry of entries) {
|
|
6711
7345
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
6712
|
-
const fullPath =
|
|
6713
|
-
const relativePath =
|
|
7346
|
+
const fullPath = path12.join(dir, entry.name);
|
|
7347
|
+
const relativePath = path12.relative(rootDir, fullPath);
|
|
6714
7348
|
let info;
|
|
6715
7349
|
try {
|
|
6716
7350
|
info = await stat2(fullPath);
|
|
@@ -7013,9 +7647,9 @@ var ReminderCache = class {
|
|
|
7013
7647
|
|
|
7014
7648
|
// src/machineLock.ts
|
|
7015
7649
|
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
7016
|
-
import { mkdirSync as
|
|
7650
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
7017
7651
|
import os6 from "os";
|
|
7018
|
-
import
|
|
7652
|
+
import path13 from "path";
|
|
7019
7653
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
7020
7654
|
var DaemonMachineLockConflictError = class extends Error {
|
|
7021
7655
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -7037,7 +7671,7 @@ function resolveDefaultMachineStateRoot() {
|
|
|
7037
7671
|
return resolveSlockHomePath("machines");
|
|
7038
7672
|
}
|
|
7039
7673
|
function ownerPath(lockDir) {
|
|
7040
|
-
return
|
|
7674
|
+
return path13.join(lockDir, "owner.json");
|
|
7041
7675
|
}
|
|
7042
7676
|
function readOwner(lockDir) {
|
|
7043
7677
|
try {
|
|
@@ -7067,13 +7701,13 @@ function acquireDaemonMachineLock(options) {
|
|
|
7067
7701
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
7068
7702
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
7069
7703
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
7070
|
-
const machineDir =
|
|
7071
|
-
const lockDir =
|
|
7704
|
+
const machineDir = path13.join(rootDir, lockId);
|
|
7705
|
+
const lockDir = path13.join(machineDir, "daemon.lock");
|
|
7072
7706
|
const token = randomUUID2();
|
|
7073
|
-
|
|
7707
|
+
mkdirSync6(machineDir, { recursive: true });
|
|
7074
7708
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
7075
7709
|
try {
|
|
7076
|
-
|
|
7710
|
+
mkdirSync6(lockDir);
|
|
7077
7711
|
const owner = {
|
|
7078
7712
|
pid: process.pid,
|
|
7079
7713
|
token,
|
|
@@ -7083,7 +7717,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
7083
7717
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
7084
7718
|
};
|
|
7085
7719
|
try {
|
|
7086
|
-
|
|
7720
|
+
writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
7087
7721
|
`, { mode: 384 });
|
|
7088
7722
|
} catch (err) {
|
|
7089
7723
|
rmSync2(lockDir, { recursive: true, force: true });
|
|
@@ -7120,8 +7754,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
7120
7754
|
}
|
|
7121
7755
|
|
|
7122
7756
|
// src/localTraceSink.ts
|
|
7123
|
-
import { appendFileSync, mkdirSync as
|
|
7124
|
-
import
|
|
7757
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync4, rmSync as rmSync3, statSync as statSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
7758
|
+
import path14 from "path";
|
|
7125
7759
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
7126
7760
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
7127
7761
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -7157,7 +7791,7 @@ var LocalRotatingTraceSink = class {
|
|
|
7157
7791
|
currentSize = 0;
|
|
7158
7792
|
sequence = 0;
|
|
7159
7793
|
constructor(options) {
|
|
7160
|
-
this.traceDir =
|
|
7794
|
+
this.traceDir = path14.join(options.machineDir, "traces");
|
|
7161
7795
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
7162
7796
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
7163
7797
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -7183,26 +7817,26 @@ var LocalRotatingTraceSink = class {
|
|
|
7183
7817
|
return this.currentFile;
|
|
7184
7818
|
}
|
|
7185
7819
|
ensureFile(nextBytes) {
|
|
7186
|
-
|
|
7820
|
+
mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
|
|
7187
7821
|
const nowMs = this.nowMsProvider();
|
|
7188
7822
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
7189
7823
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
7190
|
-
this.currentFile =
|
|
7824
|
+
this.currentFile = path14.join(
|
|
7191
7825
|
this.traceDir,
|
|
7192
7826
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
7193
7827
|
);
|
|
7194
|
-
|
|
7828
|
+
writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
|
|
7195
7829
|
this.currentSize = statSync4(this.currentFile).size;
|
|
7196
7830
|
this.currentFileOpenedAtMs = nowMs;
|
|
7197
7831
|
this.pruneOldFiles();
|
|
7198
7832
|
}
|
|
7199
7833
|
}
|
|
7200
7834
|
pruneOldFiles() {
|
|
7201
|
-
const files =
|
|
7835
|
+
const files = readdirSync4(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
|
|
7202
7836
|
const excess = files.length - this.maxFiles;
|
|
7203
7837
|
if (excess <= 0) return;
|
|
7204
7838
|
for (const file of files.slice(0, excess)) {
|
|
7205
|
-
rmSync3(
|
|
7839
|
+
rmSync3(path14.join(this.traceDir, file), { force: true });
|
|
7206
7840
|
}
|
|
7207
7841
|
}
|
|
7208
7842
|
};
|
|
@@ -7289,11 +7923,11 @@ function isDiagnosticErrorAttr(key) {
|
|
|
7289
7923
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
|
|
7290
7924
|
import { gzipSync } from "zlib";
|
|
7291
7925
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
7292
|
-
import
|
|
7926
|
+
import path15 from "path";
|
|
7293
7927
|
|
|
7294
7928
|
// src/directUploadCapability.ts
|
|
7295
|
-
function joinUrl(base,
|
|
7296
|
-
return `${base.replace(/\/+$/, "")}${
|
|
7929
|
+
function joinUrl(base, path17) {
|
|
7930
|
+
return `${base.replace(/\/+$/, "")}${path17}`;
|
|
7297
7931
|
}
|
|
7298
7932
|
function jsonHeaders(apiKey) {
|
|
7299
7933
|
return {
|
|
@@ -7512,7 +8146,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
7512
8146
|
}, nextMs);
|
|
7513
8147
|
}
|
|
7514
8148
|
async findUploadCandidates() {
|
|
7515
|
-
const traceDir =
|
|
8149
|
+
const traceDir = path15.join(this.options.machineDir, "traces");
|
|
7516
8150
|
let names;
|
|
7517
8151
|
try {
|
|
7518
8152
|
names = await readdir3(traceDir);
|
|
@@ -7524,8 +8158,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
7524
8158
|
const currentFile = this.options.currentFileProvider?.();
|
|
7525
8159
|
const candidates = [];
|
|
7526
8160
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
7527
|
-
const file =
|
|
7528
|
-
if (currentFile &&
|
|
8161
|
+
const file = path15.join(traceDir, name);
|
|
8162
|
+
if (currentFile && path15.resolve(file) === path15.resolve(currentFile)) continue;
|
|
7529
8163
|
if (await this.isUploaded(file)) continue;
|
|
7530
8164
|
try {
|
|
7531
8165
|
const info = await stat3(file);
|
|
@@ -7599,8 +8233,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
7599
8233
|
}
|
|
7600
8234
|
}
|
|
7601
8235
|
uploadStatePath(file) {
|
|
7602
|
-
const stateDir =
|
|
7603
|
-
return
|
|
8236
|
+
const stateDir = path15.join(this.options.machineDir, "trace-uploads");
|
|
8237
|
+
return path15.join(stateDir, `${path15.basename(file)}.uploaded.json`);
|
|
7604
8238
|
}
|
|
7605
8239
|
async isUploaded(file) {
|
|
7606
8240
|
try {
|
|
@@ -7612,9 +8246,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
7612
8246
|
}
|
|
7613
8247
|
async markUploaded(file, metadata) {
|
|
7614
8248
|
const stateFile = this.uploadStatePath(file);
|
|
7615
|
-
await mkdir2(
|
|
8249
|
+
await mkdir2(path15.dirname(stateFile), { recursive: true, mode: 448 });
|
|
7616
8250
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
7617
|
-
file:
|
|
8251
|
+
file: path15.basename(file),
|
|
7618
8252
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7619
8253
|
...metadata
|
|
7620
8254
|
}, null, 2)}
|
|
@@ -7633,10 +8267,10 @@ function readPositiveIntegerEnv2(name, fallback) {
|
|
|
7633
8267
|
|
|
7634
8268
|
// src/core.ts
|
|
7635
8269
|
var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
7636
|
-
var DAEMON_CLI_USAGE =
|
|
7637
|
-
function parseDaemonCliArgs(args) {
|
|
8270
|
+
var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
|
|
8271
|
+
function parseDaemonCliArgs(args, env = {}) {
|
|
7638
8272
|
let serverUrl = "";
|
|
7639
|
-
let apiKey = "";
|
|
8273
|
+
let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
|
|
7640
8274
|
for (let i = 0; i < args.length; i++) {
|
|
7641
8275
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
7642
8276
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -7653,23 +8287,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
7653
8287
|
}
|
|
7654
8288
|
}
|
|
7655
8289
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
7656
|
-
const dirname =
|
|
7657
|
-
const jsPath =
|
|
8290
|
+
const dirname = path16.dirname(fileURLToPath2(moduleUrl));
|
|
8291
|
+
const jsPath = path16.resolve(dirname, "chat-bridge.js");
|
|
7658
8292
|
try {
|
|
7659
8293
|
accessSync(jsPath);
|
|
7660
8294
|
return jsPath;
|
|
7661
8295
|
} catch {
|
|
7662
|
-
return
|
|
8296
|
+
return path16.resolve(dirname, "chat-bridge.ts");
|
|
7663
8297
|
}
|
|
7664
8298
|
}
|
|
7665
8299
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
7666
|
-
const thisDir =
|
|
7667
|
-
const bundledDistPath =
|
|
8300
|
+
const thisDir = path16.dirname(fileURLToPath2(moduleUrl));
|
|
8301
|
+
const bundledDistPath = path16.resolve(thisDir, "cli", "index.js");
|
|
7668
8302
|
try {
|
|
7669
8303
|
accessSync(bundledDistPath);
|
|
7670
8304
|
return bundledDistPath;
|
|
7671
8305
|
} catch {
|
|
7672
|
-
const workspaceDistPath =
|
|
8306
|
+
const workspaceDistPath = path16.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
7673
8307
|
accessSync(workspaceDistPath);
|
|
7674
8308
|
return workspaceDistPath;
|
|
7675
8309
|
}
|
|
@@ -7848,7 +8482,7 @@ var DaemonCore = class {
|
|
|
7848
8482
|
}
|
|
7849
8483
|
resolveMachineStateRoot() {
|
|
7850
8484
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
7851
|
-
if (this.options.dataDir) return
|
|
8485
|
+
if (this.options.dataDir) return path16.join(path16.dirname(this.options.dataDir), "machines");
|
|
7852
8486
|
return resolveDefaultMachineStateRoot();
|
|
7853
8487
|
}
|
|
7854
8488
|
shouldEnableLocalTrace() {
|
|
@@ -8231,6 +8865,8 @@ var DaemonCore = class {
|
|
|
8231
8865
|
};
|
|
8232
8866
|
|
|
8233
8867
|
export {
|
|
8868
|
+
DAEMON_API_KEY_ENV,
|
|
8869
|
+
scrubDaemonAuthEnv,
|
|
8234
8870
|
resolveWorkspaceDirectoryPath,
|
|
8235
8871
|
scanWorkspaceDirectories,
|
|
8236
8872
|
deleteWorkspaceDirectory,
|