@slock-ai/daemon 0.49.0 → 0.50.0-play.20260518104853
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-M4A5QPUN.js → chunk-SY3CKIJO.js} +600 -118
- package/dist/cli/index.js +176 -20
- package/dist/core.js +6 -2
- package/dist/drivers/piSdkRunner.js +96 -0
- package/dist/index.js +5 -3
- package/package.json +2 -1
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
} from "./chunk-KNMCE6WB.js";
|
|
8
8
|
|
|
9
9
|
// src/core.ts
|
|
10
|
-
import
|
|
10
|
+
import path17 from "path";
|
|
11
11
|
import os8 from "os";
|
|
12
12
|
import { createRequire } from "module";
|
|
13
13
|
import { accessSync } from "fs";
|
|
14
|
-
import { fileURLToPath } from "url";
|
|
14
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
15
15
|
|
|
16
16
|
// ../shared/src/tracing/index.ts
|
|
17
17
|
var DEFAULT_TRACE_FLAGS = "00";
|
|
@@ -723,6 +723,7 @@ var SERVER_CAPABILITY_MATRIX = {
|
|
|
723
723
|
var RUNTIMES = [
|
|
724
724
|
{ id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
|
|
725
725
|
{ id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
|
|
726
|
+
{ id: "pi", displayName: "Pi", binary: "pi", supported: true },
|
|
726
727
|
{ id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
|
|
727
728
|
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
728
729
|
{ id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
|
|
@@ -764,10 +765,10 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
764
765
|
};
|
|
765
766
|
|
|
766
767
|
// src/agentProcessManager.ts
|
|
767
|
-
import { mkdirSync as
|
|
768
|
+
import { mkdirSync as mkdirSync5, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
768
769
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
769
770
|
import { createHash as createHash2 } from "crypto";
|
|
770
|
-
import
|
|
771
|
+
import path13 from "path";
|
|
771
772
|
import os6 from "os";
|
|
772
773
|
|
|
773
774
|
// src/drivers/claude.ts
|
|
@@ -871,13 +872,15 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
|
|
|
871
872
|
17. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
872
873
|
18. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
|
|
873
874
|
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
|
|
875
|
-
21. **\`slock
|
|
876
|
-
22. **\`slock reminder
|
|
877
|
-
23. **\`slock reminder
|
|
878
|
-
24. **\`slock reminder
|
|
879
|
-
25. **\`slock reminder
|
|
880
|
-
26. **\`slock
|
|
875
|
+
20. **\`slock integration list\`** \u2014 List registered third-party services and this agent's active Slock Agent Logins.
|
|
876
|
+
21. **\`slock integration login\`** \u2014 Provision or reuse this agent's login for a registered third-party service.
|
|
877
|
+
22. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
878
|
+
23. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
|
|
879
|
+
24. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
|
|
880
|
+
25. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
|
|
881
|
+
26. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
|
|
882
|
+
27. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
|
|
883
|
+
28. **\`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\`).
|
|
881
884
|
|
|
882
885
|
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:
|
|
883
886
|
- failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
|
|
@@ -978,6 +981,11 @@ Each channel has a **name** and optionally a **description** that define its pur
|
|
|
978
981
|
- **Reply in context** \u2014 always respond in the channel/thread the message came from.
|
|
979
982
|
- **Stay on topic** \u2014 when proactively sharing results or updates, post in the channel most relevant to the work. Don't scatter messages across unrelated channels.
|
|
980
983
|
- If unsure where something belongs, call ${serverInfoCmd} to review channel descriptions.`;
|
|
984
|
+
const thirdPartyIntegrationsSection = isCli ? `### Third-party integrations
|
|
985
|
+
|
|
986
|
+
If a registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app, first run \`slock integration list\` and match the app to a registered service before browsing the app. Use \`slock integration login --service <service>\` to provision or reuse your agent login for that service. When the command returns \`Agent login ready\` or \`Already logged in\`, the agent-side login is ready. If the output includes an app URL, open that URL as the service-provided third-party app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`slock profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the registered service / Slock Agent Login path.` : `### Third-party integrations
|
|
987
|
+
|
|
988
|
+
If a registered third-party service requires login, use Slock Agent Login through the available registered-service interface instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app, first inspect the registered-service interface and match the app to a registered service before browsing the app. Once the registered-service interface reports the agent login is ready, the agent-side login is ready. If that interface provides an app URL, use it as the service-provided third-party app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow or treat internal request IDs as OAuth callback codes unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use your Slock profile view. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the registered service / Slock Agent Login path.`;
|
|
981
989
|
const readingHistorySection = isCli ? `### Reading history
|
|
982
990
|
|
|
983
991
|
\`slock message read --channel "#channel-name"\` or \`slock message read --channel dm:@peer-name\` or \`slock message read --channel "#channel:shortid"\`
|
|
@@ -1149,6 +1157,8 @@ ${discoverySection}
|
|
|
1149
1157
|
|
|
1150
1158
|
${channelAwarenessSection}
|
|
1151
1159
|
|
|
1160
|
+
${thirdPartyIntegrationsSection}
|
|
1161
|
+
|
|
1152
1162
|
${readingHistorySection}
|
|
1153
1163
|
|
|
1154
1164
|
${historicalReferenceSection}
|
|
@@ -1180,6 +1190,17 @@ Keep the user informed. They cannot see your internal reasoning, so:
|
|
|
1180
1190
|
- For multi-step work, send short progress updates (e.g. "Working on step 2/3\u2026").
|
|
1181
1191
|
- When done, summarize the result.
|
|
1182
1192
|
- Keep updates concise \u2014 one or two sentences. Don't flood the chat.
|
|
1193
|
+
- 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:
|
|
1194
|
+
|
|
1195
|
+
\`\`\`html
|
|
1196
|
+
<details>
|
|
1197
|
+
<summary>Evidence, logs, or edge cases</summary>
|
|
1198
|
+
|
|
1199
|
+
Detailed notes go here.
|
|
1200
|
+
</details>
|
|
1201
|
+
\`\`\`
|
|
1202
|
+
|
|
1203
|
+
Do not hide the main recommendation, blocker, or required action inside \`<details>\`; only fold supporting evidence, logs, alternatives, or extended rationale.
|
|
1183
1204
|
|
|
1184
1205
|
### Conversation etiquette
|
|
1185
1206
|
|
|
@@ -1349,6 +1370,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1349
1370
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1350
1371
|
}
|
|
1351
1372
|
|
|
1373
|
+
// src/authEnv.ts
|
|
1374
|
+
var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
|
|
1375
|
+
var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
|
|
1376
|
+
function scrubDaemonAuthEnv(env) {
|
|
1377
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1378
|
+
return env;
|
|
1379
|
+
}
|
|
1380
|
+
function scrubDaemonChildEnv(env) {
|
|
1381
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1382
|
+
delete env[SLOCK_AGENT_TOKEN_ENV];
|
|
1383
|
+
return env;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1352
1386
|
// src/drivers/cliTransport.ts
|
|
1353
1387
|
var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1354
1388
|
function runtimeContextEnv(config) {
|
|
@@ -1402,7 +1436,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)}
|
|
|
1402
1436
|
SLOCK_AGENT_TOKEN_FILE: tokenFile,
|
|
1403
1437
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
1404
1438
|
};
|
|
1405
|
-
|
|
1439
|
+
scrubDaemonChildEnv(spawnEnv);
|
|
1406
1440
|
return {
|
|
1407
1441
|
slockDir,
|
|
1408
1442
|
tokenFile,
|
|
@@ -1439,7 +1473,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn) {
|
|
|
1439
1473
|
}
|
|
1440
1474
|
function resolveCommandOnPath(command, deps = {}) {
|
|
1441
1475
|
const platform = deps.platform ?? process.platform;
|
|
1442
|
-
const env = deps.env ?? process.env;
|
|
1476
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
1443
1477
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
1444
1478
|
if (platform === "win32") {
|
|
1445
1479
|
return resolveCommandOnWindows(command, env, execFileSyncFn);
|
|
@@ -1464,7 +1498,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
1464
1498
|
return null;
|
|
1465
1499
|
}
|
|
1466
1500
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
1467
|
-
const env = deps.env ?? process.env;
|
|
1501
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
1468
1502
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
1469
1503
|
try {
|
|
1470
1504
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -2741,7 +2775,7 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
2741
2775
|
}
|
|
2742
2776
|
function runCursorModelsCommand() {
|
|
2743
2777
|
return spawnSync("cursor-agent", ["models"], {
|
|
2744
|
-
env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
2778
|
+
env: scrubDaemonChildEnv({ ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
2745
2779
|
encoding: "utf8",
|
|
2746
2780
|
timeout: 5e3
|
|
2747
2781
|
});
|
|
@@ -2788,7 +2822,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
2788
2822
|
}
|
|
2789
2823
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
2790
2824
|
const existsSyncFn = deps.existsSyncFn ?? existsSync6;
|
|
2791
|
-
const env = deps.env ?? process.env;
|
|
2825
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
2792
2826
|
const winPath = path8.win32;
|
|
2793
2827
|
let geminiEntry = null;
|
|
2794
2828
|
try {
|
|
@@ -2962,13 +2996,16 @@ var GeminiDriver = class {
|
|
|
2962
2996
|
// src/drivers/kimi.ts
|
|
2963
2997
|
import { randomUUID } from "crypto";
|
|
2964
2998
|
import { spawn as spawn6 } from "child_process";
|
|
2965
|
-
import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2999
|
+
import { chmodSync, existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2966
3000
|
import os4 from "os";
|
|
2967
3001
|
import path9 from "path";
|
|
2968
3002
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
2969
3003
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
2970
3004
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
2971
3005
|
var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
|
|
3006
|
+
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
3007
|
+
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
3008
|
+
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
2972
3009
|
function parseToolArguments(raw) {
|
|
2973
3010
|
if (typeof raw !== "string") return raw;
|
|
2974
3011
|
try {
|
|
@@ -2977,6 +3014,80 @@ function parseToolArguments(raw) {
|
|
|
2977
3014
|
return raw;
|
|
2978
3015
|
}
|
|
2979
3016
|
}
|
|
3017
|
+
function readKimiConfigSource(home = os4.homedir(), env = process.env) {
|
|
3018
|
+
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
3019
|
+
if (inlineConfig && inlineConfig.trim()) {
|
|
3020
|
+
return {
|
|
3021
|
+
raw: inlineConfig,
|
|
3022
|
+
explicitPath: null,
|
|
3023
|
+
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
3024
|
+
};
|
|
3025
|
+
}
|
|
3026
|
+
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
3027
|
+
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
|
|
3028
|
+
try {
|
|
3029
|
+
return {
|
|
3030
|
+
raw: readFileSync3(configPath, "utf8"),
|
|
3031
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
3032
|
+
sourcePath: configPath
|
|
3033
|
+
};
|
|
3034
|
+
} catch {
|
|
3035
|
+
return {
|
|
3036
|
+
raw: null,
|
|
3037
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
3038
|
+
sourcePath: configPath
|
|
3039
|
+
};
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
function buildKimiSpawnEnv(env = process.env) {
|
|
3043
|
+
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
3044
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
3045
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
3046
|
+
return scrubDaemonChildEnv(spawnEnv);
|
|
3047
|
+
}
|
|
3048
|
+
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
3049
|
+
return {
|
|
3050
|
+
...process.env,
|
|
3051
|
+
...ctx.config.envVars || {},
|
|
3052
|
+
...overrideEnv || {}
|
|
3053
|
+
};
|
|
3054
|
+
}
|
|
3055
|
+
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
3056
|
+
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
3057
|
+
const source = readKimiConfigSource(opts.home ?? os4.homedir(), env);
|
|
3058
|
+
const args = [];
|
|
3059
|
+
let configFilePath = null;
|
|
3060
|
+
let configContent = null;
|
|
3061
|
+
if (source.explicitPath) {
|
|
3062
|
+
configFilePath = source.explicitPath;
|
|
3063
|
+
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
3064
|
+
configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
3065
|
+
configContent = source.raw;
|
|
3066
|
+
if (opts.writeGeneratedConfig !== false) {
|
|
3067
|
+
writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
3068
|
+
chmodSync(configFilePath, 384);
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
if (configFilePath) {
|
|
3072
|
+
args.push("--config-file", configFilePath);
|
|
3073
|
+
}
|
|
3074
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
3075
|
+
args.push("--model", ctx.config.model);
|
|
3076
|
+
}
|
|
3077
|
+
return {
|
|
3078
|
+
args,
|
|
3079
|
+
env: buildKimiSpawnEnv(env),
|
|
3080
|
+
configFilePath,
|
|
3081
|
+
configContent
|
|
3082
|
+
};
|
|
3083
|
+
}
|
|
3084
|
+
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
3085
|
+
return {
|
|
3086
|
+
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
3087
|
+
args: commandArgs,
|
|
3088
|
+
shell: false
|
|
3089
|
+
};
|
|
3090
|
+
}
|
|
2980
3091
|
var KimiDriver = class {
|
|
2981
3092
|
id = "kimi";
|
|
2982
3093
|
lifecycle = {
|
|
@@ -2993,7 +3104,25 @@ var KimiDriver = class {
|
|
|
2993
3104
|
};
|
|
2994
3105
|
model = {
|
|
2995
3106
|
detectedModelsVerifiedAs: "launchable",
|
|
2996
|
-
toLaunchSpec: (modelId) =>
|
|
3107
|
+
toLaunchSpec: (modelId, ctx, opts) => {
|
|
3108
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
3109
|
+
const launchCtx = {
|
|
3110
|
+
...ctx,
|
|
3111
|
+
config: {
|
|
3112
|
+
...ctx.config,
|
|
3113
|
+
model: modelId
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
3117
|
+
home: opts?.home,
|
|
3118
|
+
writeGeneratedConfig: false
|
|
3119
|
+
});
|
|
3120
|
+
return {
|
|
3121
|
+
args: launch.args,
|
|
3122
|
+
env: launch.env,
|
|
3123
|
+
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
3124
|
+
};
|
|
3125
|
+
}
|
|
2997
3126
|
};
|
|
2998
3127
|
supportsStdinNotification = true;
|
|
2999
3128
|
mcpToolPrefix = "";
|
|
@@ -3047,6 +3176,7 @@ var KimiDriver = class {
|
|
|
3047
3176
|
}
|
|
3048
3177
|
}
|
|
3049
3178
|
}), "utf8");
|
|
3179
|
+
const launch = buildKimiLaunchOptions(ctx);
|
|
3050
3180
|
const args = [
|
|
3051
3181
|
"--wire",
|
|
3052
3182
|
"--yolo",
|
|
@@ -3055,17 +3185,23 @@ var KimiDriver = class {
|
|
|
3055
3185
|
"--mcp-config-file",
|
|
3056
3186
|
mcpConfigPath,
|
|
3057
3187
|
"--session",
|
|
3058
|
-
this.sessionId
|
|
3188
|
+
this.sessionId,
|
|
3189
|
+
...launch.args
|
|
3059
3190
|
];
|
|
3060
3191
|
if (ctx.config.model && ctx.config.model !== "default") {
|
|
3061
3192
|
args.push("--model", ctx.config.model);
|
|
3062
3193
|
}
|
|
3063
3194
|
const spawnEnv = prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
|
|
3064
|
-
const
|
|
3195
|
+
const spawnTarget = resolveKimiSpawn(args);
|
|
3196
|
+
const proc = spawn6(spawnTarget.command, spawnTarget.args, {
|
|
3065
3197
|
cwd: ctx.workingDirectory,
|
|
3066
3198
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3067
3199
|
env: spawnEnv,
|
|
3068
|
-
|
|
3200
|
+
// Windows cmd.exe defaults to the system code page (often CP936/GBK)
|
|
3201
|
+
// and has an 8191-character command-line limit. Kimi's official
|
|
3202
|
+
// installer/uv entrypoint is an executable, so launch it directly and
|
|
3203
|
+
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
3204
|
+
shell: spawnTarget.shell
|
|
3069
3205
|
});
|
|
3070
3206
|
proc.stdin?.write(JSON.stringify({
|
|
3071
3207
|
jsonrpc: "2.0",
|
|
@@ -3181,14 +3317,9 @@ var KimiDriver = class {
|
|
|
3181
3317
|
return detectKimiModels();
|
|
3182
3318
|
}
|
|
3183
3319
|
};
|
|
3184
|
-
function detectKimiModels(home = os4.homedir()) {
|
|
3185
|
-
const
|
|
3186
|
-
|
|
3187
|
-
try {
|
|
3188
|
-
raw = readFileSync3(configPath, "utf8");
|
|
3189
|
-
} catch {
|
|
3190
|
-
return null;
|
|
3191
|
-
}
|
|
3320
|
+
function detectKimiModels(home = os4.homedir(), opts = {}) {
|
|
3321
|
+
const raw = readKimiConfigSource(home, opts.env).raw;
|
|
3322
|
+
if (raw === null) return null;
|
|
3192
3323
|
const models = [];
|
|
3193
3324
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
3194
3325
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -3285,6 +3416,18 @@ function isSupportedOpenCodeVersion(version) {
|
|
|
3285
3416
|
}
|
|
3286
3417
|
return true;
|
|
3287
3418
|
}
|
|
3419
|
+
var AGENT_FLAG_CUTOFF_VERSION = "1.15.0";
|
|
3420
|
+
function requiresAgentCliFlag(version) {
|
|
3421
|
+
if (!version) return true;
|
|
3422
|
+
const actual = parseSemver(version);
|
|
3423
|
+
const cutoff = parseSemver(AGENT_FLAG_CUTOFF_VERSION);
|
|
3424
|
+
if (!actual || !cutoff) return true;
|
|
3425
|
+
for (let i = 0; i < 3; i += 1) {
|
|
3426
|
+
if (actual[i] > cutoff[i]) return false;
|
|
3427
|
+
if (actual[i] < cutoff[i]) return true;
|
|
3428
|
+
}
|
|
3429
|
+
return false;
|
|
3430
|
+
}
|
|
3288
3431
|
function unsupportedOpenCodeVersionMessage(version) {
|
|
3289
3432
|
if (!version || isSupportedOpenCodeVersion(version)) return null;
|
|
3290
3433
|
return `OpenCode CLI ${version} is unsupported; requires OpenCode >= ${MIN_SUPPORTED_OPENCODE_VERSION}. Upgrade opencode before starting this runtime.`;
|
|
@@ -3332,7 +3475,7 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
|
|
|
3332
3475
|
}
|
|
3333
3476
|
};
|
|
3334
3477
|
}
|
|
3335
|
-
function buildOpenCodeLaunchOptions(ctx, home = os5.homedir()) {
|
|
3478
|
+
function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = null) {
|
|
3336
3479
|
const slock = prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
3337
3480
|
const config = buildOpenCodeConfig(ctx, home);
|
|
3338
3481
|
const env = {
|
|
@@ -3351,7 +3494,9 @@ function buildOpenCodeLaunchOptions(ctx, home = os5.homedir()) {
|
|
|
3351
3494
|
if (ctx.config.model && ctx.config.model !== "default") {
|
|
3352
3495
|
args.push("--model", ctx.config.model);
|
|
3353
3496
|
}
|
|
3354
|
-
|
|
3497
|
+
if (requiresAgentCliFlag(version)) {
|
|
3498
|
+
args.push("--agent", SLOCK_AGENT_NAME);
|
|
3499
|
+
}
|
|
3355
3500
|
if (ctx.config.sessionId) {
|
|
3356
3501
|
args.push("--session", ctx.config.sessionId);
|
|
3357
3502
|
}
|
|
@@ -3435,7 +3580,7 @@ function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeMode
|
|
|
3435
3580
|
}
|
|
3436
3581
|
function runOpenCodeModelsCommand(home) {
|
|
3437
3582
|
const result = spawnSync2("opencode", ["models"], {
|
|
3438
|
-
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
3583
|
+
env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
3439
3584
|
encoding: "utf8",
|
|
3440
3585
|
timeout: 5e3
|
|
3441
3586
|
});
|
|
@@ -3487,7 +3632,8 @@ var OpenCodeDriver = class {
|
|
|
3487
3632
|
model: modelId
|
|
3488
3633
|
}
|
|
3489
3634
|
};
|
|
3490
|
-
const
|
|
3635
|
+
const version = readCommandVersion("opencode");
|
|
3636
|
+
const launch = buildOpenCodeLaunchOptions(launchCtx, opts?.home, version);
|
|
3491
3637
|
return {
|
|
3492
3638
|
args: launch.args,
|
|
3493
3639
|
env: launch.env,
|
|
@@ -3524,11 +3670,12 @@ var OpenCodeDriver = class {
|
|
|
3524
3670
|
spawn(ctx) {
|
|
3525
3671
|
this.sessionId = ctx.config.sessionId || null;
|
|
3526
3672
|
this.sessionAnnounced = false;
|
|
3527
|
-
const
|
|
3673
|
+
const version = readCommandVersion("opencode");
|
|
3674
|
+
const unsupportedMessage = unsupportedOpenCodeVersionMessage(version);
|
|
3528
3675
|
if (unsupportedMessage) {
|
|
3529
3676
|
throw new Error(unsupportedMessage);
|
|
3530
3677
|
}
|
|
3531
|
-
const launch = buildOpenCodeLaunchOptions(ctx);
|
|
3678
|
+
const launch = buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
|
|
3532
3679
|
const proc = spawn7("opencode", launch.args, {
|
|
3533
3680
|
cwd: ctx.workingDirectory,
|
|
3534
3681
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -3591,6 +3738,297 @@ var OpenCodeDriver = class {
|
|
|
3591
3738
|
}
|
|
3592
3739
|
};
|
|
3593
3740
|
|
|
3741
|
+
// src/drivers/pi.ts
|
|
3742
|
+
import { spawn as spawn8 } from "child_process";
|
|
3743
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
3744
|
+
import path11 from "path";
|
|
3745
|
+
import { fileURLToPath } from "url";
|
|
3746
|
+
import { getAgentDir, VERSION as PI_SDK_VERSION } from "@earendil-works/pi-coding-agent";
|
|
3747
|
+
var CHAT_MCP_TOOL_PREFIX2 = "chat_";
|
|
3748
|
+
var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
|
|
3749
|
+
var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
|
|
3750
|
+
var MIN_SUPPORTED_PI_VERSION = "0.74.0";
|
|
3751
|
+
function parseSemver2(version) {
|
|
3752
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
3753
|
+
if (!match) return null;
|
|
3754
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
3755
|
+
}
|
|
3756
|
+
function isSupportedPiVersion(version) {
|
|
3757
|
+
if (!version) return true;
|
|
3758
|
+
const actual = parseSemver2(version);
|
|
3759
|
+
const minimum = parseSemver2(MIN_SUPPORTED_PI_VERSION);
|
|
3760
|
+
if (!actual || !minimum) return true;
|
|
3761
|
+
for (let i = 0; i < 3; i += 1) {
|
|
3762
|
+
if (actual[i] > minimum[i]) return true;
|
|
3763
|
+
if (actual[i] < minimum[i]) return false;
|
|
3764
|
+
}
|
|
3765
|
+
return true;
|
|
3766
|
+
}
|
|
3767
|
+
function unsupportedPiVersionMessage(version) {
|
|
3768
|
+
if (!version || isSupportedPiVersion(version)) return null;
|
|
3769
|
+
return `Pi SDK ${version} is unsupported; requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION}. Upgrade the daemon Pi dependency before starting this runtime.`;
|
|
3770
|
+
}
|
|
3771
|
+
function probePi(version = PI_SDK_VERSION) {
|
|
3772
|
+
const unsupportedMessage = unsupportedPiVersionMessage(version);
|
|
3773
|
+
if (unsupportedMessage) {
|
|
3774
|
+
return {
|
|
3775
|
+
available: false,
|
|
3776
|
+
version: `${version} (requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION})`
|
|
3777
|
+
};
|
|
3778
|
+
}
|
|
3779
|
+
return { available: true, version };
|
|
3780
|
+
}
|
|
3781
|
+
function resolvePiSdkRunnerPath(moduleUrl = import.meta.url) {
|
|
3782
|
+
const moduleDir = path11.dirname(fileURLToPath(moduleUrl));
|
|
3783
|
+
const sourceSibling = path11.join(moduleDir, "piSdkRunner.ts");
|
|
3784
|
+
if (existsSync8(sourceSibling)) return sourceSibling;
|
|
3785
|
+
const bundledEntry = path11.join(moduleDir, "drivers", "piSdkRunner.js");
|
|
3786
|
+
if (existsSync8(bundledEntry)) return bundledEntry;
|
|
3787
|
+
return path11.join(moduleDir, "piSdkRunner.js");
|
|
3788
|
+
}
|
|
3789
|
+
function buildPiSdkNodeArgs(runnerPath = resolvePiSdkRunnerPath()) {
|
|
3790
|
+
if (runnerPath.endsWith(".ts")) {
|
|
3791
|
+
return [...process.execArgv, runnerPath];
|
|
3792
|
+
}
|
|
3793
|
+
return [runnerPath];
|
|
3794
|
+
}
|
|
3795
|
+
function buildPiLaunchOptions(ctx, opts = {}) {
|
|
3796
|
+
const command = opts.command ?? process.execPath;
|
|
3797
|
+
const piDir = path11.join(ctx.workingDirectory, ".slock", "pi");
|
|
3798
|
+
const sessionDir = path11.join(piDir, "sessions");
|
|
3799
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
3800
|
+
const slock = prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
3801
|
+
const runnerPath = opts.runnerPath ?? resolvePiSdkRunnerPath();
|
|
3802
|
+
const agentDir = opts.agentDir ?? getAgentDir();
|
|
3803
|
+
const runnerConfigPath = path11.join(
|
|
3804
|
+
piDir,
|
|
3805
|
+
`sdk-run-${(ctx.launchId || "launch").replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`
|
|
3806
|
+
);
|
|
3807
|
+
const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
|
|
3808
|
+
const runnerConfig = {
|
|
3809
|
+
cwd: ctx.workingDirectory,
|
|
3810
|
+
agentDir,
|
|
3811
|
+
sessionDir,
|
|
3812
|
+
sessionId: ctx.config.sessionId || null,
|
|
3813
|
+
standingPrompt: ctx.standingPrompt,
|
|
3814
|
+
prompt: turnPrompt,
|
|
3815
|
+
model: ctx.config.model && ctx.config.model !== "default" ? ctx.config.model : null
|
|
3816
|
+
};
|
|
3817
|
+
writeFileSync7(runnerConfigPath, `${JSON.stringify(runnerConfig)}
|
|
3818
|
+
`, { encoding: "utf8", mode: 384 });
|
|
3819
|
+
const args = [
|
|
3820
|
+
...buildPiSdkNodeArgs(runnerPath),
|
|
3821
|
+
"--config",
|
|
3822
|
+
runnerConfigPath
|
|
3823
|
+
];
|
|
3824
|
+
return {
|
|
3825
|
+
command,
|
|
3826
|
+
args,
|
|
3827
|
+
env: slock.spawnEnv,
|
|
3828
|
+
sessionDir,
|
|
3829
|
+
agentDir,
|
|
3830
|
+
runnerConfigPath,
|
|
3831
|
+
sdkVersion: PI_SDK_VERSION
|
|
3832
|
+
};
|
|
3833
|
+
}
|
|
3834
|
+
function isSystemFirstMessageTask2(message) {
|
|
3835
|
+
return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
|
|
3836
|
+
}
|
|
3837
|
+
function buildPiSystemPrompt(config) {
|
|
3838
|
+
return buildCliTransportSystemPrompt(config, {
|
|
3839
|
+
toolPrefix: CHAT_MCP_TOOL_PREFIX2,
|
|
3840
|
+
extraCriticalRules: [
|
|
3841
|
+
"- 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."
|
|
3842
|
+
],
|
|
3843
|
+
postStartupNotes: [
|
|
3844
|
+
"**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."
|
|
3845
|
+
],
|
|
3846
|
+
includeStdinNotificationSection: false,
|
|
3847
|
+
messageNotificationStyle: "poll"
|
|
3848
|
+
});
|
|
3849
|
+
}
|
|
3850
|
+
function contentText(content) {
|
|
3851
|
+
if (!content) return "";
|
|
3852
|
+
const chunks = [];
|
|
3853
|
+
for (const item of content) {
|
|
3854
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
3855
|
+
chunks.push(item.text);
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
return chunks.join("");
|
|
3859
|
+
}
|
|
3860
|
+
function apiKeyErrorMessage(line) {
|
|
3861
|
+
const trimmed = line.trim();
|
|
3862
|
+
if (!trimmed) return null;
|
|
3863
|
+
if (/no api key found/i.test(trimmed)) return trimmed;
|
|
3864
|
+
if (/api key.+required/i.test(trimmed)) return trimmed;
|
|
3865
|
+
if (/no models available/i.test(trimmed)) return trimmed;
|
|
3866
|
+
return null;
|
|
3867
|
+
}
|
|
3868
|
+
var PiDriver = class {
|
|
3869
|
+
id = "pi";
|
|
3870
|
+
lifecycle = {
|
|
3871
|
+
kind: "per_turn",
|
|
3872
|
+
start: "defer_until_concrete_message",
|
|
3873
|
+
exit: "terminate_on_turn_end",
|
|
3874
|
+
inFlightWake: "coalesce_into_pending"
|
|
3875
|
+
};
|
|
3876
|
+
communication = {
|
|
3877
|
+
chat: "slock_cli",
|
|
3878
|
+
runtimeControl: "none"
|
|
3879
|
+
};
|
|
3880
|
+
session = {
|
|
3881
|
+
recovery: "resume_or_fresh"
|
|
3882
|
+
};
|
|
3883
|
+
model = {
|
|
3884
|
+
detectedModelsVerifiedAs: "launchable",
|
|
3885
|
+
toLaunchSpec: (modelId, ctx) => {
|
|
3886
|
+
if (!ctx) return modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] };
|
|
3887
|
+
const launchCtx = {
|
|
3888
|
+
...ctx,
|
|
3889
|
+
config: {
|
|
3890
|
+
...ctx.config,
|
|
3891
|
+
model: modelId
|
|
3892
|
+
}
|
|
3893
|
+
};
|
|
3894
|
+
const launch = buildPiLaunchOptions(launchCtx);
|
|
3895
|
+
return {
|
|
3896
|
+
args: launch.args,
|
|
3897
|
+
env: launch.env,
|
|
3898
|
+
configFiles: [launch.runnerConfigPath],
|
|
3899
|
+
params: {
|
|
3900
|
+
agentDir: launch.agentDir,
|
|
3901
|
+
sessionDir: launch.sessionDir,
|
|
3902
|
+
sdkVersion: launch.sdkVersion,
|
|
3903
|
+
resources: "extensions/skills/prompt-templates/themes/context-files disabled by Slock policy"
|
|
3904
|
+
}
|
|
3905
|
+
};
|
|
3906
|
+
}
|
|
3907
|
+
};
|
|
3908
|
+
supportsStdinNotification = false;
|
|
3909
|
+
mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
|
|
3910
|
+
busyDeliveryMode = "none";
|
|
3911
|
+
terminateProcessOnTurnEnd = true;
|
|
3912
|
+
deferSpawnUntilMessage = true;
|
|
3913
|
+
usesSlockCliForCommunication = true;
|
|
3914
|
+
sessionId = null;
|
|
3915
|
+
sessionAnnounced = false;
|
|
3916
|
+
apiKeyErrorAnnounced = false;
|
|
3917
|
+
turnEnded = false;
|
|
3918
|
+
assistantTextByMessageId = /* @__PURE__ */ new Map();
|
|
3919
|
+
shouldDeferWakeMessage(message) {
|
|
3920
|
+
return isSystemFirstMessageTask2(message);
|
|
3921
|
+
}
|
|
3922
|
+
probe() {
|
|
3923
|
+
return probePi();
|
|
3924
|
+
}
|
|
3925
|
+
async detectModels() {
|
|
3926
|
+
return null;
|
|
3927
|
+
}
|
|
3928
|
+
spawn(ctx) {
|
|
3929
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
3930
|
+
this.sessionAnnounced = false;
|
|
3931
|
+
this.apiKeyErrorAnnounced = false;
|
|
3932
|
+
this.turnEnded = false;
|
|
3933
|
+
this.assistantTextByMessageId.clear();
|
|
3934
|
+
const unsupportedMessage = unsupportedPiVersionMessage(PI_SDK_VERSION);
|
|
3935
|
+
if (unsupportedMessage) throw new Error(unsupportedMessage);
|
|
3936
|
+
const launch = buildPiLaunchOptions(ctx);
|
|
3937
|
+
const proc = spawn8(launch.command, launch.args, {
|
|
3938
|
+
cwd: ctx.workingDirectory,
|
|
3939
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
3940
|
+
env: launch.env,
|
|
3941
|
+
shell: false
|
|
3942
|
+
});
|
|
3943
|
+
proc.stdin?.end();
|
|
3944
|
+
return { process: proc };
|
|
3945
|
+
}
|
|
3946
|
+
parseLine(line) {
|
|
3947
|
+
let event;
|
|
3948
|
+
try {
|
|
3949
|
+
event = JSON.parse(line);
|
|
3950
|
+
} catch {
|
|
3951
|
+
if (this.apiKeyErrorAnnounced) return [];
|
|
3952
|
+
const message = apiKeyErrorMessage(line);
|
|
3953
|
+
if (!message) return [];
|
|
3954
|
+
this.apiKeyErrorAnnounced = true;
|
|
3955
|
+
this.turnEnded = true;
|
|
3956
|
+
return [
|
|
3957
|
+
{ kind: "error", message },
|
|
3958
|
+
{ kind: "turn_end", sessionId: this.sessionId || void 0 }
|
|
3959
|
+
];
|
|
3960
|
+
}
|
|
3961
|
+
const events = [];
|
|
3962
|
+
if (event.type === "session" && event.id) {
|
|
3963
|
+
this.sessionId = event.id;
|
|
3964
|
+
}
|
|
3965
|
+
if (!this.sessionAnnounced && this.sessionId) {
|
|
3966
|
+
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
3967
|
+
this.sessionAnnounced = true;
|
|
3968
|
+
}
|
|
3969
|
+
switch (event.type) {
|
|
3970
|
+
case "agent_start":
|
|
3971
|
+
case "turn_start":
|
|
3972
|
+
events.push({ kind: "thinking", text: "" });
|
|
3973
|
+
break;
|
|
3974
|
+
case "message_update":
|
|
3975
|
+
case "message_end":
|
|
3976
|
+
if (event.message?.role === "assistant") {
|
|
3977
|
+
const key = event.message.id || "current";
|
|
3978
|
+
const currentText = contentText(event.message.content);
|
|
3979
|
+
const previousText = this.assistantTextByMessageId.get(key) ?? "";
|
|
3980
|
+
if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
|
|
3981
|
+
events.push({ kind: "text", text: currentText.slice(previousText.length) });
|
|
3982
|
+
} else if (currentText && currentText !== previousText) {
|
|
3983
|
+
events.push({ kind: "text", text: currentText });
|
|
3984
|
+
}
|
|
3985
|
+
this.assistantTextByMessageId.set(key, currentText);
|
|
3986
|
+
if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
|
|
3987
|
+
events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
break;
|
|
3991
|
+
case "tool_execution_start":
|
|
3992
|
+
events.push({
|
|
3993
|
+
kind: "tool_call",
|
|
3994
|
+
name: event.toolName || "unknown_tool",
|
|
3995
|
+
input: event.args
|
|
3996
|
+
});
|
|
3997
|
+
break;
|
|
3998
|
+
case "tool_execution_end":
|
|
3999
|
+
events.push({
|
|
4000
|
+
kind: "tool_output",
|
|
4001
|
+
name: event.toolName || "unknown_tool"
|
|
4002
|
+
});
|
|
4003
|
+
if (event.isError) {
|
|
4004
|
+
events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
|
|
4005
|
+
}
|
|
4006
|
+
break;
|
|
4007
|
+
case "compaction_start":
|
|
4008
|
+
events.push({ kind: "compaction_started" });
|
|
4009
|
+
break;
|
|
4010
|
+
case "compaction_end":
|
|
4011
|
+
events.push({ kind: "compaction_finished" });
|
|
4012
|
+
if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
|
|
4013
|
+
break;
|
|
4014
|
+
case "turn_end":
|
|
4015
|
+
case "agent_end":
|
|
4016
|
+
if (!this.turnEnded) {
|
|
4017
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
4018
|
+
this.turnEnded = true;
|
|
4019
|
+
}
|
|
4020
|
+
break;
|
|
4021
|
+
}
|
|
4022
|
+
return events;
|
|
4023
|
+
}
|
|
4024
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
4025
|
+
return null;
|
|
4026
|
+
}
|
|
4027
|
+
buildSystemPrompt(config, _agentId) {
|
|
4028
|
+
return buildPiSystemPrompt(config);
|
|
4029
|
+
}
|
|
4030
|
+
};
|
|
4031
|
+
|
|
3594
4032
|
// src/drivers/index.ts
|
|
3595
4033
|
var driverFactories = {
|
|
3596
4034
|
claude: () => new ClaudeDriver(),
|
|
@@ -3599,7 +4037,8 @@ var driverFactories = {
|
|
|
3599
4037
|
cursor: () => new CursorDriver(),
|
|
3600
4038
|
gemini: () => new GeminiDriver(),
|
|
3601
4039
|
kimi: () => new KimiDriver(),
|
|
3602
|
-
opencode: () => new OpenCodeDriver()
|
|
4040
|
+
opencode: () => new OpenCodeDriver(),
|
|
4041
|
+
pi: () => new PiDriver()
|
|
3603
4042
|
};
|
|
3604
4043
|
function getDriver(runtimeId) {
|
|
3605
4044
|
const createDriver = driverFactories[runtimeId];
|
|
@@ -3612,7 +4051,7 @@ function getDriver(runtimeId) {
|
|
|
3612
4051
|
|
|
3613
4052
|
// src/workspaces.ts
|
|
3614
4053
|
import { readdir, rm, stat } from "fs/promises";
|
|
3615
|
-
import
|
|
4054
|
+
import path12 from "path";
|
|
3616
4055
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
3617
4056
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
3618
4057
|
}
|
|
@@ -3620,7 +4059,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
3620
4059
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
3621
4060
|
return null;
|
|
3622
4061
|
}
|
|
3623
|
-
return
|
|
4062
|
+
return path12.join(dataDir, directoryName);
|
|
3624
4063
|
}
|
|
3625
4064
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
3626
4065
|
return {
|
|
@@ -3669,7 +4108,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
3669
4108
|
return summary;
|
|
3670
4109
|
}
|
|
3671
4110
|
const childSummaries = await Promise.all(
|
|
3672
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
4111
|
+
entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
|
|
3673
4112
|
);
|
|
3674
4113
|
for (const childSummary of childSummaries) {
|
|
3675
4114
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -3688,7 +4127,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
3688
4127
|
if (!entry.isDirectory()) {
|
|
3689
4128
|
return null;
|
|
3690
4129
|
}
|
|
3691
|
-
const dirPath =
|
|
4130
|
+
const dirPath = path12.join(dataDir, entry.name);
|
|
3692
4131
|
try {
|
|
3693
4132
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
3694
4133
|
return {
|
|
@@ -3892,12 +4331,12 @@ function findSessionJsonl(root, predicate) {
|
|
|
3892
4331
|
for (const entry of entries) {
|
|
3893
4332
|
if (++visited > maxEntries) return null;
|
|
3894
4333
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
3895
|
-
return
|
|
4334
|
+
return path13.join(dir, entry.name);
|
|
3896
4335
|
}
|
|
3897
4336
|
for (const entry of entries) {
|
|
3898
4337
|
if (++visited > maxEntries) return null;
|
|
3899
4338
|
if (!entry.isDirectory()) continue;
|
|
3900
|
-
const found = visit(
|
|
4339
|
+
const found = visit(path13.join(dir, entry.name), depth - 1);
|
|
3901
4340
|
if (found) return found;
|
|
3902
4341
|
}
|
|
3903
4342
|
return null;
|
|
@@ -3910,10 +4349,10 @@ function safeSessionFilename(value) {
|
|
|
3910
4349
|
}
|
|
3911
4350
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
3912
4351
|
try {
|
|
3913
|
-
const dir =
|
|
3914
|
-
|
|
3915
|
-
const filePath =
|
|
3916
|
-
|
|
4352
|
+
const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
|
|
4353
|
+
mkdirSync5(dir, { recursive: true });
|
|
4354
|
+
const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
4355
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
3917
4356
|
type: "runtime_session_handoff",
|
|
3918
4357
|
runtime,
|
|
3919
4358
|
sessionId,
|
|
@@ -3932,7 +4371,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
3932
4371
|
}
|
|
3933
4372
|
}
|
|
3934
4373
|
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
|
|
3935
|
-
const directPath =
|
|
4374
|
+
const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
|
|
3936
4375
|
if (directPath) {
|
|
3937
4376
|
try {
|
|
3938
4377
|
if (statSync2(directPath).isFile()) {
|
|
@@ -3941,7 +4380,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), f
|
|
|
3941
4380
|
} catch {
|
|
3942
4381
|
}
|
|
3943
4382
|
}
|
|
3944
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
4383
|
+
const resolvedPath = runtime === "claude" ? findSessionJsonl(path13.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path13.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
|
|
3945
4384
|
if (!resolvedPath && fallbackDir) {
|
|
3946
4385
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
3947
4386
|
if (fallback) return fallback;
|
|
@@ -5075,26 +5514,26 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
5075
5514
|
this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
|
|
5076
5515
|
try {
|
|
5077
5516
|
const driver = this.driverResolver(config.runtime || "claude");
|
|
5078
|
-
const agentDataDir =
|
|
5517
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
5079
5518
|
await mkdir(agentDataDir, { recursive: true });
|
|
5080
5519
|
const runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
5081
|
-
const memoryMdPath =
|
|
5520
|
+
const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
|
|
5082
5521
|
try {
|
|
5083
5522
|
await access(memoryMdPath);
|
|
5084
5523
|
} catch {
|
|
5085
5524
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
5086
5525
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
5087
5526
|
}
|
|
5088
|
-
const notesDir =
|
|
5527
|
+
const notesDir = path13.join(agentDataDir, "notes");
|
|
5089
5528
|
await mkdir(notesDir, { recursive: true });
|
|
5090
5529
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
5091
5530
|
const seedFiles = buildOnboardingSeedFiles();
|
|
5092
5531
|
for (const { relativePath, content } of seedFiles) {
|
|
5093
|
-
const fullPath =
|
|
5532
|
+
const fullPath = path13.join(agentDataDir, relativePath);
|
|
5094
5533
|
try {
|
|
5095
5534
|
await access(fullPath);
|
|
5096
5535
|
} catch {
|
|
5097
|
-
await mkdir(
|
|
5536
|
+
await mkdir(path13.dirname(fullPath), { recursive: true });
|
|
5098
5537
|
await writeFile(fullPath, content);
|
|
5099
5538
|
}
|
|
5100
5539
|
}
|
|
@@ -5350,11 +5789,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5350
5789
|
...runtimeTraceCounterAttrs(ap),
|
|
5351
5790
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "process_exit")
|
|
5352
5791
|
});
|
|
5353
|
-
|
|
5354
|
-
this.finishCompactionIfActive(agentId, "Context compaction finished (inferred from process exit)");
|
|
5355
|
-
} else {
|
|
5356
|
-
this.clearCompactionWatchdog(ap);
|
|
5357
|
-
}
|
|
5792
|
+
this.interruptCompactionIfActive(agentId);
|
|
5358
5793
|
this.agents.delete(agentId);
|
|
5359
5794
|
if (missingResumeSession) {
|
|
5360
5795
|
const staleSessionId = ap.sessionId;
|
|
@@ -5739,6 +6174,37 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5739
6174
|
}));
|
|
5740
6175
|
return true;
|
|
5741
6176
|
}
|
|
6177
|
+
if (ap.gatedSteering.compacting) {
|
|
6178
|
+
ap.pendingNotificationCount++;
|
|
6179
|
+
if (ap.notificationTimer) {
|
|
6180
|
+
clearTimeout(ap.notificationTimer);
|
|
6181
|
+
ap.notificationTimer = null;
|
|
6182
|
+
}
|
|
6183
|
+
if (ap.driver.busyDeliveryMode === "gated") {
|
|
6184
|
+
this.recordGatedSteeringEvent(agentId, ap, "buffer", {
|
|
6185
|
+
reason: "compaction_boundary",
|
|
6186
|
+
pendingMessages: ap.inbox.length
|
|
6187
|
+
});
|
|
6188
|
+
}
|
|
6189
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.delivery_buffered", {
|
|
6190
|
+
pendingNotificationCount: ap.pendingNotificationCount,
|
|
6191
|
+
pendingMessages: ap.inbox.length,
|
|
6192
|
+
busyDeliveryMode: ap.driver.busyDeliveryMode
|
|
6193
|
+
});
|
|
6194
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
6195
|
+
outcome: "queued_compaction_boundary",
|
|
6196
|
+
accepted: true,
|
|
6197
|
+
process_present: true,
|
|
6198
|
+
runtime: ap.config.runtime,
|
|
6199
|
+
session_id_present: true,
|
|
6200
|
+
launchId: ap.launchId || void 0,
|
|
6201
|
+
inbox_count: ap.inbox.length,
|
|
6202
|
+
pending_notification_count: ap.pendingNotificationCount,
|
|
6203
|
+
busy_delivery_mode: ap.driver.busyDeliveryMode,
|
|
6204
|
+
notification_timer_present: false
|
|
6205
|
+
}));
|
|
6206
|
+
return true;
|
|
6207
|
+
}
|
|
5742
6208
|
if (ap.driver.busyDeliveryMode === "gated") {
|
|
5743
6209
|
ap.pendingNotificationCount++;
|
|
5744
6210
|
this.recordGatedSteeringEvent(agentId, ap, "buffer", {
|
|
@@ -5776,7 +6242,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5776
6242
|
return true;
|
|
5777
6243
|
}
|
|
5778
6244
|
async resetWorkspace(agentId) {
|
|
5779
|
-
const agentDataDir =
|
|
6245
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
5780
6246
|
try {
|
|
5781
6247
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
5782
6248
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -5813,7 +6279,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5813
6279
|
return result;
|
|
5814
6280
|
}
|
|
5815
6281
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
5816
|
-
const workspacePath =
|
|
6282
|
+
const workspacePath = path13.join(this.dataDir, agentId);
|
|
5817
6283
|
return {
|
|
5818
6284
|
agentId,
|
|
5819
6285
|
launchId,
|
|
@@ -6061,7 +6527,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6061
6527
|
}
|
|
6062
6528
|
// Workspace file browsing
|
|
6063
6529
|
async getFileTree(agentId, dirPath) {
|
|
6064
|
-
const agentDir =
|
|
6530
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
6065
6531
|
try {
|
|
6066
6532
|
await stat2(agentDir);
|
|
6067
6533
|
} catch {
|
|
@@ -6069,8 +6535,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6069
6535
|
}
|
|
6070
6536
|
let targetDir = agentDir;
|
|
6071
6537
|
if (dirPath) {
|
|
6072
|
-
const resolved =
|
|
6073
|
-
if (!resolved.startsWith(agentDir +
|
|
6538
|
+
const resolved = path13.resolve(agentDir, dirPath);
|
|
6539
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
6074
6540
|
return [];
|
|
6075
6541
|
}
|
|
6076
6542
|
targetDir = resolved;
|
|
@@ -6078,14 +6544,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6078
6544
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
6079
6545
|
}
|
|
6080
6546
|
async readFile(agentId, filePath) {
|
|
6081
|
-
const agentDir =
|
|
6082
|
-
const resolved =
|
|
6083
|
-
if (!resolved.startsWith(agentDir +
|
|
6547
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
6548
|
+
const resolved = path13.resolve(agentDir, filePath);
|
|
6549
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
6084
6550
|
throw new Error("Access denied");
|
|
6085
6551
|
}
|
|
6086
6552
|
const info = await stat2(resolved);
|
|
6087
6553
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
6088
|
-
const ext =
|
|
6554
|
+
const ext = path13.extname(resolved).toLowerCase();
|
|
6089
6555
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
6090
6556
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
6091
6557
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -6120,13 +6586,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6120
6586
|
const agent = this.agents.get(agentId);
|
|
6121
6587
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
6122
6588
|
const home = os6.homedir();
|
|
6123
|
-
const workspaceDir =
|
|
6589
|
+
const workspaceDir = path13.join(this.dataDir, agentId);
|
|
6124
6590
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
6125
6591
|
const globalResults = await Promise.all(
|
|
6126
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
6592
|
+
paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
|
|
6127
6593
|
);
|
|
6128
6594
|
const workspaceResults = await Promise.all(
|
|
6129
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
6595
|
+
paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
|
|
6130
6596
|
);
|
|
6131
6597
|
const dedup = (skills) => {
|
|
6132
6598
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -6155,7 +6621,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6155
6621
|
const skills = [];
|
|
6156
6622
|
for (const entry of entries) {
|
|
6157
6623
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
6158
|
-
const skillMd =
|
|
6624
|
+
const skillMd = path13.join(dir, entry.name, "SKILL.md");
|
|
6159
6625
|
try {
|
|
6160
6626
|
const content = await readFile(skillMd, "utf-8");
|
|
6161
6627
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -6166,7 +6632,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6166
6632
|
} else if (entry.name.endsWith(".md")) {
|
|
6167
6633
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
6168
6634
|
try {
|
|
6169
|
-
const content = await readFile(
|
|
6635
|
+
const content = await readFile(path13.join(dir, entry.name), "utf-8");
|
|
6170
6636
|
const skill = this.parseSkillMd(cmdName, content);
|
|
6171
6637
|
skill.sourcePath = dir;
|
|
6172
6638
|
skills.push(skill);
|
|
@@ -6407,11 +6873,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6407
6873
|
ap.compactionWatchdog = null;
|
|
6408
6874
|
this.broadcastActivity(agentId, "working", "Context compaction still running; no finish event observed");
|
|
6409
6875
|
}
|
|
6410
|
-
|
|
6876
|
+
completeCompactionIfActive(agentId, detail = "Context compaction finished", options = {}) {
|
|
6411
6877
|
const ap = this.agents.get(agentId);
|
|
6412
6878
|
if (!ap || !ap.compactionStartedAt) return;
|
|
6413
6879
|
this.clearCompactionWatchdog(ap);
|
|
6414
6880
|
this.broadcastActivity(agentId, "working", detail, [{ kind: "compaction_finished" }]);
|
|
6881
|
+
ap.gatedSteering.compacting = false;
|
|
6882
|
+
this.setGatedSteeringPhase(agentId, ap, "assistant_continuation", { event: "compaction_finished", inferred: true });
|
|
6883
|
+
if (options.flushBoundaryMessages ?? true) {
|
|
6884
|
+
this.flushCompactionBoundaryMessages(agentId, ap);
|
|
6885
|
+
}
|
|
6886
|
+
ap.isIdle = false;
|
|
6887
|
+
}
|
|
6888
|
+
interruptCompactionIfActive(agentId) {
|
|
6889
|
+
const ap = this.agents.get(agentId);
|
|
6890
|
+
if (!ap || !ap.compactionStartedAt && !ap.gatedSteering.compacting) return;
|
|
6891
|
+
this.clearCompactionWatchdog(ap);
|
|
6892
|
+
ap.gatedSteering.compacting = false;
|
|
6415
6893
|
}
|
|
6416
6894
|
messagesTraceAttrs(messages) {
|
|
6417
6895
|
if (!messages || messages.length === 0) return {};
|
|
@@ -6734,28 +7212,28 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6734
7212
|
this.sendRuntimeProfileReport(agentId);
|
|
6735
7213
|
break;
|
|
6736
7214
|
case "thinking": {
|
|
6737
|
-
this.
|
|
7215
|
+
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
7216
|
+
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
|
|
6738
7217
|
this.queueTrajectoryText(agentId, "thinking", event.text);
|
|
6739
7218
|
if (ap) ap.isIdle = false;
|
|
6740
|
-
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
6741
7219
|
this.setGatedSteeringPhase(agentId, ap, "assistant_continuation", { event: "thinking" });
|
|
6742
7220
|
break;
|
|
6743
7221
|
}
|
|
6744
7222
|
case "text": {
|
|
6745
|
-
this.
|
|
7223
|
+
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
7224
|
+
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
|
|
6746
7225
|
this.queueTrajectoryText(agentId, "text", event.text);
|
|
6747
7226
|
if (ap) ap.isIdle = false;
|
|
6748
|
-
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
6749
7227
|
this.setGatedSteeringPhase(agentId, ap, "assistant_continuation", { event: "text" });
|
|
6750
7228
|
break;
|
|
6751
7229
|
}
|
|
6752
7230
|
case "tool_call": {
|
|
6753
|
-
this.
|
|
7231
|
+
if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
7232
|
+
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed tool use)");
|
|
6754
7233
|
this.flushPendingTrajectory(agentId);
|
|
6755
7234
|
const invocation = normalizeToolDisplayInvocation(event.name, event.input);
|
|
6756
7235
|
if (ap) {
|
|
6757
7236
|
ap.gatedSteering.outstandingToolUses++;
|
|
6758
|
-
this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
6759
7237
|
this.noteRuntimeProfileToolCall(agentId, ap, invocation.toolName);
|
|
6760
7238
|
this.recordRuntimeTraceEvent(agentId, ap, "tool.call.started", { tool: invocation.toolName });
|
|
6761
7239
|
this.setGatedSteeringPhase(agentId, ap, "tool_wait", {
|
|
@@ -6816,7 +7294,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6816
7294
|
break;
|
|
6817
7295
|
case "turn_end":
|
|
6818
7296
|
if (ap) this.recordRuntimeTraceEvent(agentId, ap, "runtime.turn.completed");
|
|
6819
|
-
this.
|
|
7297
|
+
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from turn end)", {
|
|
7298
|
+
flushBoundaryMessages: false
|
|
7299
|
+
});
|
|
6820
7300
|
this.flushPendingTrajectory(agentId);
|
|
6821
7301
|
if (ap) {
|
|
6822
7302
|
this.clearGatedInFlightBatch(agentId, ap, "turn_end");
|
|
@@ -6873,7 +7353,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6873
7353
|
}
|
|
6874
7354
|
break;
|
|
6875
7355
|
case "error": {
|
|
6876
|
-
this.
|
|
7356
|
+
this.interruptCompactionIfActive(agentId);
|
|
6877
7357
|
this.flushPendingTrajectory(agentId);
|
|
6878
7358
|
if (ap) ap.lastRuntimeError = event.message;
|
|
6879
7359
|
if (ap) {
|
|
@@ -7116,8 +7596,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
7116
7596
|
const nodes = [];
|
|
7117
7597
|
for (const entry of entries) {
|
|
7118
7598
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
7119
|
-
const fullPath =
|
|
7120
|
-
const relativePath =
|
|
7599
|
+
const fullPath = path13.join(dir, entry.name);
|
|
7600
|
+
const relativePath = path13.relative(rootDir, fullPath);
|
|
7121
7601
|
let info;
|
|
7122
7602
|
try {
|
|
7123
7603
|
info = await stat2(fullPath);
|
|
@@ -7420,9 +7900,9 @@ var ReminderCache = class {
|
|
|
7420
7900
|
|
|
7421
7901
|
// src/machineLock.ts
|
|
7422
7902
|
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
7423
|
-
import { mkdirSync as
|
|
7903
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
7424
7904
|
import os7 from "os";
|
|
7425
|
-
import
|
|
7905
|
+
import path14 from "path";
|
|
7426
7906
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
7427
7907
|
var DaemonMachineLockConflictError = class extends Error {
|
|
7428
7908
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -7444,7 +7924,7 @@ function resolveDefaultMachineStateRoot() {
|
|
|
7444
7924
|
return resolveSlockHomePath("machines");
|
|
7445
7925
|
}
|
|
7446
7926
|
function ownerPath(lockDir) {
|
|
7447
|
-
return
|
|
7927
|
+
return path14.join(lockDir, "owner.json");
|
|
7448
7928
|
}
|
|
7449
7929
|
function readOwner(lockDir) {
|
|
7450
7930
|
try {
|
|
@@ -7474,13 +7954,13 @@ function acquireDaemonMachineLock(options) {
|
|
|
7474
7954
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
7475
7955
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
7476
7956
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
7477
|
-
const machineDir =
|
|
7478
|
-
const lockDir =
|
|
7957
|
+
const machineDir = path14.join(rootDir, lockId);
|
|
7958
|
+
const lockDir = path14.join(machineDir, "daemon.lock");
|
|
7479
7959
|
const token = randomUUID2();
|
|
7480
|
-
|
|
7960
|
+
mkdirSync6(machineDir, { recursive: true });
|
|
7481
7961
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
7482
7962
|
try {
|
|
7483
|
-
|
|
7963
|
+
mkdirSync6(lockDir);
|
|
7484
7964
|
const owner = {
|
|
7485
7965
|
pid: process.pid,
|
|
7486
7966
|
token,
|
|
@@ -7490,7 +7970,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
7490
7970
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
7491
7971
|
};
|
|
7492
7972
|
try {
|
|
7493
|
-
|
|
7973
|
+
writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
7494
7974
|
`, { mode: 384 });
|
|
7495
7975
|
} catch (err) {
|
|
7496
7976
|
rmSync2(lockDir, { recursive: true, force: true });
|
|
@@ -7527,8 +8007,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
7527
8007
|
}
|
|
7528
8008
|
|
|
7529
8009
|
// src/localTraceSink.ts
|
|
7530
|
-
import { appendFileSync, mkdirSync as
|
|
7531
|
-
import
|
|
8010
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync3, rmSync as rmSync3, statSync as statSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
8011
|
+
import path15 from "path";
|
|
7532
8012
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
7533
8013
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
7534
8014
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -7564,7 +8044,7 @@ var LocalRotatingTraceSink = class {
|
|
|
7564
8044
|
currentSize = 0;
|
|
7565
8045
|
sequence = 0;
|
|
7566
8046
|
constructor(options) {
|
|
7567
|
-
this.traceDir =
|
|
8047
|
+
this.traceDir = path15.join(options.machineDir, "traces");
|
|
7568
8048
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
7569
8049
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
7570
8050
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -7590,15 +8070,15 @@ var LocalRotatingTraceSink = class {
|
|
|
7590
8070
|
return this.currentFile;
|
|
7591
8071
|
}
|
|
7592
8072
|
ensureFile(nextBytes) {
|
|
7593
|
-
|
|
8073
|
+
mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
|
|
7594
8074
|
const nowMs = this.nowMsProvider();
|
|
7595
8075
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
7596
8076
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
7597
|
-
this.currentFile =
|
|
8077
|
+
this.currentFile = path15.join(
|
|
7598
8078
|
this.traceDir,
|
|
7599
8079
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
7600
8080
|
);
|
|
7601
|
-
|
|
8081
|
+
writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
|
|
7602
8082
|
this.currentSize = statSync4(this.currentFile).size;
|
|
7603
8083
|
this.currentFileOpenedAtMs = nowMs;
|
|
7604
8084
|
this.pruneOldFiles();
|
|
@@ -7609,7 +8089,7 @@ var LocalRotatingTraceSink = class {
|
|
|
7609
8089
|
const excess = files.length - this.maxFiles;
|
|
7610
8090
|
if (excess <= 0) return;
|
|
7611
8091
|
for (const file of files.slice(0, excess)) {
|
|
7612
|
-
rmSync3(
|
|
8092
|
+
rmSync3(path15.join(this.traceDir, file), { force: true });
|
|
7613
8093
|
}
|
|
7614
8094
|
}
|
|
7615
8095
|
};
|
|
@@ -7696,11 +8176,11 @@ function isDiagnosticErrorAttr(key) {
|
|
|
7696
8176
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
|
|
7697
8177
|
import { gzipSync } from "zlib";
|
|
7698
8178
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
7699
|
-
import
|
|
8179
|
+
import path16 from "path";
|
|
7700
8180
|
|
|
7701
8181
|
// src/directUploadCapability.ts
|
|
7702
|
-
function joinUrl(base,
|
|
7703
|
-
return `${base.replace(/\/+$/, "")}${
|
|
8182
|
+
function joinUrl(base, path18) {
|
|
8183
|
+
return `${base.replace(/\/+$/, "")}${path18}`;
|
|
7704
8184
|
}
|
|
7705
8185
|
function jsonHeaders(apiKey) {
|
|
7706
8186
|
return {
|
|
@@ -7919,7 +8399,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
7919
8399
|
}, nextMs);
|
|
7920
8400
|
}
|
|
7921
8401
|
async findUploadCandidates() {
|
|
7922
|
-
const traceDir =
|
|
8402
|
+
const traceDir = path16.join(this.options.machineDir, "traces");
|
|
7923
8403
|
let names;
|
|
7924
8404
|
try {
|
|
7925
8405
|
names = await readdir3(traceDir);
|
|
@@ -7931,8 +8411,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
7931
8411
|
const currentFile = this.options.currentFileProvider?.();
|
|
7932
8412
|
const candidates = [];
|
|
7933
8413
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
7934
|
-
const file =
|
|
7935
|
-
if (currentFile &&
|
|
8414
|
+
const file = path16.join(traceDir, name);
|
|
8415
|
+
if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
|
|
7936
8416
|
if (await this.isUploaded(file)) continue;
|
|
7937
8417
|
try {
|
|
7938
8418
|
const info = await stat3(file);
|
|
@@ -8006,8 +8486,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
8006
8486
|
}
|
|
8007
8487
|
}
|
|
8008
8488
|
uploadStatePath(file) {
|
|
8009
|
-
const stateDir =
|
|
8010
|
-
return
|
|
8489
|
+
const stateDir = path16.join(this.options.machineDir, "trace-uploads");
|
|
8490
|
+
return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
|
|
8011
8491
|
}
|
|
8012
8492
|
async isUploaded(file) {
|
|
8013
8493
|
try {
|
|
@@ -8019,9 +8499,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
8019
8499
|
}
|
|
8020
8500
|
async markUploaded(file, metadata) {
|
|
8021
8501
|
const stateFile = this.uploadStatePath(file);
|
|
8022
|
-
await mkdir2(
|
|
8502
|
+
await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
|
|
8023
8503
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
8024
|
-
file:
|
|
8504
|
+
file: path16.basename(file),
|
|
8025
8505
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8026
8506
|
...metadata
|
|
8027
8507
|
}, null, 2)}
|
|
@@ -8040,10 +8520,10 @@ function readPositiveIntegerEnv2(name, fallback) {
|
|
|
8040
8520
|
|
|
8041
8521
|
// src/core.ts
|
|
8042
8522
|
var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
8043
|
-
var DAEMON_CLI_USAGE =
|
|
8044
|
-
function parseDaemonCliArgs(args) {
|
|
8523
|
+
var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
|
|
8524
|
+
function parseDaemonCliArgs(args, env = {}) {
|
|
8045
8525
|
let serverUrl = "";
|
|
8046
|
-
let apiKey = "";
|
|
8526
|
+
let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
|
|
8047
8527
|
for (let i = 0; i < args.length; i++) {
|
|
8048
8528
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
8049
8529
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -8060,23 +8540,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
8060
8540
|
}
|
|
8061
8541
|
}
|
|
8062
8542
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
8063
|
-
const dirname =
|
|
8064
|
-
const jsPath =
|
|
8543
|
+
const dirname = path17.dirname(fileURLToPath2(moduleUrl));
|
|
8544
|
+
const jsPath = path17.resolve(dirname, "chat-bridge.js");
|
|
8065
8545
|
try {
|
|
8066
8546
|
accessSync(jsPath);
|
|
8067
8547
|
return jsPath;
|
|
8068
8548
|
} catch {
|
|
8069
|
-
return
|
|
8549
|
+
return path17.resolve(dirname, "chat-bridge.ts");
|
|
8070
8550
|
}
|
|
8071
8551
|
}
|
|
8072
8552
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
8073
|
-
const thisDir =
|
|
8074
|
-
const bundledDistPath =
|
|
8553
|
+
const thisDir = path17.dirname(fileURLToPath2(moduleUrl));
|
|
8554
|
+
const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
|
|
8075
8555
|
try {
|
|
8076
8556
|
accessSync(bundledDistPath);
|
|
8077
8557
|
return bundledDistPath;
|
|
8078
8558
|
} catch {
|
|
8079
|
-
const workspaceDistPath =
|
|
8559
|
+
const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
8080
8560
|
accessSync(workspaceDistPath);
|
|
8081
8561
|
return workspaceDistPath;
|
|
8082
8562
|
}
|
|
@@ -8255,7 +8735,7 @@ var DaemonCore = class {
|
|
|
8255
8735
|
}
|
|
8256
8736
|
resolveMachineStateRoot() {
|
|
8257
8737
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
8258
|
-
if (this.options.dataDir) return
|
|
8738
|
+
if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
|
|
8259
8739
|
return resolveDefaultMachineStateRoot();
|
|
8260
8740
|
}
|
|
8261
8741
|
shouldEnableLocalTrace() {
|
|
@@ -8638,6 +9118,8 @@ var DaemonCore = class {
|
|
|
8638
9118
|
};
|
|
8639
9119
|
|
|
8640
9120
|
export {
|
|
9121
|
+
DAEMON_API_KEY_ENV,
|
|
9122
|
+
scrubDaemonAuthEnv,
|
|
8641
9123
|
resolveWorkspaceDirectoryPath,
|
|
8642
9124
|
scanWorkspaceDirectories,
|
|
8643
9125
|
deleteWorkspaceDirectory,
|