@slock-ai/daemon 0.52.0 → 0.52.1-play.20260520171228
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-XEHIMW55.js → chunk-UM3RDELD.js} +683 -116
- package/dist/cli/index.js +151 -0
- 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
|
|
@@ -823,6 +824,7 @@ function buildPrompt(config, variant, opts) {
|
|
|
823
824
|
"- Always communicate through `slock` CLI commands. This is your only output channel.",
|
|
824
825
|
...opts.extraCriticalRules,
|
|
825
826
|
"- Use only the provided `slock` CLI commands for messaging.",
|
|
827
|
+
"- Do not combine multiple `slock` CLI commands in one shell command. Run one `slock` command per tool call, read its output, then decide the next command.",
|
|
826
828
|
"- Always claim a task via `slock task claim` before starting work on it. If the claim fails, move on to a different task."
|
|
827
829
|
] : [
|
|
828
830
|
`- Always communicate through ${sendCmd}. This is your only output channel.`,
|
|
@@ -871,13 +873,15 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
|
|
|
871
873
|
17. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
872
874
|
18. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
|
|
873
875
|
19. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--avatar-url pixel:random:<seed>\`, \`--display-name <name>\`, and \`--description <text>\`. Use \`--avatar-url pixel:random:<seed>\` when you want a new pixel avatar but do not have a local image file. 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
|
|
876
|
+
20. **\`slock integration list\`** \u2014 List registered third-party services and this agent's active Slock Agent Logins.
|
|
877
|
+
21. **\`slock integration login\`** \u2014 Provision or reuse this agent's login for a registered third-party service.
|
|
878
|
+
22. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
879
|
+
23. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
|
|
880
|
+
24. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
|
|
881
|
+
25. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
|
|
882
|
+
26. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
|
|
883
|
+
27. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
|
|
884
|
+
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
885
|
|
|
882
886
|
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
887
|
- failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
|
|
@@ -978,6 +982,11 @@ Each channel has a **name** and optionally a **description** that define its pur
|
|
|
978
982
|
- **Reply in context** \u2014 always respond in the channel/thread the message came from.
|
|
979
983
|
- **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
984
|
- If unsure where something belongs, call ${serverInfoCmd} to review channel descriptions.`;
|
|
985
|
+
const thirdPartyIntegrationsSection = isCli ? `### Third-party integrations
|
|
986
|
+
|
|
987
|
+
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
|
|
988
|
+
|
|
989
|
+
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
990
|
const readingHistorySection = isCli ? `### Reading history
|
|
982
991
|
|
|
983
992
|
\`slock message read --channel "#channel-name"\` or \`slock message read --channel dm:@peer-name\` or \`slock message read --channel "#channel:shortid"\`
|
|
@@ -1149,6 +1158,8 @@ ${discoverySection}
|
|
|
1149
1158
|
|
|
1150
1159
|
${channelAwarenessSection}
|
|
1151
1160
|
|
|
1161
|
+
${thirdPartyIntegrationsSection}
|
|
1162
|
+
|
|
1152
1163
|
${readingHistorySection}
|
|
1153
1164
|
|
|
1154
1165
|
${historicalReferenceSection}
|
|
@@ -1180,6 +1191,17 @@ Keep the user informed. They cannot see your internal reasoning, so:
|
|
|
1180
1191
|
- For multi-step work, send short progress updates (e.g. "Working on step 2/3\u2026").
|
|
1181
1192
|
- When done, summarize the result.
|
|
1182
1193
|
- Keep updates concise \u2014 one or two sentences. Don't flood the chat.
|
|
1194
|
+
- 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:
|
|
1195
|
+
|
|
1196
|
+
\`\`\`html
|
|
1197
|
+
<details>
|
|
1198
|
+
<summary>Evidence, logs, or edge cases</summary>
|
|
1199
|
+
|
|
1200
|
+
Detailed notes go here.
|
|
1201
|
+
</details>
|
|
1202
|
+
\`\`\`
|
|
1203
|
+
|
|
1204
|
+
Do not hide the main recommendation, blocker, or required action inside \`<details>\`; only fold supporting evidence, logs, alternatives, or extended rationale.
|
|
1183
1205
|
|
|
1184
1206
|
### Conversation etiquette
|
|
1185
1207
|
|
|
@@ -1348,6 +1370,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1348
1370
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1349
1371
|
}
|
|
1350
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
|
+
|
|
1351
1386
|
// src/agentCredentialProxy.ts
|
|
1352
1387
|
import { randomBytes } from "crypto";
|
|
1353
1388
|
import http from "http";
|
|
@@ -1810,6 +1845,18 @@ function consumeVisibleResponse(registration, targetUrl, sendTarget, responseTex
|
|
|
1810
1845
|
});
|
|
1811
1846
|
return;
|
|
1812
1847
|
}
|
|
1848
|
+
if (targetUrl.pathname === "/internal/agent-api/send" && parsed.state === "sent") {
|
|
1849
|
+
const messageSeq2 = typeof parsed.messageSeq === "number" && Number.isFinite(parsed.messageSeq) ? Math.floor(parsed.messageSeq) : void 0;
|
|
1850
|
+
if (sendTarget && messageSeq2 && messageSeq2 > 0) {
|
|
1851
|
+
coordinator.consumeVisibleMessages({
|
|
1852
|
+
target: sendTarget,
|
|
1853
|
+
messages: normalizeVisibleMessages([{ seq: messageSeq2, id: parsed.messageId }], sendTarget),
|
|
1854
|
+
boundarySeq: messageSeq2,
|
|
1855
|
+
source: "agent_api_send_commit"
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1813
1860
|
if (targetUrl.pathname === "/internal/agent-api/events" && Array.isArray(parsed.events)) {
|
|
1814
1861
|
const messages = normalizeVisibleMessages(parsed.events);
|
|
1815
1862
|
coordinator.consumeVisibleMessages({ messages, source: "agent_api_events" });
|
|
@@ -1938,7 +1985,7 @@ ${cmdCredentialLine}"${process.execPath}" "${ctx.slockCliPath}" %*\r
|
|
|
1938
1985
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
1939
1986
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
1940
1987
|
};
|
|
1941
|
-
|
|
1988
|
+
scrubDaemonChildEnv(spawnEnv);
|
|
1942
1989
|
delete spawnEnv.SLOCK_AGENT_CREDENTIAL_KEY;
|
|
1943
1990
|
delete spawnEnv.SLOCK_AGENT_CREDENTIAL_KEY_FILE;
|
|
1944
1991
|
delete spawnEnv.SLOCK_AGENT_PROXY_URL;
|
|
@@ -1985,7 +2032,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn) {
|
|
|
1985
2032
|
}
|
|
1986
2033
|
function resolveCommandOnPath(command, deps = {}) {
|
|
1987
2034
|
const platform = deps.platform ?? process.platform;
|
|
1988
|
-
const env = deps.env ?? process.env;
|
|
2035
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
1989
2036
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
1990
2037
|
if (platform === "win32") {
|
|
1991
2038
|
return resolveCommandOnWindows(command, env, execFileSyncFn);
|
|
@@ -2010,7 +2057,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
2010
2057
|
return null;
|
|
2011
2058
|
}
|
|
2012
2059
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
2013
|
-
const env = deps.env ?? process.env;
|
|
2060
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
2014
2061
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
2015
2062
|
try {
|
|
2016
2063
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -3306,7 +3353,7 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
3306
3353
|
}
|
|
3307
3354
|
function runCursorModelsCommand() {
|
|
3308
3355
|
return spawnSync("cursor-agent", ["models"], {
|
|
3309
|
-
env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
3356
|
+
env: scrubDaemonChildEnv({ ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
3310
3357
|
encoding: "utf8",
|
|
3311
3358
|
timeout: 5e3
|
|
3312
3359
|
});
|
|
@@ -3353,7 +3400,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
3353
3400
|
}
|
|
3354
3401
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
3355
3402
|
const existsSyncFn = deps.existsSyncFn ?? existsSync6;
|
|
3356
|
-
const env = deps.env ?? process.env;
|
|
3403
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
3357
3404
|
const winPath = path8.win32;
|
|
3358
3405
|
let geminiEntry = null;
|
|
3359
3406
|
try {
|
|
@@ -3527,13 +3574,16 @@ var GeminiDriver = class {
|
|
|
3527
3574
|
// src/drivers/kimi.ts
|
|
3528
3575
|
import { randomUUID } from "crypto";
|
|
3529
3576
|
import { spawn as spawn6 } from "child_process";
|
|
3530
|
-
import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
3577
|
+
import { chmodSync, existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
3531
3578
|
import os4 from "os";
|
|
3532
3579
|
import path9 from "path";
|
|
3533
3580
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
3534
3581
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
3535
3582
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
3536
3583
|
var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
|
|
3584
|
+
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
3585
|
+
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
3586
|
+
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
3537
3587
|
function parseToolArguments(raw) {
|
|
3538
3588
|
if (typeof raw !== "string") return raw;
|
|
3539
3589
|
try {
|
|
@@ -3542,6 +3592,73 @@ function parseToolArguments(raw) {
|
|
|
3542
3592
|
return raw;
|
|
3543
3593
|
}
|
|
3544
3594
|
}
|
|
3595
|
+
function readKimiConfigSource(home = os4.homedir(), env = process.env) {
|
|
3596
|
+
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
3597
|
+
if (inlineConfig && inlineConfig.trim()) {
|
|
3598
|
+
return {
|
|
3599
|
+
raw: inlineConfig,
|
|
3600
|
+
explicitPath: null,
|
|
3601
|
+
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
3602
|
+
};
|
|
3603
|
+
}
|
|
3604
|
+
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
3605
|
+
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
|
|
3606
|
+
try {
|
|
3607
|
+
return {
|
|
3608
|
+
raw: readFileSync3(configPath, "utf8"),
|
|
3609
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
3610
|
+
sourcePath: configPath
|
|
3611
|
+
};
|
|
3612
|
+
} catch {
|
|
3613
|
+
return {
|
|
3614
|
+
raw: null,
|
|
3615
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
3616
|
+
sourcePath: configPath
|
|
3617
|
+
};
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
function buildKimiSpawnEnv(env = process.env) {
|
|
3621
|
+
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
3622
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
3623
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
3624
|
+
return scrubDaemonChildEnv(spawnEnv);
|
|
3625
|
+
}
|
|
3626
|
+
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
3627
|
+
return {
|
|
3628
|
+
...process.env,
|
|
3629
|
+
...ctx.config.envVars || {},
|
|
3630
|
+
...overrideEnv || {}
|
|
3631
|
+
};
|
|
3632
|
+
}
|
|
3633
|
+
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
3634
|
+
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
3635
|
+
const source = readKimiConfigSource(opts.home ?? os4.homedir(), env);
|
|
3636
|
+
const args = [];
|
|
3637
|
+
let configFilePath = null;
|
|
3638
|
+
let configContent = null;
|
|
3639
|
+
if (source.explicitPath) {
|
|
3640
|
+
configFilePath = source.explicitPath;
|
|
3641
|
+
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
3642
|
+
configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
3643
|
+
configContent = source.raw;
|
|
3644
|
+
if (opts.writeGeneratedConfig !== false) {
|
|
3645
|
+
writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
3646
|
+
chmodSync(configFilePath, 384);
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
if (configFilePath) {
|
|
3650
|
+
args.push("--config-file", configFilePath);
|
|
3651
|
+
}
|
|
3652
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
3653
|
+
args.push("--model", ctx.config.model);
|
|
3654
|
+
}
|
|
3655
|
+
return {
|
|
3656
|
+
args,
|
|
3657
|
+
env: buildKimiSpawnEnv(env),
|
|
3658
|
+
configFilePath,
|
|
3659
|
+
configContent
|
|
3660
|
+
};
|
|
3661
|
+
}
|
|
3545
3662
|
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
3546
3663
|
return {
|
|
3547
3664
|
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
@@ -3565,7 +3682,25 @@ var KimiDriver = class {
|
|
|
3565
3682
|
};
|
|
3566
3683
|
model = {
|
|
3567
3684
|
detectedModelsVerifiedAs: "launchable",
|
|
3568
|
-
toLaunchSpec: (modelId) =>
|
|
3685
|
+
toLaunchSpec: (modelId, ctx, opts) => {
|
|
3686
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
3687
|
+
const launchCtx = {
|
|
3688
|
+
...ctx,
|
|
3689
|
+
config: {
|
|
3690
|
+
...ctx.config,
|
|
3691
|
+
model: modelId
|
|
3692
|
+
}
|
|
3693
|
+
};
|
|
3694
|
+
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
3695
|
+
home: opts?.home,
|
|
3696
|
+
writeGeneratedConfig: false
|
|
3697
|
+
});
|
|
3698
|
+
return {
|
|
3699
|
+
args: launch.args,
|
|
3700
|
+
env: launch.env,
|
|
3701
|
+
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
3702
|
+
};
|
|
3703
|
+
}
|
|
3569
3704
|
};
|
|
3570
3705
|
supportsStdinNotification = true;
|
|
3571
3706
|
mcpToolPrefix = "";
|
|
@@ -3619,6 +3754,7 @@ var KimiDriver = class {
|
|
|
3619
3754
|
}
|
|
3620
3755
|
}
|
|
3621
3756
|
}), "utf8");
|
|
3757
|
+
const launch = buildKimiLaunchOptions(ctx);
|
|
3622
3758
|
const args = [
|
|
3623
3759
|
"--wire",
|
|
3624
3760
|
"--yolo",
|
|
@@ -3627,14 +3763,15 @@ var KimiDriver = class {
|
|
|
3627
3763
|
"--mcp-config-file",
|
|
3628
3764
|
mcpConfigPath,
|
|
3629
3765
|
"--session",
|
|
3630
|
-
this.sessionId
|
|
3766
|
+
this.sessionId,
|
|
3767
|
+
...launch.args
|
|
3631
3768
|
];
|
|
3632
3769
|
if (ctx.config.model && ctx.config.model !== "default") {
|
|
3633
3770
|
args.push("--model", ctx.config.model);
|
|
3634
3771
|
}
|
|
3635
3772
|
const spawnEnv = prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
|
|
3636
|
-
const
|
|
3637
|
-
const proc = spawn6(
|
|
3773
|
+
const spawnTarget = resolveKimiSpawn(args);
|
|
3774
|
+
const proc = spawn6(spawnTarget.command, spawnTarget.args, {
|
|
3638
3775
|
cwd: ctx.workingDirectory,
|
|
3639
3776
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3640
3777
|
env: spawnEnv,
|
|
@@ -3642,7 +3779,7 @@ var KimiDriver = class {
|
|
|
3642
3779
|
// and has an 8191-character command-line limit. Kimi's official
|
|
3643
3780
|
// installer/uv entrypoint is an executable, so launch it directly and
|
|
3644
3781
|
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
3645
|
-
shell:
|
|
3782
|
+
shell: spawnTarget.shell
|
|
3646
3783
|
});
|
|
3647
3784
|
proc.stdin?.write(JSON.stringify({
|
|
3648
3785
|
jsonrpc: "2.0",
|
|
@@ -3758,14 +3895,9 @@ var KimiDriver = class {
|
|
|
3758
3895
|
return detectKimiModels();
|
|
3759
3896
|
}
|
|
3760
3897
|
};
|
|
3761
|
-
function detectKimiModels(home = os4.homedir()) {
|
|
3762
|
-
const
|
|
3763
|
-
|
|
3764
|
-
try {
|
|
3765
|
-
raw = readFileSync3(configPath, "utf8");
|
|
3766
|
-
} catch {
|
|
3767
|
-
return null;
|
|
3768
|
-
}
|
|
3898
|
+
function detectKimiModels(home = os4.homedir(), opts = {}) {
|
|
3899
|
+
const raw = readKimiConfigSource(home, opts.env).raw;
|
|
3900
|
+
if (raw === null) return null;
|
|
3769
3901
|
const models = [];
|
|
3770
3902
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
3771
3903
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -3786,7 +3918,7 @@ function detectKimiModels(home = os4.homedir()) {
|
|
|
3786
3918
|
|
|
3787
3919
|
// src/drivers/opencode.ts
|
|
3788
3920
|
import { spawn as spawn7, spawnSync as spawnSync2 } from "child_process";
|
|
3789
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
3921
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
|
|
3790
3922
|
import os5 from "os";
|
|
3791
3923
|
import path10 from "path";
|
|
3792
3924
|
var CHAT_MCP_SERVER_NAME = "chat";
|
|
@@ -4024,11 +4156,14 @@ function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeMode
|
|
|
4024
4156
|
if (commandResult.error || commandResult.status !== 0) return null;
|
|
4025
4157
|
return parseOpenCodeModelsOutput(commandResult.stdout);
|
|
4026
4158
|
}
|
|
4027
|
-
function runOpenCodeModelsCommand(home) {
|
|
4028
|
-
const
|
|
4029
|
-
|
|
4159
|
+
function runOpenCodeModelsCommand(home, deps = {}) {
|
|
4160
|
+
const platform = deps.platform ?? process.platform;
|
|
4161
|
+
const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
|
|
4162
|
+
const result = spawnSyncFn("opencode", ["models"], {
|
|
4163
|
+
env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
4030
4164
|
encoding: "utf8",
|
|
4031
|
-
timeout: 5e3
|
|
4165
|
+
timeout: 5e3,
|
|
4166
|
+
shell: platform === "win32"
|
|
4032
4167
|
});
|
|
4033
4168
|
return {
|
|
4034
4169
|
status: result.status,
|
|
@@ -4036,6 +4171,102 @@ function runOpenCodeModelsCommand(home) {
|
|
|
4036
4171
|
error: result.error
|
|
4037
4172
|
};
|
|
4038
4173
|
}
|
|
4174
|
+
function isWindowsCommandShim(commandPath) {
|
|
4175
|
+
const ext = path10.win32.extname(commandPath).toLowerCase();
|
|
4176
|
+
return ext === ".cmd" || ext === ".bat";
|
|
4177
|
+
}
|
|
4178
|
+
function opencodePackageEntryCandidates(packageRoot) {
|
|
4179
|
+
const winPath = path10.win32;
|
|
4180
|
+
return [
|
|
4181
|
+
winPath.join(packageRoot, "bin", "opencode.exe"),
|
|
4182
|
+
winPath.join(packageRoot, "bin", "opencode.js"),
|
|
4183
|
+
winPath.join(packageRoot, "bin", "opencode.mjs"),
|
|
4184
|
+
winPath.join(packageRoot, "dist", "index.js")
|
|
4185
|
+
];
|
|
4186
|
+
}
|
|
4187
|
+
function openCodeSpecForEntry(entry, commandArgs) {
|
|
4188
|
+
if (path10.win32.extname(entry).toLowerCase() === ".exe") {
|
|
4189
|
+
return { command: entry, args: commandArgs, shell: false };
|
|
4190
|
+
}
|
|
4191
|
+
return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
|
|
4192
|
+
}
|
|
4193
|
+
function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
4194
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync8;
|
|
4195
|
+
const execFileSyncFn = deps.execFileSyncFn;
|
|
4196
|
+
const env = deps.env ?? process.env;
|
|
4197
|
+
const winPath = path10.win32;
|
|
4198
|
+
const candidates = [];
|
|
4199
|
+
if (execFileSyncFn) {
|
|
4200
|
+
try {
|
|
4201
|
+
const globalRoot = String(execFileSyncFn("npm", ["root", "-g"], {
|
|
4202
|
+
encoding: "utf8",
|
|
4203
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4204
|
+
env
|
|
4205
|
+
})).trim();
|
|
4206
|
+
if (globalRoot) {
|
|
4207
|
+
candidates.push(...opencodePackageEntryCandidates(winPath.join(globalRoot, "opencode-ai")));
|
|
4208
|
+
}
|
|
4209
|
+
} catch {
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
if (commandPath) {
|
|
4213
|
+
const commandDir = winPath.dirname(commandPath);
|
|
4214
|
+
candidates.push(...opencodePackageEntryCandidates(winPath.join(commandDir, "node_modules", "opencode-ai")));
|
|
4215
|
+
candidates.push(...extractWindowsShimTargets(commandPath, deps));
|
|
4216
|
+
}
|
|
4217
|
+
for (const candidate of candidates) {
|
|
4218
|
+
if (existsSyncFn(candidate)) return candidate;
|
|
4219
|
+
}
|
|
4220
|
+
return null;
|
|
4221
|
+
}
|
|
4222
|
+
function extractWindowsShimTargets(commandPath, deps = {}) {
|
|
4223
|
+
if (!isWindowsCommandShim(commandPath)) return [];
|
|
4224
|
+
const readFileSyncFn = deps.readFileSyncFn ?? readFileSync4;
|
|
4225
|
+
const commandDir = path10.win32.dirname(commandPath);
|
|
4226
|
+
let raw;
|
|
4227
|
+
try {
|
|
4228
|
+
raw = String(readFileSyncFn(commandPath, "utf8"));
|
|
4229
|
+
} catch {
|
|
4230
|
+
return [];
|
|
4231
|
+
}
|
|
4232
|
+
const candidates = [];
|
|
4233
|
+
const dp0Pattern = /%~dp0\\?([^"\r\n]*?opencode\.(?:exe|js|mjs|cjs))/gi;
|
|
4234
|
+
for (const match of raw.matchAll(dp0Pattern)) {
|
|
4235
|
+
const relative = match[1]?.replace(/^\\+/, "");
|
|
4236
|
+
if (relative) candidates.push(path10.win32.normalize(path10.win32.join(commandDir, relative)));
|
|
4237
|
+
}
|
|
4238
|
+
return candidates;
|
|
4239
|
+
}
|
|
4240
|
+
function resolveOpenCodeSpawn(commandArgs, deps = {}) {
|
|
4241
|
+
const platform = deps.platform ?? process.platform;
|
|
4242
|
+
if (platform !== "win32") {
|
|
4243
|
+
return {
|
|
4244
|
+
command: resolveCommandOnPath("opencode", deps) ?? "opencode",
|
|
4245
|
+
args: commandArgs,
|
|
4246
|
+
shell: false
|
|
4247
|
+
};
|
|
4248
|
+
}
|
|
4249
|
+
const command = resolveCommandOnPath("opencode", deps);
|
|
4250
|
+
if (command && path10.win32.extname(command).toLowerCase() === ".exe") {
|
|
4251
|
+
return { command, args: commandArgs, shell: false };
|
|
4252
|
+
}
|
|
4253
|
+
const packageEntry = resolveWindowsOpenCodePackageEntry(command, deps);
|
|
4254
|
+
if (packageEntry) return openCodeSpecForEntry(packageEntry, commandArgs);
|
|
4255
|
+
if (command && !isWindowsCommandShim(command)) {
|
|
4256
|
+
return { command, args: commandArgs, shell: false };
|
|
4257
|
+
}
|
|
4258
|
+
throw new Error(
|
|
4259
|
+
"Cannot resolve OpenCode CLI entry point on Windows without cmd.exe. Install the native OpenCode executable or install opencode-ai globally so Slock can launch node_modules/opencode-ai/bin/opencode.exe directly."
|
|
4260
|
+
);
|
|
4261
|
+
}
|
|
4262
|
+
function readOpenCodeVersion(deps = {}) {
|
|
4263
|
+
try {
|
|
4264
|
+
const launch = resolveOpenCodeSpawn([], deps);
|
|
4265
|
+
return readCommandVersion(launch.command, launch.args, deps);
|
|
4266
|
+
} catch {
|
|
4267
|
+
return null;
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4039
4270
|
function isSystemFirstMessageTask(message) {
|
|
4040
4271
|
return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX);
|
|
4041
4272
|
}
|
|
@@ -4078,7 +4309,7 @@ var OpenCodeDriver = class {
|
|
|
4078
4309
|
model: modelId
|
|
4079
4310
|
}
|
|
4080
4311
|
};
|
|
4081
|
-
const version =
|
|
4312
|
+
const version = readOpenCodeVersion();
|
|
4082
4313
|
const launch = buildOpenCodeLaunchOptions(launchCtx, opts?.home, version);
|
|
4083
4314
|
return {
|
|
4084
4315
|
args: launch.args,
|
|
@@ -4099,8 +4330,13 @@ var OpenCodeDriver = class {
|
|
|
4099
4330
|
sessionId = null;
|
|
4100
4331
|
sessionAnnounced = false;
|
|
4101
4332
|
probe() {
|
|
4102
|
-
|
|
4103
|
-
|
|
4333
|
+
let version;
|
|
4334
|
+
try {
|
|
4335
|
+
const launch = resolveOpenCodeSpawn([]);
|
|
4336
|
+
version = readCommandVersion(launch.command, launch.args);
|
|
4337
|
+
} catch {
|
|
4338
|
+
return { available: false };
|
|
4339
|
+
}
|
|
4104
4340
|
const unsupportedMessage = unsupportedOpenCodeVersionMessage(version);
|
|
4105
4341
|
if (unsupportedMessage) {
|
|
4106
4342
|
return {
|
|
@@ -4108,7 +4344,7 @@ var OpenCodeDriver = class {
|
|
|
4108
4344
|
version: `${version} (requires >= ${MIN_SUPPORTED_OPENCODE_VERSION})`
|
|
4109
4345
|
};
|
|
4110
4346
|
}
|
|
4111
|
-
return { available: true, version };
|
|
4347
|
+
return { available: true, version: version ?? void 0 };
|
|
4112
4348
|
}
|
|
4113
4349
|
async detectModels() {
|
|
4114
4350
|
return detectOpenCodeModels();
|
|
@@ -4116,17 +4352,18 @@ var OpenCodeDriver = class {
|
|
|
4116
4352
|
spawn(ctx) {
|
|
4117
4353
|
this.sessionId = ctx.config.sessionId || null;
|
|
4118
4354
|
this.sessionAnnounced = false;
|
|
4119
|
-
const version =
|
|
4355
|
+
const version = readOpenCodeVersion();
|
|
4120
4356
|
const unsupportedMessage = unsupportedOpenCodeVersionMessage(version);
|
|
4121
4357
|
if (unsupportedMessage) {
|
|
4122
4358
|
throw new Error(unsupportedMessage);
|
|
4123
4359
|
}
|
|
4124
4360
|
const launch = buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
|
|
4125
|
-
const
|
|
4361
|
+
const spawnSpec = resolveOpenCodeSpawn(launch.args);
|
|
4362
|
+
const proc = spawn7(spawnSpec.command, spawnSpec.args, {
|
|
4126
4363
|
cwd: ctx.workingDirectory,
|
|
4127
4364
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4128
4365
|
env: launch.env,
|
|
4129
|
-
shell:
|
|
4366
|
+
shell: spawnSpec.shell
|
|
4130
4367
|
});
|
|
4131
4368
|
proc.stdin?.end();
|
|
4132
4369
|
return { process: proc };
|
|
@@ -4184,6 +4421,297 @@ var OpenCodeDriver = class {
|
|
|
4184
4421
|
}
|
|
4185
4422
|
};
|
|
4186
4423
|
|
|
4424
|
+
// src/drivers/pi.ts
|
|
4425
|
+
import { spawn as spawn8 } from "child_process";
|
|
4426
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
4427
|
+
import path11 from "path";
|
|
4428
|
+
import { fileURLToPath } from "url";
|
|
4429
|
+
import { getAgentDir, VERSION as PI_SDK_VERSION } from "@earendil-works/pi-coding-agent";
|
|
4430
|
+
var CHAT_MCP_TOOL_PREFIX2 = "chat_";
|
|
4431
|
+
var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
|
|
4432
|
+
var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
|
|
4433
|
+
var MIN_SUPPORTED_PI_VERSION = "0.74.0";
|
|
4434
|
+
function parseSemver2(version) {
|
|
4435
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
4436
|
+
if (!match) return null;
|
|
4437
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
4438
|
+
}
|
|
4439
|
+
function isSupportedPiVersion(version) {
|
|
4440
|
+
if (!version) return true;
|
|
4441
|
+
const actual = parseSemver2(version);
|
|
4442
|
+
const minimum = parseSemver2(MIN_SUPPORTED_PI_VERSION);
|
|
4443
|
+
if (!actual || !minimum) return true;
|
|
4444
|
+
for (let i = 0; i < 3; i += 1) {
|
|
4445
|
+
if (actual[i] > minimum[i]) return true;
|
|
4446
|
+
if (actual[i] < minimum[i]) return false;
|
|
4447
|
+
}
|
|
4448
|
+
return true;
|
|
4449
|
+
}
|
|
4450
|
+
function unsupportedPiVersionMessage(version) {
|
|
4451
|
+
if (!version || isSupportedPiVersion(version)) return null;
|
|
4452
|
+
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.`;
|
|
4453
|
+
}
|
|
4454
|
+
function probePi(version = PI_SDK_VERSION) {
|
|
4455
|
+
const unsupportedMessage = unsupportedPiVersionMessage(version);
|
|
4456
|
+
if (unsupportedMessage) {
|
|
4457
|
+
return {
|
|
4458
|
+
available: false,
|
|
4459
|
+
version: `${version} (requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION})`
|
|
4460
|
+
};
|
|
4461
|
+
}
|
|
4462
|
+
return { available: true, version };
|
|
4463
|
+
}
|
|
4464
|
+
function resolvePiSdkRunnerPath(moduleUrl = import.meta.url) {
|
|
4465
|
+
const moduleDir = path11.dirname(fileURLToPath(moduleUrl));
|
|
4466
|
+
const sourceSibling = path11.join(moduleDir, "piSdkRunner.ts");
|
|
4467
|
+
if (existsSync9(sourceSibling)) return sourceSibling;
|
|
4468
|
+
const bundledEntry = path11.join(moduleDir, "drivers", "piSdkRunner.js");
|
|
4469
|
+
if (existsSync9(bundledEntry)) return bundledEntry;
|
|
4470
|
+
return path11.join(moduleDir, "piSdkRunner.js");
|
|
4471
|
+
}
|
|
4472
|
+
function buildPiSdkNodeArgs(runnerPath = resolvePiSdkRunnerPath()) {
|
|
4473
|
+
if (runnerPath.endsWith(".ts")) {
|
|
4474
|
+
return [...process.execArgv, runnerPath];
|
|
4475
|
+
}
|
|
4476
|
+
return [runnerPath];
|
|
4477
|
+
}
|
|
4478
|
+
function buildPiLaunchOptions(ctx, opts = {}) {
|
|
4479
|
+
const command = opts.command ?? process.execPath;
|
|
4480
|
+
const piDir = path11.join(ctx.workingDirectory, ".slock", "pi");
|
|
4481
|
+
const sessionDir = path11.join(piDir, "sessions");
|
|
4482
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
4483
|
+
const slock = prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
4484
|
+
const runnerPath = opts.runnerPath ?? resolvePiSdkRunnerPath();
|
|
4485
|
+
const agentDir = opts.agentDir ?? getAgentDir();
|
|
4486
|
+
const runnerConfigPath = path11.join(
|
|
4487
|
+
piDir,
|
|
4488
|
+
`sdk-run-${(ctx.launchId || "launch").replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`
|
|
4489
|
+
);
|
|
4490
|
+
const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
|
|
4491
|
+
const runnerConfig = {
|
|
4492
|
+
cwd: ctx.workingDirectory,
|
|
4493
|
+
agentDir,
|
|
4494
|
+
sessionDir,
|
|
4495
|
+
sessionId: ctx.config.sessionId || null,
|
|
4496
|
+
standingPrompt: ctx.standingPrompt,
|
|
4497
|
+
prompt: turnPrompt,
|
|
4498
|
+
model: ctx.config.model && ctx.config.model !== "default" ? ctx.config.model : null
|
|
4499
|
+
};
|
|
4500
|
+
writeFileSync7(runnerConfigPath, `${JSON.stringify(runnerConfig)}
|
|
4501
|
+
`, { encoding: "utf8", mode: 384 });
|
|
4502
|
+
const args = [
|
|
4503
|
+
...buildPiSdkNodeArgs(runnerPath),
|
|
4504
|
+
"--config",
|
|
4505
|
+
runnerConfigPath
|
|
4506
|
+
];
|
|
4507
|
+
return {
|
|
4508
|
+
command,
|
|
4509
|
+
args,
|
|
4510
|
+
env: slock.spawnEnv,
|
|
4511
|
+
sessionDir,
|
|
4512
|
+
agentDir,
|
|
4513
|
+
runnerConfigPath,
|
|
4514
|
+
sdkVersion: PI_SDK_VERSION
|
|
4515
|
+
};
|
|
4516
|
+
}
|
|
4517
|
+
function isSystemFirstMessageTask2(message) {
|
|
4518
|
+
return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
|
|
4519
|
+
}
|
|
4520
|
+
function buildPiSystemPrompt(config) {
|
|
4521
|
+
return buildCliTransportSystemPrompt(config, {
|
|
4522
|
+
toolPrefix: CHAT_MCP_TOOL_PREFIX2,
|
|
4523
|
+
extraCriticalRules: [
|
|
4524
|
+
"- 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."
|
|
4525
|
+
],
|
|
4526
|
+
postStartupNotes: [
|
|
4527
|
+
"**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."
|
|
4528
|
+
],
|
|
4529
|
+
includeStdinNotificationSection: false,
|
|
4530
|
+
messageNotificationStyle: "poll"
|
|
4531
|
+
});
|
|
4532
|
+
}
|
|
4533
|
+
function contentText(content) {
|
|
4534
|
+
if (!content) return "";
|
|
4535
|
+
const chunks = [];
|
|
4536
|
+
for (const item of content) {
|
|
4537
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
4538
|
+
chunks.push(item.text);
|
|
4539
|
+
}
|
|
4540
|
+
}
|
|
4541
|
+
return chunks.join("");
|
|
4542
|
+
}
|
|
4543
|
+
function apiKeyErrorMessage(line) {
|
|
4544
|
+
const trimmed = line.trim();
|
|
4545
|
+
if (!trimmed) return null;
|
|
4546
|
+
if (/no api key found/i.test(trimmed)) return trimmed;
|
|
4547
|
+
if (/api key.+required/i.test(trimmed)) return trimmed;
|
|
4548
|
+
if (/no models available/i.test(trimmed)) return trimmed;
|
|
4549
|
+
return null;
|
|
4550
|
+
}
|
|
4551
|
+
var PiDriver = class {
|
|
4552
|
+
id = "pi";
|
|
4553
|
+
lifecycle = {
|
|
4554
|
+
kind: "per_turn",
|
|
4555
|
+
start: "defer_until_concrete_message",
|
|
4556
|
+
exit: "terminate_on_turn_end",
|
|
4557
|
+
inFlightWake: "coalesce_into_pending"
|
|
4558
|
+
};
|
|
4559
|
+
communication = {
|
|
4560
|
+
chat: "slock_cli",
|
|
4561
|
+
runtimeControl: "none"
|
|
4562
|
+
};
|
|
4563
|
+
session = {
|
|
4564
|
+
recovery: "resume_or_fresh"
|
|
4565
|
+
};
|
|
4566
|
+
model = {
|
|
4567
|
+
detectedModelsVerifiedAs: "launchable",
|
|
4568
|
+
toLaunchSpec: (modelId, ctx) => {
|
|
4569
|
+
if (!ctx) return modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] };
|
|
4570
|
+
const launchCtx = {
|
|
4571
|
+
...ctx,
|
|
4572
|
+
config: {
|
|
4573
|
+
...ctx.config,
|
|
4574
|
+
model: modelId
|
|
4575
|
+
}
|
|
4576
|
+
};
|
|
4577
|
+
const launch = buildPiLaunchOptions(launchCtx);
|
|
4578
|
+
return {
|
|
4579
|
+
args: launch.args,
|
|
4580
|
+
env: launch.env,
|
|
4581
|
+
configFiles: [launch.runnerConfigPath],
|
|
4582
|
+
params: {
|
|
4583
|
+
agentDir: launch.agentDir,
|
|
4584
|
+
sessionDir: launch.sessionDir,
|
|
4585
|
+
sdkVersion: launch.sdkVersion,
|
|
4586
|
+
resources: "extensions/skills/prompt-templates/themes/context-files disabled by Slock policy"
|
|
4587
|
+
}
|
|
4588
|
+
};
|
|
4589
|
+
}
|
|
4590
|
+
};
|
|
4591
|
+
supportsStdinNotification = false;
|
|
4592
|
+
mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
|
|
4593
|
+
busyDeliveryMode = "none";
|
|
4594
|
+
terminateProcessOnTurnEnd = true;
|
|
4595
|
+
deferSpawnUntilMessage = true;
|
|
4596
|
+
usesSlockCliForCommunication = true;
|
|
4597
|
+
sessionId = null;
|
|
4598
|
+
sessionAnnounced = false;
|
|
4599
|
+
apiKeyErrorAnnounced = false;
|
|
4600
|
+
turnEnded = false;
|
|
4601
|
+
assistantTextByMessageId = /* @__PURE__ */ new Map();
|
|
4602
|
+
shouldDeferWakeMessage(message) {
|
|
4603
|
+
return isSystemFirstMessageTask2(message);
|
|
4604
|
+
}
|
|
4605
|
+
probe() {
|
|
4606
|
+
return probePi();
|
|
4607
|
+
}
|
|
4608
|
+
async detectModels() {
|
|
4609
|
+
return null;
|
|
4610
|
+
}
|
|
4611
|
+
spawn(ctx) {
|
|
4612
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
4613
|
+
this.sessionAnnounced = false;
|
|
4614
|
+
this.apiKeyErrorAnnounced = false;
|
|
4615
|
+
this.turnEnded = false;
|
|
4616
|
+
this.assistantTextByMessageId.clear();
|
|
4617
|
+
const unsupportedMessage = unsupportedPiVersionMessage(PI_SDK_VERSION);
|
|
4618
|
+
if (unsupportedMessage) throw new Error(unsupportedMessage);
|
|
4619
|
+
const launch = buildPiLaunchOptions(ctx);
|
|
4620
|
+
const proc = spawn8(launch.command, launch.args, {
|
|
4621
|
+
cwd: ctx.workingDirectory,
|
|
4622
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4623
|
+
env: launch.env,
|
|
4624
|
+
shell: false
|
|
4625
|
+
});
|
|
4626
|
+
proc.stdin?.end();
|
|
4627
|
+
return { process: proc };
|
|
4628
|
+
}
|
|
4629
|
+
parseLine(line) {
|
|
4630
|
+
let event;
|
|
4631
|
+
try {
|
|
4632
|
+
event = JSON.parse(line);
|
|
4633
|
+
} catch {
|
|
4634
|
+
if (this.apiKeyErrorAnnounced) return [];
|
|
4635
|
+
const message = apiKeyErrorMessage(line);
|
|
4636
|
+
if (!message) return [];
|
|
4637
|
+
this.apiKeyErrorAnnounced = true;
|
|
4638
|
+
this.turnEnded = true;
|
|
4639
|
+
return [
|
|
4640
|
+
{ kind: "error", message },
|
|
4641
|
+
{ kind: "turn_end", sessionId: this.sessionId || void 0 }
|
|
4642
|
+
];
|
|
4643
|
+
}
|
|
4644
|
+
const events = [];
|
|
4645
|
+
if (event.type === "session" && event.id) {
|
|
4646
|
+
this.sessionId = event.id;
|
|
4647
|
+
}
|
|
4648
|
+
if (!this.sessionAnnounced && this.sessionId) {
|
|
4649
|
+
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
4650
|
+
this.sessionAnnounced = true;
|
|
4651
|
+
}
|
|
4652
|
+
switch (event.type) {
|
|
4653
|
+
case "agent_start":
|
|
4654
|
+
case "turn_start":
|
|
4655
|
+
events.push({ kind: "thinking", text: "" });
|
|
4656
|
+
break;
|
|
4657
|
+
case "message_update":
|
|
4658
|
+
case "message_end":
|
|
4659
|
+
if (event.message?.role === "assistant") {
|
|
4660
|
+
const key = event.message.id || "current";
|
|
4661
|
+
const currentText = contentText(event.message.content);
|
|
4662
|
+
const previousText = this.assistantTextByMessageId.get(key) ?? "";
|
|
4663
|
+
if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
|
|
4664
|
+
events.push({ kind: "text", text: currentText.slice(previousText.length) });
|
|
4665
|
+
} else if (currentText && currentText !== previousText) {
|
|
4666
|
+
events.push({ kind: "text", text: currentText });
|
|
4667
|
+
}
|
|
4668
|
+
this.assistantTextByMessageId.set(key, currentText);
|
|
4669
|
+
if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
|
|
4670
|
+
events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
break;
|
|
4674
|
+
case "tool_execution_start":
|
|
4675
|
+
events.push({
|
|
4676
|
+
kind: "tool_call",
|
|
4677
|
+
name: event.toolName || "unknown_tool",
|
|
4678
|
+
input: event.args
|
|
4679
|
+
});
|
|
4680
|
+
break;
|
|
4681
|
+
case "tool_execution_end":
|
|
4682
|
+
events.push({
|
|
4683
|
+
kind: "tool_output",
|
|
4684
|
+
name: event.toolName || "unknown_tool"
|
|
4685
|
+
});
|
|
4686
|
+
if (event.isError) {
|
|
4687
|
+
events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
|
|
4688
|
+
}
|
|
4689
|
+
break;
|
|
4690
|
+
case "compaction_start":
|
|
4691
|
+
events.push({ kind: "compaction_started" });
|
|
4692
|
+
break;
|
|
4693
|
+
case "compaction_end":
|
|
4694
|
+
events.push({ kind: "compaction_finished" });
|
|
4695
|
+
if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
|
|
4696
|
+
break;
|
|
4697
|
+
case "turn_end":
|
|
4698
|
+
case "agent_end":
|
|
4699
|
+
if (!this.turnEnded) {
|
|
4700
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
4701
|
+
this.turnEnded = true;
|
|
4702
|
+
}
|
|
4703
|
+
break;
|
|
4704
|
+
}
|
|
4705
|
+
return events;
|
|
4706
|
+
}
|
|
4707
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
4708
|
+
return null;
|
|
4709
|
+
}
|
|
4710
|
+
buildSystemPrompt(config, _agentId) {
|
|
4711
|
+
return buildPiSystemPrompt(config);
|
|
4712
|
+
}
|
|
4713
|
+
};
|
|
4714
|
+
|
|
4187
4715
|
// src/drivers/index.ts
|
|
4188
4716
|
var driverFactories = {
|
|
4189
4717
|
claude: () => new ClaudeDriver(),
|
|
@@ -4192,7 +4720,8 @@ var driverFactories = {
|
|
|
4192
4720
|
cursor: () => new CursorDriver(),
|
|
4193
4721
|
gemini: () => new GeminiDriver(),
|
|
4194
4722
|
kimi: () => new KimiDriver(),
|
|
4195
|
-
opencode: () => new OpenCodeDriver()
|
|
4723
|
+
opencode: () => new OpenCodeDriver(),
|
|
4724
|
+
pi: () => new PiDriver()
|
|
4196
4725
|
};
|
|
4197
4726
|
function getDriver(runtimeId) {
|
|
4198
4727
|
const createDriver = driverFactories[runtimeId];
|
|
@@ -4205,7 +4734,7 @@ function getDriver(runtimeId) {
|
|
|
4205
4734
|
|
|
4206
4735
|
// src/workspaces.ts
|
|
4207
4736
|
import { readdir, rm, stat } from "fs/promises";
|
|
4208
|
-
import
|
|
4737
|
+
import path12 from "path";
|
|
4209
4738
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
4210
4739
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
4211
4740
|
}
|
|
@@ -4213,7 +4742,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
4213
4742
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
4214
4743
|
return null;
|
|
4215
4744
|
}
|
|
4216
|
-
return
|
|
4745
|
+
return path12.join(dataDir, directoryName);
|
|
4217
4746
|
}
|
|
4218
4747
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
4219
4748
|
return {
|
|
@@ -4262,7 +4791,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
4262
4791
|
return summary;
|
|
4263
4792
|
}
|
|
4264
4793
|
const childSummaries = await Promise.all(
|
|
4265
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
4794
|
+
entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
|
|
4266
4795
|
);
|
|
4267
4796
|
for (const childSummary of childSummaries) {
|
|
4268
4797
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -4281,7 +4810,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
4281
4810
|
if (!entry.isDirectory()) {
|
|
4282
4811
|
return null;
|
|
4283
4812
|
}
|
|
4284
|
-
const dirPath =
|
|
4813
|
+
const dirPath = path12.join(dataDir, entry.name);
|
|
4285
4814
|
try {
|
|
4286
4815
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
4287
4816
|
return {
|
|
@@ -4364,6 +4893,7 @@ function classifyRuntimeError(message, httpStatus) {
|
|
|
4364
4893
|
return "ProviderApiError";
|
|
4365
4894
|
}
|
|
4366
4895
|
if (/\btimeout|timed out\b/i.test(message)) return "TimeoutError";
|
|
4896
|
+
if (/stream closed before response\.completed|error decoding response body/i.test(message)) return "ProviderStreamError";
|
|
4367
4897
|
if (/\brate.?limit|too many requests\b/i.test(message)) return "RateLimitError";
|
|
4368
4898
|
if (/\bnot found\b/i.test(message)) return "NotFoundError";
|
|
4369
4899
|
return "RuntimeError";
|
|
@@ -4538,12 +5068,12 @@ function findSessionJsonl(root, predicate) {
|
|
|
4538
5068
|
for (const entry of entries) {
|
|
4539
5069
|
if (++visited > maxEntries) return null;
|
|
4540
5070
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
4541
|
-
return
|
|
5071
|
+
return path13.join(dir, entry.name);
|
|
4542
5072
|
}
|
|
4543
5073
|
for (const entry of entries) {
|
|
4544
5074
|
if (++visited > maxEntries) return null;
|
|
4545
5075
|
if (!entry.isDirectory()) continue;
|
|
4546
|
-
const found = visit(
|
|
5076
|
+
const found = visit(path13.join(dir, entry.name), depth - 1);
|
|
4547
5077
|
if (found) return found;
|
|
4548
5078
|
}
|
|
4549
5079
|
return null;
|
|
@@ -4556,10 +5086,10 @@ function safeSessionFilename(value) {
|
|
|
4556
5086
|
}
|
|
4557
5087
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
4558
5088
|
try {
|
|
4559
|
-
const dir =
|
|
4560
|
-
|
|
4561
|
-
const filePath =
|
|
4562
|
-
|
|
5089
|
+
const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
|
|
5090
|
+
mkdirSync5(dir, { recursive: true });
|
|
5091
|
+
const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
5092
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
4563
5093
|
type: "runtime_session_handoff",
|
|
4564
5094
|
runtime,
|
|
4565
5095
|
sessionId,
|
|
@@ -4578,7 +5108,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
4578
5108
|
}
|
|
4579
5109
|
}
|
|
4580
5110
|
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
|
|
4581
|
-
const directPath =
|
|
5111
|
+
const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
|
|
4582
5112
|
if (directPath) {
|
|
4583
5113
|
try {
|
|
4584
5114
|
if (statSync2(directPath).isFile()) {
|
|
@@ -4587,7 +5117,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), f
|
|
|
4587
5117
|
} catch {
|
|
4588
5118
|
}
|
|
4589
5119
|
}
|
|
4590
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
5120
|
+
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;
|
|
4591
5121
|
if (!resolvedPath && fallbackDir) {
|
|
4592
5122
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
4593
5123
|
if (fallback) return fallback;
|
|
@@ -5238,12 +5768,21 @@ function classifyTerminalFailure(ap) {
|
|
|
5238
5768
|
].filter((value) => !!value);
|
|
5239
5769
|
for (const text of candidates) {
|
|
5240
5770
|
const lower = text.toLowerCase();
|
|
5241
|
-
if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found")) {
|
|
5771
|
+
if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found") || isProviderStreamFailureText(text)) {
|
|
5242
5772
|
return text;
|
|
5243
5773
|
}
|
|
5244
5774
|
}
|
|
5245
5775
|
return null;
|
|
5246
5776
|
}
|
|
5777
|
+
function isProviderStreamFailureText(text) {
|
|
5778
|
+
return /stream closed before response\.completed|error decoding response body/i.test(text);
|
|
5779
|
+
}
|
|
5780
|
+
function isCodexProviderReconnectLog(text) {
|
|
5781
|
+
return /Reconnecting\.\.\.\s*\d+\s*\/\s*\d+/i.test(text);
|
|
5782
|
+
}
|
|
5783
|
+
function isCodexBenignTransportLog(text) {
|
|
5784
|
+
return /Falling back from WebSockets/i.test(text);
|
|
5785
|
+
}
|
|
5247
5786
|
function hasDirectStdinRecoveryEvidence(ap) {
|
|
5248
5787
|
const candidates = [
|
|
5249
5788
|
ap.lastRuntimeError,
|
|
@@ -5887,26 +6426,26 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
5887
6426
|
this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
|
|
5888
6427
|
try {
|
|
5889
6428
|
const driver = this.driverResolver(config.runtime || "claude");
|
|
5890
|
-
const agentDataDir =
|
|
6429
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
5891
6430
|
await mkdir(agentDataDir, { recursive: true });
|
|
5892
6431
|
const runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
5893
|
-
const memoryMdPath =
|
|
6432
|
+
const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
|
|
5894
6433
|
try {
|
|
5895
6434
|
await access(memoryMdPath);
|
|
5896
6435
|
} catch {
|
|
5897
6436
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
5898
6437
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
5899
6438
|
}
|
|
5900
|
-
const notesDir =
|
|
6439
|
+
const notesDir = path13.join(agentDataDir, "notes");
|
|
5901
6440
|
await mkdir(notesDir, { recursive: true });
|
|
5902
6441
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
5903
6442
|
const seedFiles = buildOnboardingSeedFiles();
|
|
5904
6443
|
for (const { relativePath, content } of seedFiles) {
|
|
5905
|
-
const fullPath =
|
|
6444
|
+
const fullPath = path13.join(agentDataDir, relativePath);
|
|
5906
6445
|
try {
|
|
5907
6446
|
await access(fullPath);
|
|
5908
6447
|
} catch {
|
|
5909
|
-
await mkdir(
|
|
6448
|
+
await mkdir(path13.dirname(fullPath), { recursive: true });
|
|
5910
6449
|
await writeFile(fullPath, content);
|
|
5911
6450
|
}
|
|
5912
6451
|
}
|
|
@@ -6095,8 +6634,24 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6095
6634
|
proc.stderr?.on("data", (chunk) => {
|
|
6096
6635
|
const text = chunk.toString().trim();
|
|
6097
6636
|
if (!text) return;
|
|
6098
|
-
if (/Reconnecting\.\.\.|Falling back from WebSockets/i.test(text)) return;
|
|
6099
6637
|
const current = this.agents.get(agentId);
|
|
6638
|
+
if (driver.id === "codex" && isCodexProviderReconnectLog(text)) {
|
|
6639
|
+
if (current) {
|
|
6640
|
+
current.recentStderr = pushRecentStderr(current.recentStderr, text);
|
|
6641
|
+
}
|
|
6642
|
+
this.recordDaemonTrace("daemon.agent.provider_reconnect", {
|
|
6643
|
+
agentId,
|
|
6644
|
+
launchId: current?.launchId || void 0,
|
|
6645
|
+
runtime: config.runtime,
|
|
6646
|
+
model: config.model
|
|
6647
|
+
});
|
|
6648
|
+
this.broadcastActivity(agentId, "working", "Codex reconnecting to provider\u2026", [
|
|
6649
|
+
{ kind: "text", text }
|
|
6650
|
+
]);
|
|
6651
|
+
logger.info(`[Agent ${agentId} stderr]: ${text}`);
|
|
6652
|
+
return;
|
|
6653
|
+
}
|
|
6654
|
+
if (driver.id === "codex" && isCodexBenignTransportLog(text)) return;
|
|
6100
6655
|
if (current) {
|
|
6101
6656
|
current.recentStderr = pushRecentStderr(current.recentStderr, text);
|
|
6102
6657
|
}
|
|
@@ -6240,10 +6795,20 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6240
6795
|
}
|
|
6241
6796
|
this.broadcastActivity(agentId, "online", "Process idle");
|
|
6242
6797
|
} else {
|
|
6243
|
-
this.idleAgentConfigs.delete(agentId);
|
|
6244
6798
|
const reason = formatCrashReason(finalCode, finalSignal, ap);
|
|
6245
|
-
|
|
6246
|
-
|
|
6799
|
+
if (terminalFailureDetail && isProviderStreamFailureText(terminalFailureDetail)) {
|
|
6800
|
+
this.idleAgentConfigs.set(agentId, {
|
|
6801
|
+
config: { ...ap.config, sessionId: ap.sessionId },
|
|
6802
|
+
sessionId: ap.sessionId,
|
|
6803
|
+
launchId: ap.launchId
|
|
6804
|
+
});
|
|
6805
|
+
logger.warn(`[Agent ${agentId}] Recoverable provider stream failure (${reason}) \u2014 keeping agent wakeable`);
|
|
6806
|
+
this.sendAgentStatus(agentId, "active", ap.launchId);
|
|
6807
|
+
} else {
|
|
6808
|
+
this.idleAgentConfigs.delete(agentId);
|
|
6809
|
+
logger.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
|
|
6810
|
+
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
6811
|
+
}
|
|
6247
6812
|
if (terminalFailureDetail) {
|
|
6248
6813
|
this.broadcastActivity(
|
|
6249
6814
|
agentId,
|
|
@@ -6757,7 +7322,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6757
7322
|
return true;
|
|
6758
7323
|
}
|
|
6759
7324
|
async resetWorkspace(agentId) {
|
|
6760
|
-
const agentDataDir =
|
|
7325
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
6761
7326
|
try {
|
|
6762
7327
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
6763
7328
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -6794,7 +7359,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6794
7359
|
return result;
|
|
6795
7360
|
}
|
|
6796
7361
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
6797
|
-
const workspacePath =
|
|
7362
|
+
const workspacePath = path13.join(this.dataDir, agentId);
|
|
6798
7363
|
return {
|
|
6799
7364
|
agentId,
|
|
6800
7365
|
launchId,
|
|
@@ -7051,7 +7616,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7051
7616
|
}
|
|
7052
7617
|
// Workspace file browsing
|
|
7053
7618
|
async getFileTree(agentId, dirPath) {
|
|
7054
|
-
const agentDir =
|
|
7619
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
7055
7620
|
try {
|
|
7056
7621
|
await stat2(agentDir);
|
|
7057
7622
|
} catch {
|
|
@@ -7059,8 +7624,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7059
7624
|
}
|
|
7060
7625
|
let targetDir = agentDir;
|
|
7061
7626
|
if (dirPath) {
|
|
7062
|
-
const resolved =
|
|
7063
|
-
if (!resolved.startsWith(agentDir +
|
|
7627
|
+
const resolved = path13.resolve(agentDir, dirPath);
|
|
7628
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
7064
7629
|
return [];
|
|
7065
7630
|
}
|
|
7066
7631
|
targetDir = resolved;
|
|
@@ -7068,14 +7633,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7068
7633
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
7069
7634
|
}
|
|
7070
7635
|
async readFile(agentId, filePath) {
|
|
7071
|
-
const agentDir =
|
|
7072
|
-
const resolved =
|
|
7073
|
-
if (!resolved.startsWith(agentDir +
|
|
7636
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
7637
|
+
const resolved = path13.resolve(agentDir, filePath);
|
|
7638
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
7074
7639
|
throw new Error("Access denied");
|
|
7075
7640
|
}
|
|
7076
7641
|
const info = await stat2(resolved);
|
|
7077
7642
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
7078
|
-
const ext =
|
|
7643
|
+
const ext = path13.extname(resolved).toLowerCase();
|
|
7079
7644
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
7080
7645
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
7081
7646
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -7110,13 +7675,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7110
7675
|
const agent = this.agents.get(agentId);
|
|
7111
7676
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
7112
7677
|
const home = os6.homedir();
|
|
7113
|
-
const workspaceDir =
|
|
7678
|
+
const workspaceDir = path13.join(this.dataDir, agentId);
|
|
7114
7679
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
7115
7680
|
const globalResults = await Promise.all(
|
|
7116
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
7681
|
+
paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
|
|
7117
7682
|
);
|
|
7118
7683
|
const workspaceResults = await Promise.all(
|
|
7119
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
7684
|
+
paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
|
|
7120
7685
|
);
|
|
7121
7686
|
const dedup = (skills) => {
|
|
7122
7687
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7145,7 +7710,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7145
7710
|
const skills = [];
|
|
7146
7711
|
for (const entry of entries) {
|
|
7147
7712
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
7148
|
-
const skillMd =
|
|
7713
|
+
const skillMd = path13.join(dir, entry.name, "SKILL.md");
|
|
7149
7714
|
try {
|
|
7150
7715
|
const content = await readFile(skillMd, "utf-8");
|
|
7151
7716
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -7156,7 +7721,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7156
7721
|
} else if (entry.name.endsWith(".md")) {
|
|
7157
7722
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
7158
7723
|
try {
|
|
7159
|
-
const content = await readFile(
|
|
7724
|
+
const content = await readFile(path13.join(dir, entry.name), "utf-8");
|
|
7160
7725
|
const skill = this.parseSkillMd(cmdName, content);
|
|
7161
7726
|
skill.sourcePath = dir;
|
|
7162
7727
|
skills.push(skill);
|
|
@@ -8122,8 +8687,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
8122
8687
|
const nodes = [];
|
|
8123
8688
|
for (const entry of entries) {
|
|
8124
8689
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
8125
|
-
const fullPath =
|
|
8126
|
-
const relativePath =
|
|
8690
|
+
const fullPath = path13.join(dir, entry.name);
|
|
8691
|
+
const relativePath = path13.relative(rootDir, fullPath);
|
|
8127
8692
|
let info;
|
|
8128
8693
|
try {
|
|
8129
8694
|
info = await stat2(fullPath);
|
|
@@ -8428,9 +8993,9 @@ var ReminderCache = class {
|
|
|
8428
8993
|
|
|
8429
8994
|
// src/machineLock.ts
|
|
8430
8995
|
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
8431
|
-
import { mkdirSync as
|
|
8996
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
8432
8997
|
import os7 from "os";
|
|
8433
|
-
import
|
|
8998
|
+
import path14 from "path";
|
|
8434
8999
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
8435
9000
|
var DaemonMachineLockConflictError = class extends Error {
|
|
8436
9001
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -8452,7 +9017,7 @@ function resolveDefaultMachineStateRoot() {
|
|
|
8452
9017
|
return resolveSlockHomePath("machines");
|
|
8453
9018
|
}
|
|
8454
9019
|
function ownerPath(lockDir) {
|
|
8455
|
-
return
|
|
9020
|
+
return path14.join(lockDir, "owner.json");
|
|
8456
9021
|
}
|
|
8457
9022
|
function readOwner(lockDir) {
|
|
8458
9023
|
try {
|
|
@@ -8482,13 +9047,13 @@ function acquireDaemonMachineLock(options) {
|
|
|
8482
9047
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
8483
9048
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
8484
9049
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
8485
|
-
const machineDir =
|
|
8486
|
-
const lockDir =
|
|
9050
|
+
const machineDir = path14.join(rootDir, lockId);
|
|
9051
|
+
const lockDir = path14.join(machineDir, "daemon.lock");
|
|
8487
9052
|
const token = randomUUID2();
|
|
8488
|
-
|
|
9053
|
+
mkdirSync6(machineDir, { recursive: true });
|
|
8489
9054
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
8490
9055
|
try {
|
|
8491
|
-
|
|
9056
|
+
mkdirSync6(lockDir);
|
|
8492
9057
|
const owner = {
|
|
8493
9058
|
pid: process.pid,
|
|
8494
9059
|
token,
|
|
@@ -8498,7 +9063,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
8498
9063
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
8499
9064
|
};
|
|
8500
9065
|
try {
|
|
8501
|
-
|
|
9066
|
+
writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
8502
9067
|
`, { mode: 384 });
|
|
8503
9068
|
} catch (err) {
|
|
8504
9069
|
rmSync3(lockDir, { recursive: true, force: true });
|
|
@@ -8535,8 +9100,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
8535
9100
|
}
|
|
8536
9101
|
|
|
8537
9102
|
// src/localTraceSink.ts
|
|
8538
|
-
import { appendFileSync, mkdirSync as
|
|
8539
|
-
import
|
|
9103
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync3, rmSync as rmSync4, statSync as statSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
9104
|
+
import path15 from "path";
|
|
8540
9105
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
8541
9106
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
8542
9107
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -8572,7 +9137,7 @@ var LocalRotatingTraceSink = class {
|
|
|
8572
9137
|
currentSize = 0;
|
|
8573
9138
|
sequence = 0;
|
|
8574
9139
|
constructor(options) {
|
|
8575
|
-
this.traceDir =
|
|
9140
|
+
this.traceDir = path15.join(options.machineDir, "traces");
|
|
8576
9141
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
8577
9142
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
8578
9143
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -8598,15 +9163,15 @@ var LocalRotatingTraceSink = class {
|
|
|
8598
9163
|
return this.currentFile;
|
|
8599
9164
|
}
|
|
8600
9165
|
ensureFile(nextBytes) {
|
|
8601
|
-
|
|
9166
|
+
mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
|
|
8602
9167
|
const nowMs = this.nowMsProvider();
|
|
8603
9168
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
8604
9169
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
8605
|
-
this.currentFile =
|
|
9170
|
+
this.currentFile = path15.join(
|
|
8606
9171
|
this.traceDir,
|
|
8607
9172
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
8608
9173
|
);
|
|
8609
|
-
|
|
9174
|
+
writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
|
|
8610
9175
|
this.currentSize = statSync4(this.currentFile).size;
|
|
8611
9176
|
this.currentFileOpenedAtMs = nowMs;
|
|
8612
9177
|
this.pruneOldFiles();
|
|
@@ -8617,7 +9182,7 @@ var LocalRotatingTraceSink = class {
|
|
|
8617
9182
|
const excess = files.length - this.maxFiles;
|
|
8618
9183
|
if (excess <= 0) return;
|
|
8619
9184
|
for (const file of files.slice(0, excess)) {
|
|
8620
|
-
rmSync4(
|
|
9185
|
+
rmSync4(path15.join(this.traceDir, file), { force: true });
|
|
8621
9186
|
}
|
|
8622
9187
|
}
|
|
8623
9188
|
};
|
|
@@ -8704,11 +9269,11 @@ function isDiagnosticErrorAttr(key) {
|
|
|
8704
9269
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
|
|
8705
9270
|
import { gzipSync } from "zlib";
|
|
8706
9271
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
8707
|
-
import
|
|
9272
|
+
import path16 from "path";
|
|
8708
9273
|
|
|
8709
9274
|
// src/directUploadCapability.ts
|
|
8710
|
-
function joinUrl(base,
|
|
8711
|
-
return `${base.replace(/\/+$/, "")}${
|
|
9275
|
+
function joinUrl(base, path18) {
|
|
9276
|
+
return `${base.replace(/\/+$/, "")}${path18}`;
|
|
8712
9277
|
}
|
|
8713
9278
|
function jsonHeaders(apiKey) {
|
|
8714
9279
|
return {
|
|
@@ -8927,7 +9492,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
8927
9492
|
}, nextMs);
|
|
8928
9493
|
}
|
|
8929
9494
|
async findUploadCandidates() {
|
|
8930
|
-
const traceDir =
|
|
9495
|
+
const traceDir = path16.join(this.options.machineDir, "traces");
|
|
8931
9496
|
let names;
|
|
8932
9497
|
try {
|
|
8933
9498
|
names = await readdir3(traceDir);
|
|
@@ -8939,8 +9504,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
8939
9504
|
const currentFile = this.options.currentFileProvider?.();
|
|
8940
9505
|
const candidates = [];
|
|
8941
9506
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
8942
|
-
const file =
|
|
8943
|
-
if (currentFile &&
|
|
9507
|
+
const file = path16.join(traceDir, name);
|
|
9508
|
+
if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
|
|
8944
9509
|
if (await this.isUploaded(file)) continue;
|
|
8945
9510
|
try {
|
|
8946
9511
|
const info = await stat3(file);
|
|
@@ -9014,8 +9579,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
9014
9579
|
}
|
|
9015
9580
|
}
|
|
9016
9581
|
uploadStatePath(file) {
|
|
9017
|
-
const stateDir =
|
|
9018
|
-
return
|
|
9582
|
+
const stateDir = path16.join(this.options.machineDir, "trace-uploads");
|
|
9583
|
+
return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
|
|
9019
9584
|
}
|
|
9020
9585
|
async isUploaded(file) {
|
|
9021
9586
|
try {
|
|
@@ -9027,9 +9592,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
9027
9592
|
}
|
|
9028
9593
|
async markUploaded(file, metadata) {
|
|
9029
9594
|
const stateFile = this.uploadStatePath(file);
|
|
9030
|
-
await mkdir2(
|
|
9595
|
+
await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
|
|
9031
9596
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
9032
|
-
file:
|
|
9597
|
+
file: path16.basename(file),
|
|
9033
9598
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9034
9599
|
...metadata
|
|
9035
9600
|
}, null, 2)}
|
|
@@ -9051,7 +9616,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
|
9051
9616
|
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels"];
|
|
9052
9617
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
9053
9618
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
9054
|
-
var DAEMON_CLI_USAGE =
|
|
9619
|
+
var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
|
|
9055
9620
|
var RunnerCredentialMintError2 = class extends Error {
|
|
9056
9621
|
code;
|
|
9057
9622
|
retryable;
|
|
@@ -9087,9 +9652,9 @@ function runnerCredentialErrorDetail2(error) {
|
|
|
9087
9652
|
async function waitForRunnerCredentialRetry2() {
|
|
9088
9653
|
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
9089
9654
|
}
|
|
9090
|
-
function parseDaemonCliArgs(args) {
|
|
9655
|
+
function parseDaemonCliArgs(args, env = {}) {
|
|
9091
9656
|
let serverUrl = "";
|
|
9092
|
-
let apiKey = "";
|
|
9657
|
+
let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
|
|
9093
9658
|
for (let i = 0; i < args.length; i++) {
|
|
9094
9659
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
9095
9660
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -9106,23 +9671,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
9106
9671
|
}
|
|
9107
9672
|
}
|
|
9108
9673
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
9109
|
-
const dirname =
|
|
9110
|
-
const jsPath =
|
|
9674
|
+
const dirname = path17.dirname(fileURLToPath2(moduleUrl));
|
|
9675
|
+
const jsPath = path17.resolve(dirname, "chat-bridge.js");
|
|
9111
9676
|
try {
|
|
9112
9677
|
accessSync(jsPath);
|
|
9113
9678
|
return jsPath;
|
|
9114
9679
|
} catch {
|
|
9115
|
-
return
|
|
9680
|
+
return path17.resolve(dirname, "chat-bridge.ts");
|
|
9116
9681
|
}
|
|
9117
9682
|
}
|
|
9118
9683
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
9119
|
-
const thisDir =
|
|
9120
|
-
const bundledDistPath =
|
|
9684
|
+
const thisDir = path17.dirname(fileURLToPath2(moduleUrl));
|
|
9685
|
+
const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
|
|
9121
9686
|
try {
|
|
9122
9687
|
accessSync(bundledDistPath);
|
|
9123
9688
|
return bundledDistPath;
|
|
9124
9689
|
} catch {
|
|
9125
|
-
const workspaceDistPath =
|
|
9690
|
+
const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
9126
9691
|
accessSync(workspaceDistPath);
|
|
9127
9692
|
return workspaceDistPath;
|
|
9128
9693
|
}
|
|
@@ -9301,7 +9866,7 @@ var DaemonCore = class {
|
|
|
9301
9866
|
}
|
|
9302
9867
|
resolveMachineStateRoot() {
|
|
9303
9868
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
9304
|
-
if (this.options.dataDir) return
|
|
9869
|
+
if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
|
|
9305
9870
|
return resolveDefaultMachineStateRoot();
|
|
9306
9871
|
}
|
|
9307
9872
|
shouldEnableLocalTrace() {
|
|
@@ -9803,6 +10368,8 @@ var DaemonCore = class {
|
|
|
9803
10368
|
};
|
|
9804
10369
|
|
|
9805
10370
|
export {
|
|
10371
|
+
DAEMON_API_KEY_ENV,
|
|
10372
|
+
scrubDaemonAuthEnv,
|
|
9806
10373
|
resolveWorkspaceDirectoryPath,
|
|
9807
10374
|
scanWorkspaceDirectories,
|
|
9808
10375
|
deleteWorkspaceDirectory,
|