@slock-ai/daemon 0.54.2-alpha.0 → 0.54.2-play.20260528162546
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-7ZOPGUXT.js → chunk-MWGRDZGK.js} +932 -407
- package/dist/cli/index.js +69 -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
|
@@ -8,11 +8,35 @@ import {
|
|
|
8
8
|
} from "./chunk-VOZJ2ELH.js";
|
|
9
9
|
|
|
10
10
|
// src/core.ts
|
|
11
|
-
import
|
|
12
|
-
import
|
|
11
|
+
import path17 from "path";
|
|
12
|
+
import os7 from "os";
|
|
13
13
|
import { createRequire as createRequire2 } from "module";
|
|
14
14
|
import { accessSync } from "fs";
|
|
15
|
-
import { fileURLToPath } from "url";
|
|
15
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16
|
+
|
|
17
|
+
// ../shared/src/slockRefs.ts
|
|
18
|
+
var SLOCK_REF_CHANNEL_NAME_PATTERN = String.raw`[\p{L}\p{N}_-]+`;
|
|
19
|
+
var SLOCK_REF_USER_NAME_PATTERN = SLOCK_REF_CHANNEL_NAME_PATTERN;
|
|
20
|
+
var SLOCK_REF_DM_PEER_PATTERN = String.raw`[\w-]+`;
|
|
21
|
+
var SLOCK_REF_THREAD_SHORT_ID_PATTERN = String.raw`[\da-f]{6,8}`;
|
|
22
|
+
var SLOCK_REF_MESSAGE_ID_PATTERN = String.raw`[A-Za-z0-9][A-Za-z0-9-]{1,63}`;
|
|
23
|
+
var SLOCK_REF_TASK_NUMBER_PATTERN = String.raw`[1-9]\d*`;
|
|
24
|
+
var USER_RE = new RegExp(String.raw`^@(${SLOCK_REF_USER_NAME_PATTERN})$`, "u");
|
|
25
|
+
var CHANNEL_RE = new RegExp(String.raw`^#(${SLOCK_REF_CHANNEL_NAME_PATTERN})$`, "u");
|
|
26
|
+
var CHANNEL_THREAD_RE = new RegExp(
|
|
27
|
+
String.raw`^#(${SLOCK_REF_CHANNEL_NAME_PATTERN}):(${SLOCK_REF_THREAD_SHORT_ID_PATTERN})$`,
|
|
28
|
+
"iu"
|
|
29
|
+
);
|
|
30
|
+
var DM_RE = new RegExp(String.raw`^dm:@(${SLOCK_REF_DM_PEER_PATTERN})$`, "iu");
|
|
31
|
+
var DM_THREAD_RE = new RegExp(
|
|
32
|
+
String.raw`^dm:@(${SLOCK_REF_DM_PEER_PATTERN}):(${SLOCK_REF_THREAD_SHORT_ID_PATTERN})$`,
|
|
33
|
+
"iu"
|
|
34
|
+
);
|
|
35
|
+
var TASK_RE = new RegExp(String.raw`^task\s+#(${SLOCK_REF_TASK_NUMBER_PATTERN})$`, "iu");
|
|
36
|
+
var CHANNEL_MESSAGE_RE = new RegExp(
|
|
37
|
+
String.raw`^#(${SLOCK_REF_CHANNEL_NAME_PATTERN})(?::(${SLOCK_REF_THREAD_SHORT_ID_PATTERN}))?\s+msg=(${SLOCK_REF_MESSAGE_ID_PATTERN})$`,
|
|
38
|
+
"iu"
|
|
39
|
+
);
|
|
16
40
|
|
|
17
41
|
// ../shared/src/tracing/index.ts
|
|
18
42
|
var DEFAULT_TRACE_FLAGS = "00";
|
|
@@ -724,9 +748,11 @@ var SERVER_CAPABILITY_MATRIX = {
|
|
|
724
748
|
};
|
|
725
749
|
|
|
726
750
|
// ../shared/src/index.ts
|
|
751
|
+
var RUNTIME_CONFIG_VERSION = 1;
|
|
727
752
|
var RUNTIMES = [
|
|
728
753
|
{ id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
|
|
729
754
|
{ id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
|
|
755
|
+
{ id: "pi", displayName: "Pi", binary: "pi", supported: true },
|
|
730
756
|
{ id: "antigravity", displayName: "Antigravity CLI", binary: "agy", supported: true },
|
|
731
757
|
{ id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
|
|
732
758
|
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
@@ -734,6 +760,171 @@ var RUNTIMES = [
|
|
|
734
760
|
{ id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true },
|
|
735
761
|
{ id: "opencode", displayName: "OpenCode", binary: "opencode", supported: true }
|
|
736
762
|
];
|
|
763
|
+
var RUNTIME_MODELS = {
|
|
764
|
+
claude: [
|
|
765
|
+
{ id: "opus", label: "Opus" },
|
|
766
|
+
{ id: "sonnet", label: "Sonnet" },
|
|
767
|
+
{ id: "haiku", label: "Haiku" }
|
|
768
|
+
],
|
|
769
|
+
codex: [
|
|
770
|
+
{ id: "gpt-5.5", label: "GPT-5.5" },
|
|
771
|
+
{ id: "gpt-5.4", label: "GPT-5.4" },
|
|
772
|
+
{ id: "gpt-5.3-codex", label: "GPT-5.3 Codex" },
|
|
773
|
+
{ id: "gpt-5.3-codex-spark", label: "GPT-5.3 Codex Spark" },
|
|
774
|
+
{ id: "gpt-5.2-codex", label: "GPT-5.2 Codex" },
|
|
775
|
+
{ id: "gpt-5.2", label: "GPT-5.2" },
|
|
776
|
+
{ id: "gpt-5.1-codex-max", label: "GPT-5.1 Codex Max" },
|
|
777
|
+
{ id: "gpt-5.1-codex", label: "GPT-5.1 Codex" },
|
|
778
|
+
{ id: "gpt-5-codex", label: "GPT-5 Codex" },
|
|
779
|
+
{ id: "gpt-5", label: "GPT-5" }
|
|
780
|
+
],
|
|
781
|
+
antigravity: [
|
|
782
|
+
{ id: "default", label: "AGY configured default", verified: "suggestion_only" }
|
|
783
|
+
],
|
|
784
|
+
copilot: [
|
|
785
|
+
{ id: "gpt-5.4", label: "GPT-5.4" },
|
|
786
|
+
{ id: "gpt-5.2", label: "GPT-5.2" },
|
|
787
|
+
{ id: "claude-4-sonnet", label: "Claude 4 Sonnet" },
|
|
788
|
+
{ id: "claude-4.5-sonnet", label: "Claude 4.5 Sonnet" }
|
|
789
|
+
],
|
|
790
|
+
cursor: [
|
|
791
|
+
{ id: "composer-2-fast", label: "Composer 2 Fast" },
|
|
792
|
+
{ id: "composer-2", label: "Composer 2" },
|
|
793
|
+
{ id: "auto", label: "Auto" }
|
|
794
|
+
],
|
|
795
|
+
gemini: [
|
|
796
|
+
{ id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
|
|
797
|
+
{ id: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro (Preview)" },
|
|
798
|
+
{ id: "gemini-3-flash-preview", label: "Gemini 3 Flash (Preview)" },
|
|
799
|
+
{ id: "gemini-2.5-pro", label: "Gemini 2.5 Pro" },
|
|
800
|
+
{ id: "gemini-2.5-flash", label: "Gemini 2.5 Flash" }
|
|
801
|
+
],
|
|
802
|
+
opencode: [
|
|
803
|
+
{ id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
|
|
804
|
+
{ id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro (OpenCode)", verified: "suggestion_only" },
|
|
805
|
+
{ id: "openrouter/anthropic/claude-opus-4.5", label: "Claude Opus 4.5 via OpenRouter", verified: "suggestion_only" },
|
|
806
|
+
{ id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
|
|
807
|
+
],
|
|
808
|
+
pi: [
|
|
809
|
+
{ id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro" },
|
|
810
|
+
{ id: "deepseek/deepseek-v4-flash", label: "DeepSeek V4 Flash" },
|
|
811
|
+
{ id: "kimi-coding/kimi-for-coding", label: "Kimi for Coding" },
|
|
812
|
+
{ id: "zai/glm-5.1", label: "GLM-5.1" },
|
|
813
|
+
{ id: "zai/glm-4.7", label: "GLM-4.7" }
|
|
814
|
+
],
|
|
815
|
+
// Kimi CLI resolves model keys from each user's local config, so the safest
|
|
816
|
+
// built-in option is to defer to whatever default model the CLI already uses.
|
|
817
|
+
kimi: [
|
|
818
|
+
{ id: "default", label: "Configured Default" }
|
|
819
|
+
]
|
|
820
|
+
};
|
|
821
|
+
function getDefaultModel(runtimeId) {
|
|
822
|
+
const models = RUNTIME_MODELS[runtimeId];
|
|
823
|
+
return models?.[0]?.id ?? "sonnet";
|
|
824
|
+
}
|
|
825
|
+
var CONTROLLED_RUNTIME_ENV_KEYS = {
|
|
826
|
+
claude: ["ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY", "ANTHROPIC_CUSTOM_MODEL_OPTION"]
|
|
827
|
+
};
|
|
828
|
+
function isPlainRecord(value) {
|
|
829
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
830
|
+
}
|
|
831
|
+
function normalizeEnvVars(envVars) {
|
|
832
|
+
if (!isPlainRecord(envVars)) return null;
|
|
833
|
+
const normalized = {};
|
|
834
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
835
|
+
if (typeof key === "string" && typeof value === "string") {
|
|
836
|
+
normalized[key] = value;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
840
|
+
}
|
|
841
|
+
function getControlledRuntimeEnvKeys(runtime) {
|
|
842
|
+
return CONTROLLED_RUNTIME_ENV_KEYS[runtime] ?? [];
|
|
843
|
+
}
|
|
844
|
+
function stripControlledRuntimeEnvVars(runtime, envVars) {
|
|
845
|
+
const normalized = normalizeEnvVars(envVars);
|
|
846
|
+
if (!normalized) return null;
|
|
847
|
+
const controlled = new Set(getControlledRuntimeEnvKeys(runtime));
|
|
848
|
+
for (const key of controlled) {
|
|
849
|
+
delete normalized[key];
|
|
850
|
+
}
|
|
851
|
+
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
852
|
+
}
|
|
853
|
+
function isPresetRuntimeModel(runtime, model) {
|
|
854
|
+
return (RUNTIME_MODELS[runtime] ?? []).some((candidate) => candidate.id === model);
|
|
855
|
+
}
|
|
856
|
+
function modelConfigFromLegacy(runtime, model) {
|
|
857
|
+
return isPresetRuntimeModel(runtime, model) ? { kind: "preset", id: model } : { kind: "custom", name: model };
|
|
858
|
+
}
|
|
859
|
+
function parseProviderConfig(runtime, value) {
|
|
860
|
+
if (runtime !== "claude") return void 0;
|
|
861
|
+
if (!isPlainRecord(value)) return { kind: "default" };
|
|
862
|
+
if (value.kind === "custom" && typeof value.apiUrl === "string" && value.apiUrl.trim() && typeof value.apiKey === "string" && value.apiKey.trim()) {
|
|
863
|
+
return { kind: "custom", apiUrl: value.apiUrl.trim(), apiKey: value.apiKey.trim() };
|
|
864
|
+
}
|
|
865
|
+
return { kind: "default" };
|
|
866
|
+
}
|
|
867
|
+
function parseModelConfig(runtime, value, fallback) {
|
|
868
|
+
if (!isPlainRecord(value)) return modelConfigFromLegacy(runtime, fallback);
|
|
869
|
+
if (value.kind === "custom" && typeof value.name === "string" && value.name.trim()) {
|
|
870
|
+
return { kind: "custom", name: value.name.trim() };
|
|
871
|
+
}
|
|
872
|
+
if (value.kind === "preset" && typeof value.id === "string" && value.id.trim()) {
|
|
873
|
+
return { kind: "preset", id: value.id.trim() };
|
|
874
|
+
}
|
|
875
|
+
return modelConfigFromLegacy(runtime, fallback);
|
|
876
|
+
}
|
|
877
|
+
function parseModeConfig(value) {
|
|
878
|
+
if (!isPlainRecord(value)) return { kind: "default" };
|
|
879
|
+
if (value.kind === "fast") return { kind: "fast" };
|
|
880
|
+
return { kind: "default" };
|
|
881
|
+
}
|
|
882
|
+
function hydrateRuntimeConfig(input) {
|
|
883
|
+
const stored = isPlainRecord(input.runtimeConfig) && input.runtimeConfig.version === RUNTIME_CONFIG_VERSION ? input.runtimeConfig : null;
|
|
884
|
+
const runtime = typeof stored?.runtime === "string" && stored.runtime.trim() || typeof input.runtime === "string" && input.runtime.trim() || "claude";
|
|
885
|
+
const fallbackModel = typeof input.model === "string" && input.model.trim() || getDefaultModel(runtime);
|
|
886
|
+
const legacyEnvVars = normalizeEnvVars(input.envVars);
|
|
887
|
+
const storedEnvVars = normalizeEnvVars(stored?.envVars);
|
|
888
|
+
const legacyClaudeApiUrl = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_BASE_URL : void 0;
|
|
889
|
+
const legacyClaudeApiKey = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_API_KEY : void 0;
|
|
890
|
+
const provider = stored ? parseProviderConfig(runtime, stored.provider) : runtime === "claude" && legacyClaudeApiUrl && legacyClaudeApiKey ? { kind: "custom", apiUrl: legacyClaudeApiUrl, apiKey: legacyClaudeApiKey } : runtime === "claude" ? { kind: "default" } : void 0;
|
|
891
|
+
return {
|
|
892
|
+
version: RUNTIME_CONFIG_VERSION,
|
|
893
|
+
runtime,
|
|
894
|
+
...provider ? { provider } : {},
|
|
895
|
+
model: stored ? parseModelConfig(runtime, stored.model, fallbackModel) : modelConfigFromLegacy(runtime, fallbackModel),
|
|
896
|
+
mode: stored ? parseModeConfig(stored.mode) : { kind: "default" },
|
|
897
|
+
reasoningEffort: stored?.reasoningEffort ?? input.reasoningEffort ?? null,
|
|
898
|
+
envVars: stripControlledRuntimeEnvVars(runtime, stored ? storedEnvVars : legacyEnvVars)
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
function runtimeConfigModelValue(config) {
|
|
902
|
+
return config.model.kind === "custom" ? config.model.name : config.model.id;
|
|
903
|
+
}
|
|
904
|
+
function runtimeConfigToLaunchFields(config) {
|
|
905
|
+
const normalized = isPlainRecord(config) && config.version === RUNTIME_CONFIG_VERSION ? hydrateRuntimeConfig({ runtimeConfig: config }) : hydrateRuntimeConfig(config);
|
|
906
|
+
const generatedEnvVars = {};
|
|
907
|
+
if (normalized.runtime === "claude") {
|
|
908
|
+
if (normalized.provider?.kind === "custom") {
|
|
909
|
+
generatedEnvVars.ANTHROPIC_BASE_URL = normalized.provider.apiUrl;
|
|
910
|
+
generatedEnvVars.ANTHROPIC_API_KEY = normalized.provider.apiKey;
|
|
911
|
+
}
|
|
912
|
+
if (normalized.model.kind === "custom") {
|
|
913
|
+
generatedEnvVars.ANTHROPIC_CUSTOM_MODEL_OPTION = normalized.model.name;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
const envVars = {
|
|
917
|
+
...normalized.envVars ?? {},
|
|
918
|
+
...generatedEnvVars
|
|
919
|
+
};
|
|
920
|
+
return {
|
|
921
|
+
runtime: normalized.runtime,
|
|
922
|
+
model: runtimeConfigModelValue(normalized),
|
|
923
|
+
mode: normalized.mode,
|
|
924
|
+
reasoningEffort: normalized.reasoningEffort ?? null,
|
|
925
|
+
envVars: Object.keys(envVars).length > 0 ? envVars : null
|
|
926
|
+
};
|
|
927
|
+
}
|
|
737
928
|
var PLAN_CONFIG = {
|
|
738
929
|
free: {
|
|
739
930
|
displayName: "Hobby",
|
|
@@ -769,17 +960,14 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
769
960
|
};
|
|
770
961
|
|
|
771
962
|
// src/agentProcessManager.ts
|
|
772
|
-
import { mkdirSync as
|
|
963
|
+
import { mkdirSync as mkdirSync5, readdirSync, statSync, writeFileSync as writeFileSync8 } from "fs";
|
|
773
964
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
774
965
|
import { createHash as createHash2 } from "crypto";
|
|
775
|
-
import
|
|
776
|
-
import
|
|
966
|
+
import path13 from "path";
|
|
967
|
+
import os5 from "os";
|
|
777
968
|
|
|
778
969
|
// src/drivers/claude.ts
|
|
779
970
|
import { spawn } from "child_process";
|
|
780
|
-
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
781
|
-
import os2 from "os";
|
|
782
|
-
import path4 from "path";
|
|
783
971
|
|
|
784
972
|
// src/drivers/cliTransport.ts
|
|
785
973
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
@@ -1359,6 +1547,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1359
1547
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1360
1548
|
}
|
|
1361
1549
|
|
|
1550
|
+
// src/authEnv.ts
|
|
1551
|
+
var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
|
|
1552
|
+
var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
|
|
1553
|
+
function scrubDaemonAuthEnv(env) {
|
|
1554
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1555
|
+
return env;
|
|
1556
|
+
}
|
|
1557
|
+
function scrubDaemonChildEnv(env) {
|
|
1558
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1559
|
+
delete env[SLOCK_AGENT_TOKEN_ENV];
|
|
1560
|
+
return env;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1362
1563
|
// src/agentCredentialProxy.ts
|
|
1363
1564
|
import { randomBytes } from "crypto";
|
|
1364
1565
|
import http from "http";
|
|
@@ -1949,11 +2150,13 @@ function unregisterAgentCredentialProxyForLaunch(input) {
|
|
|
1949
2150
|
// src/drivers/cliTransport.ts
|
|
1950
2151
|
var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1951
2152
|
var powershellSingleQuote = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
1952
|
-
var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels";
|
|
2153
|
+
var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels,knowledge";
|
|
1953
2154
|
var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
|
|
1954
2155
|
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
1955
2156
|
var RAW_CREDENTIAL_ENV_DENYLIST = [
|
|
1956
|
-
"
|
|
2157
|
+
"SLOCK_AGENT_TOKEN",
|
|
2158
|
+
"SLOCK_AGENT_CREDENTIAL_KEY",
|
|
2159
|
+
"SLOCK_AGENT_CREDENTIAL_KEY_FILE"
|
|
1957
2160
|
];
|
|
1958
2161
|
var cachedOpencliBinPath;
|
|
1959
2162
|
function resolveOpencliBinPath() {
|
|
@@ -2152,10 +2355,11 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2152
2355
|
}
|
|
2153
2356
|
}
|
|
2154
2357
|
const wrapperPath = platform === "win32" ? path2.join(slockDir, "slock.cmd") : posixWrapper;
|
|
2358
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
2155
2359
|
const spawnEnv = {
|
|
2156
2360
|
...process.env,
|
|
2157
2361
|
FORCE_COLOR: "0",
|
|
2158
|
-
...
|
|
2362
|
+
...launchRuntimeFields.envVars || {},
|
|
2159
2363
|
...extraEnv,
|
|
2160
2364
|
...platform === "win32" ? windowsUtf8Env() : {},
|
|
2161
2365
|
...runtimeContextEnv(ctx.config),
|
|
@@ -2166,7 +2370,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2166
2370
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
2167
2371
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
2168
2372
|
};
|
|
2169
|
-
|
|
2373
|
+
scrubDaemonChildEnv(spawnEnv);
|
|
2170
2374
|
for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
|
|
2171
2375
|
delete spawnEnv[key];
|
|
2172
2376
|
}
|
|
@@ -2186,6 +2390,129 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2186
2390
|
};
|
|
2187
2391
|
}
|
|
2188
2392
|
|
|
2393
|
+
// src/drivers/claudeEventNormalizer.ts
|
|
2394
|
+
function collectResultErrorDetail(message, fallback) {
|
|
2395
|
+
const parts = [];
|
|
2396
|
+
if (Array.isArray(message.errors)) {
|
|
2397
|
+
for (const err of message.errors) {
|
|
2398
|
+
if (typeof err === "string" && err.trim()) parts.push(err.trim());
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
if (typeof message.result === "string" && message.result.trim()) {
|
|
2402
|
+
parts.push(message.result.trim());
|
|
2403
|
+
}
|
|
2404
|
+
return parts.join(" | ") || fallback;
|
|
2405
|
+
}
|
|
2406
|
+
function isProviderApiFailureText(value, hasToolUse) {
|
|
2407
|
+
return !hasToolUse && /^\s*API Error:/i.test(value) && (/\b(?:ECONNRESET|EPIPE|ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/i.test(value) || /\bUnable to connect to API\b/i.test(value) || /\b(?:timed out|timeout)\b/i.test(value) || /\b5\d{2}\b/.test(value));
|
|
2408
|
+
}
|
|
2409
|
+
var ClaudeEventNormalizer = class {
|
|
2410
|
+
normalizeLine(line) {
|
|
2411
|
+
let event;
|
|
2412
|
+
try {
|
|
2413
|
+
event = JSON.parse(line);
|
|
2414
|
+
} catch {
|
|
2415
|
+
return [];
|
|
2416
|
+
}
|
|
2417
|
+
const events = [];
|
|
2418
|
+
const pushResultError = (message, fallback) => {
|
|
2419
|
+
events.push({ kind: "error", message: collectResultErrorDetail(message, fallback) });
|
|
2420
|
+
};
|
|
2421
|
+
switch (event.type) {
|
|
2422
|
+
case "system":
|
|
2423
|
+
if (event.subtype === "init" && event.session_id) {
|
|
2424
|
+
events.push({ kind: "session_init", sessionId: event.session_id });
|
|
2425
|
+
}
|
|
2426
|
+
if (event.subtype === "status" && event.status === "compacting") {
|
|
2427
|
+
events.push({ kind: "compaction_started" });
|
|
2428
|
+
}
|
|
2429
|
+
if (event.subtype === "status" && event.status === "requesting") {
|
|
2430
|
+
events.push({
|
|
2431
|
+
kind: "internal_progress",
|
|
2432
|
+
source: "claude_system_status",
|
|
2433
|
+
itemType: "requesting",
|
|
2434
|
+
payloadBytes: Buffer.byteLength(line, "utf8")
|
|
2435
|
+
});
|
|
2436
|
+
}
|
|
2437
|
+
if (event.subtype === "compact_boundary") {
|
|
2438
|
+
events.push({ kind: "compaction_finished" });
|
|
2439
|
+
}
|
|
2440
|
+
break;
|
|
2441
|
+
case "stream_event":
|
|
2442
|
+
events.push({
|
|
2443
|
+
kind: "internal_progress",
|
|
2444
|
+
source: "claude_stream_event",
|
|
2445
|
+
itemType: typeof event.event?.type === "string" && event.event.type.length > 0 ? event.event.type : "unknown",
|
|
2446
|
+
payloadBytes: Buffer.byteLength(line, "utf8")
|
|
2447
|
+
});
|
|
2448
|
+
break;
|
|
2449
|
+
case "assistant": {
|
|
2450
|
+
const content = event.message?.content;
|
|
2451
|
+
if (Array.isArray(content)) {
|
|
2452
|
+
const hasToolUse = content.some((block) => block?.type === "tool_use");
|
|
2453
|
+
for (const block of content) {
|
|
2454
|
+
if (block.type === "thinking" && block.thinking) {
|
|
2455
|
+
events.push({ kind: "thinking", text: block.thinking });
|
|
2456
|
+
} else if (block.type === "text" && block.text) {
|
|
2457
|
+
if (isProviderApiFailureText(block.text, hasToolUse)) {
|
|
2458
|
+
events.push({ kind: "error", message: block.text });
|
|
2459
|
+
} else {
|
|
2460
|
+
events.push({ kind: "text", text: block.text });
|
|
2461
|
+
}
|
|
2462
|
+
} else if (block.type === "tool_use") {
|
|
2463
|
+
events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
break;
|
|
2468
|
+
}
|
|
2469
|
+
case "user": {
|
|
2470
|
+
const content = event.message?.content;
|
|
2471
|
+
if (Array.isArray(content)) {
|
|
2472
|
+
for (const block of content) {
|
|
2473
|
+
if (block.type === "tool_result") {
|
|
2474
|
+
events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result" });
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
break;
|
|
2479
|
+
}
|
|
2480
|
+
case "result": {
|
|
2481
|
+
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
2482
|
+
const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
|
|
2483
|
+
switch (subtype) {
|
|
2484
|
+
case "success":
|
|
2485
|
+
if (event.is_error && stopReason !== "max_tokens") {
|
|
2486
|
+
pushResultError(event, "Execution failed");
|
|
2487
|
+
}
|
|
2488
|
+
break;
|
|
2489
|
+
case "error_during_execution":
|
|
2490
|
+
if (stopReason !== "max_tokens") {
|
|
2491
|
+
pushResultError(event, "Execution failed");
|
|
2492
|
+
}
|
|
2493
|
+
break;
|
|
2494
|
+
case "error_max_budget_usd":
|
|
2495
|
+
pushResultError(event, "Budget limit exceeded");
|
|
2496
|
+
break;
|
|
2497
|
+
case "error_max_turns":
|
|
2498
|
+
pushResultError(event, "Max turns exceeded");
|
|
2499
|
+
break;
|
|
2500
|
+
case "error_max_structured_output_retries":
|
|
2501
|
+
pushResultError(event, "Structured output retries exceeded");
|
|
2502
|
+
break;
|
|
2503
|
+
}
|
|
2504
|
+
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
2505
|
+
break;
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
return events;
|
|
2509
|
+
}
|
|
2510
|
+
};
|
|
2511
|
+
|
|
2512
|
+
// src/drivers/claudeLaunch.ts
|
|
2513
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
2514
|
+
import path4 from "path";
|
|
2515
|
+
|
|
2189
2516
|
// src/drivers/probe.ts
|
|
2190
2517
|
import { execFileSync } from "child_process";
|
|
2191
2518
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -2342,7 +2669,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
|
|
|
2342
2669
|
}
|
|
2343
2670
|
function resolveCommandOnPath(command, deps = {}) {
|
|
2344
2671
|
const platform = deps.platform ?? process.platform;
|
|
2345
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
2672
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
2346
2673
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
2347
2674
|
const existsSyncFn = deps.existsSyncFn ?? existsSync2;
|
|
2348
2675
|
if (platform === "win32") {
|
|
@@ -2368,7 +2695,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
2368
2695
|
return null;
|
|
2369
2696
|
}
|
|
2370
2697
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
2371
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
2698
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
2372
2699
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
2373
2700
|
try {
|
|
2374
2701
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -2386,12 +2713,10 @@ function resolveHomePath(relativePath, deps = {}) {
|
|
|
2386
2713
|
return path3.join(homeDir, relativePath);
|
|
2387
2714
|
}
|
|
2388
2715
|
|
|
2389
|
-
// src/drivers/
|
|
2716
|
+
// src/drivers/claudeLaunch.ts
|
|
2390
2717
|
var CLAUDE_DESKTOP_CLI_RELATIVE_PATH = path4.join("Applications", "Claude Code URL Handler.app", "Contents", "MacOS", "claude");
|
|
2391
2718
|
var CLAUDE_DESKTOP_CLI_SYSTEM_PATH = "/Applications/Claude Code URL Handler.app/Contents/MacOS/claude";
|
|
2392
2719
|
var CLAUDE_SYSTEM_PROMPT_FILE = "claude-system-prompt.md";
|
|
2393
|
-
var CLAUDE_MCP_CONFIG_FILE = "claude-mcp-config.json";
|
|
2394
|
-
var SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME = "chat";
|
|
2395
2720
|
var CLAUDE_DISALLOWED_TOOLS = [
|
|
2396
2721
|
"EnterPlanMode",
|
|
2397
2722
|
"ExitPlanMode",
|
|
@@ -2417,81 +2742,54 @@ function probeClaude(deps = {}) {
|
|
|
2417
2742
|
version: readCommandVersion(command, [], deps) ?? void 0
|
|
2418
2743
|
};
|
|
2419
2744
|
}
|
|
2420
|
-
function
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2745
|
+
function buildClaudeArgs(config, opts) {
|
|
2746
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(config);
|
|
2747
|
+
const args = [
|
|
2748
|
+
"--allow-dangerously-skip-permissions",
|
|
2749
|
+
"--dangerously-skip-permissions",
|
|
2750
|
+
"--verbose",
|
|
2751
|
+
"--permission-mode",
|
|
2752
|
+
"bypassPermissions",
|
|
2753
|
+
"--output-format",
|
|
2754
|
+
"stream-json",
|
|
2755
|
+
"--input-format",
|
|
2756
|
+
"stream-json",
|
|
2757
|
+
"--include-partial-messages",
|
|
2758
|
+
"--model",
|
|
2759
|
+
launchRuntimeFields.model || "sonnet",
|
|
2760
|
+
"--disallowed-tools",
|
|
2761
|
+
CLAUDE_DISALLOWED_TOOLS,
|
|
2762
|
+
"--append-system-prompt-file",
|
|
2763
|
+
opts.standingPromptFilePath
|
|
2764
|
+
];
|
|
2765
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
2766
|
+
args.push("--effort", launchRuntimeFields.reasoningEffort);
|
|
2428
2767
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
expandClaudeMcpConfigVariables(readFileSync2(configPath, "utf8"), vars)
|
|
2435
|
-
);
|
|
2436
|
-
if (!isRecord(parsed) || !isRecord(parsed.mcpServers)) return null;
|
|
2437
|
-
return parsed.mcpServers;
|
|
2438
|
-
} catch (err) {
|
|
2439
|
-
logger.warn(
|
|
2440
|
-
`[Claude] failed to read MCP config ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
2441
|
-
);
|
|
2442
|
-
return null;
|
|
2768
|
+
if (launchRuntimeFields.mode.kind === "fast") {
|
|
2769
|
+
args.push("--bare");
|
|
2770
|
+
}
|
|
2771
|
+
if (config.sessionId) {
|
|
2772
|
+
args.push("--resume", config.sessionId);
|
|
2443
2773
|
}
|
|
2774
|
+
return args;
|
|
2444
2775
|
}
|
|
2445
|
-
function
|
|
2446
|
-
const
|
|
2447
|
-
|
|
2776
|
+
function writeClaudeSystemPromptFile(standingPrompt, slockDir) {
|
|
2777
|
+
const systemPromptPath = path4.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
|
|
2778
|
+
writeFileSync2(systemPromptPath, standingPrompt, { mode: 384 });
|
|
2779
|
+
return systemPromptPath;
|
|
2448
2780
|
}
|
|
2449
|
-
function
|
|
2450
|
-
const
|
|
2451
|
-
const
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
}
|
|
2781
|
+
function buildClaudeSpawnSpec(claudeCommand, platform = process.platform) {
|
|
2782
|
+
const lowerClaudeCommand = claudeCommand?.toLowerCase();
|
|
2783
|
+
const isBatchFile = Boolean(
|
|
2784
|
+
platform === "win32" && lowerClaudeCommand && (lowerClaudeCommand.endsWith(".cmd") || lowerClaudeCommand.endsWith(".bat"))
|
|
2785
|
+
);
|
|
2786
|
+
return {
|
|
2787
|
+
command: claudeCommand ?? "claude",
|
|
2788
|
+
shell: platform === "win32" && (!claudeCommand || isBatchFile)
|
|
2458
2789
|
};
|
|
2459
|
-
pushIfFile(path4.join(home, ".claude.json"));
|
|
2460
|
-
pushIfFile(path4.join(ctx.workingDirectory, ".mcp.json"));
|
|
2461
|
-
const pluginRoot = path4.join(resolveClaudeConfigDir(ctx, home), "plugins");
|
|
2462
|
-
try {
|
|
2463
|
-
for (const entry of readdirSync(pluginRoot)) {
|
|
2464
|
-
const pluginPath = path4.join(pluginRoot, entry);
|
|
2465
|
-
const configPath = path4.join(pluginPath, ".mcp.json");
|
|
2466
|
-
try {
|
|
2467
|
-
if (existsSync3(configPath) && statSync(configPath).isFile()) {
|
|
2468
|
-
files.push({
|
|
2469
|
-
path: configPath,
|
|
2470
|
-
vars: { CLAUDE_PLUGIN_ROOT: pluginPath }
|
|
2471
|
-
});
|
|
2472
|
-
}
|
|
2473
|
-
} catch {
|
|
2474
|
-
}
|
|
2475
|
-
}
|
|
2476
|
-
} catch {
|
|
2477
|
-
}
|
|
2478
|
-
return files;
|
|
2479
|
-
}
|
|
2480
|
-
function buildClaudeUserMcpServers(ctx, home) {
|
|
2481
|
-
const servers = /* @__PURE__ */ Object.create(null);
|
|
2482
|
-
for (const configFile of collectClaudeMcpConfigFiles(ctx, home)) {
|
|
2483
|
-
const mcpServers = readClaudeMcpServers(configFile.path, configFile.vars);
|
|
2484
|
-
if (!mcpServers) continue;
|
|
2485
|
-
for (const [name, server] of Object.entries(mcpServers)) {
|
|
2486
|
-
if (!isRecord(server)) {
|
|
2487
|
-
logger.warn(`[Claude] ignoring invalid MCP server "${name}" in ${configFile.path}`);
|
|
2488
|
-
continue;
|
|
2489
|
-
}
|
|
2490
|
-
servers[name] = server;
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
|
-
return servers;
|
|
2494
2790
|
}
|
|
2791
|
+
|
|
2792
|
+
// src/drivers/claude.ts
|
|
2495
2793
|
var ClaudeDriver = class {
|
|
2496
2794
|
id = "claude";
|
|
2497
2795
|
lifecycle = {
|
|
@@ -2501,7 +2799,7 @@ var ClaudeDriver = class {
|
|
|
2501
2799
|
};
|
|
2502
2800
|
communication = {
|
|
2503
2801
|
chat: "slock_cli",
|
|
2504
|
-
runtimeControl: "
|
|
2802
|
+
runtimeControl: "none"
|
|
2505
2803
|
};
|
|
2506
2804
|
session = {
|
|
2507
2805
|
recovery: "resume_or_fresh"
|
|
@@ -2511,107 +2809,37 @@ var ClaudeDriver = class {
|
|
|
2511
2809
|
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
2512
2810
|
};
|
|
2513
2811
|
supportsStdinNotification = true;
|
|
2514
|
-
mcpToolPrefix = "
|
|
2812
|
+
mcpToolPrefix = "";
|
|
2515
2813
|
usesSlockCliForCommunication = true;
|
|
2516
2814
|
// Claude Code supports same-turn steering, but raw stdin injection at an
|
|
2517
2815
|
// arbitrary busy instant can collide with active signed thinking blocks. The
|
|
2518
2816
|
// daemon therefore gates busy delivery on Claude stream-json boundaries.
|
|
2519
2817
|
busyDeliveryMode = "gated";
|
|
2520
2818
|
supportsNativeStandingPrompt = true;
|
|
2819
|
+
eventNormalizer = new ClaudeEventNormalizer();
|
|
2521
2820
|
probe() {
|
|
2522
2821
|
return probeClaude();
|
|
2523
2822
|
}
|
|
2524
|
-
buildClaudeArgs(config,
|
|
2525
|
-
|
|
2526
|
-
"--allow-dangerously-skip-permissions",
|
|
2527
|
-
"--dangerously-skip-permissions",
|
|
2528
|
-
"--verbose",
|
|
2529
|
-
"--permission-mode",
|
|
2530
|
-
"bypassPermissions",
|
|
2531
|
-
"--output-format",
|
|
2532
|
-
"stream-json",
|
|
2533
|
-
"--input-format",
|
|
2534
|
-
"stream-json",
|
|
2535
|
-
"--model",
|
|
2536
|
-
config.model || "sonnet",
|
|
2537
|
-
"--disallowed-tools",
|
|
2538
|
-
CLAUDE_DISALLOWED_TOOLS
|
|
2539
|
-
];
|
|
2540
|
-
if (opts.standingPromptFilePath) {
|
|
2541
|
-
args.push("--append-system-prompt-file", opts.standingPromptFilePath);
|
|
2542
|
-
} else {
|
|
2543
|
-
args.push("--append-system-prompt", standingPrompt);
|
|
2544
|
-
}
|
|
2545
|
-
if (config.sessionId) {
|
|
2546
|
-
args.push("--resume", config.sessionId);
|
|
2547
|
-
}
|
|
2548
|
-
return args;
|
|
2549
|
-
}
|
|
2550
|
-
buildRuntimeActionsMcpServer(ctx) {
|
|
2551
|
-
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
2552
|
-
const command = isTsSource ? "npx" : "node";
|
|
2553
|
-
const bridgeArgs = isTsSource ? ["tsx", ctx.chatBridgePath] : [ctx.chatBridgePath];
|
|
2554
|
-
return {
|
|
2555
|
-
command,
|
|
2556
|
-
args: [
|
|
2557
|
-
...bridgeArgs,
|
|
2558
|
-
"--agent-id",
|
|
2559
|
-
ctx.agentId,
|
|
2560
|
-
"--server-url",
|
|
2561
|
-
ctx.config.serverUrl,
|
|
2562
|
-
"--auth-token",
|
|
2563
|
-
ctx.config.authToken || ctx.daemonApiKey,
|
|
2564
|
-
"--runtime",
|
|
2565
|
-
this.id,
|
|
2566
|
-
...ctx.launchId ? ["--launch-id", ctx.launchId] : [],
|
|
2567
|
-
"--runtime-actions-only"
|
|
2568
|
-
]
|
|
2569
|
-
};
|
|
2570
|
-
}
|
|
2571
|
-
buildRuntimeActionsMcpConfig(ctx, home = os2.homedir()) {
|
|
2572
|
-
const userMcpServers = buildClaudeUserMcpServers(ctx, home);
|
|
2573
|
-
if (Object.prototype.hasOwnProperty.call(userMcpServers, SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME)) {
|
|
2574
|
-
logger.warn(
|
|
2575
|
-
`[Agent ${ctx.agentId}] Claude user MCP server "${SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME}" is reserved by Slock runtime actions and will be ignored`
|
|
2576
|
-
);
|
|
2577
|
-
}
|
|
2578
|
-
return JSON.stringify({
|
|
2579
|
-
mcpServers: {
|
|
2580
|
-
...userMcpServers,
|
|
2581
|
-
[SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME]: this.buildRuntimeActionsMcpServer(ctx)
|
|
2582
|
-
}
|
|
2583
|
-
});
|
|
2584
|
-
}
|
|
2585
|
-
writeClaudeLaunchFiles(ctx, slockDir, home = os2.homedir()) {
|
|
2586
|
-
const systemPromptPath = path4.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
|
|
2587
|
-
const mcpConfigPath = path4.join(slockDir, CLAUDE_MCP_CONFIG_FILE);
|
|
2588
|
-
writeFileSync2(systemPromptPath, ctx.standingPrompt, { mode: 384 });
|
|
2589
|
-
writeFileSync2(mcpConfigPath, this.buildRuntimeActionsMcpConfig(ctx, home), { mode: 384 });
|
|
2590
|
-
return { systemPromptPath, mcpConfigPath };
|
|
2823
|
+
buildClaudeArgs(config, opts) {
|
|
2824
|
+
return buildClaudeArgs(config, opts);
|
|
2591
2825
|
}
|
|
2592
2826
|
async spawn(ctx) {
|
|
2593
2827
|
const { slockDir, tokenFile, spawnEnv } = await prepareCliTransport(ctx);
|
|
2594
|
-
const
|
|
2595
|
-
const args = this.buildClaudeArgs(ctx.config,
|
|
2828
|
+
const systemPromptPath = writeClaudeSystemPromptFile(ctx.standingPrompt, slockDir);
|
|
2829
|
+
const args = this.buildClaudeArgs(ctx.config, {
|
|
2596
2830
|
standingPromptFilePath: systemPromptPath
|
|
2597
2831
|
});
|
|
2598
|
-
args.push("--mcp-config", mcpConfigPath, "--strict-mcp-config");
|
|
2599
2832
|
delete spawnEnv.CLAUDECODE;
|
|
2600
2833
|
logger.info(
|
|
2601
2834
|
`[Agent ${ctx.agentId}] transport=cli cli=${ctx.slockCliPath} token_file=${tokenFile}`
|
|
2602
2835
|
);
|
|
2603
2836
|
const claudeCommand = resolveClaudeCommand();
|
|
2604
|
-
const
|
|
2605
|
-
const
|
|
2606
|
-
const isBatchFile = Boolean(
|
|
2607
|
-
isWindows && lowerClaudeCommand && (lowerClaudeCommand.endsWith(".cmd") || lowerClaudeCommand.endsWith(".bat"))
|
|
2608
|
-
);
|
|
2609
|
-
const useShell = isWindows && (!claudeCommand || isBatchFile);
|
|
2610
|
-
const proc = spawn(claudeCommand ?? "claude", args, {
|
|
2837
|
+
const spawnSpec = buildClaudeSpawnSpec(claudeCommand);
|
|
2838
|
+
const proc = spawn(spawnSpec.command, args, {
|
|
2611
2839
|
cwd: ctx.workingDirectory,
|
|
2612
2840
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2613
2841
|
env: spawnEnv,
|
|
2614
|
-
shell:
|
|
2842
|
+
shell: spawnSpec.shell
|
|
2615
2843
|
});
|
|
2616
2844
|
const stdinMsg = JSON.stringify({
|
|
2617
2845
|
type: "user",
|
|
@@ -2625,99 +2853,7 @@ var ClaudeDriver = class {
|
|
|
2625
2853
|
return { process: proc };
|
|
2626
2854
|
}
|
|
2627
2855
|
parseLine(line) {
|
|
2628
|
-
|
|
2629
|
-
try {
|
|
2630
|
-
event = JSON.parse(line);
|
|
2631
|
-
} catch {
|
|
2632
|
-
return [];
|
|
2633
|
-
}
|
|
2634
|
-
const events = [];
|
|
2635
|
-
const pushResultError = (message, fallback) => {
|
|
2636
|
-
const parts = [];
|
|
2637
|
-
if (Array.isArray(message.errors)) {
|
|
2638
|
-
for (const err of message.errors) {
|
|
2639
|
-
if (typeof err === "string" && err.trim()) parts.push(err.trim());
|
|
2640
|
-
}
|
|
2641
|
-
}
|
|
2642
|
-
if (typeof message.result === "string" && message.result.trim()) {
|
|
2643
|
-
parts.push(message.result.trim());
|
|
2644
|
-
}
|
|
2645
|
-
const detail = parts.join(" | ") || fallback;
|
|
2646
|
-
events.push({ kind: "error", message: detail });
|
|
2647
|
-
};
|
|
2648
|
-
const isProviderApiFailureText = (value, hasToolUse) => !hasToolUse && /^\s*API Error:/i.test(value) && (/\b(?:ECONNRESET|EPIPE|ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/i.test(value) || /\bUnable to connect to API\b/i.test(value) || /\b(?:timed out|timeout)\b/i.test(value) || /\b5\d{2}\b/.test(value));
|
|
2649
|
-
switch (event.type) {
|
|
2650
|
-
case "system":
|
|
2651
|
-
if (event.subtype === "init" && event.session_id) {
|
|
2652
|
-
events.push({ kind: "session_init", sessionId: event.session_id });
|
|
2653
|
-
}
|
|
2654
|
-
if (event.subtype === "status" && event.status === "compacting") {
|
|
2655
|
-
events.push({ kind: "compaction_started" });
|
|
2656
|
-
}
|
|
2657
|
-
if (event.subtype === "compact_boundary") {
|
|
2658
|
-
events.push({ kind: "compaction_finished" });
|
|
2659
|
-
}
|
|
2660
|
-
break;
|
|
2661
|
-
case "assistant": {
|
|
2662
|
-
const content = event.message?.content;
|
|
2663
|
-
if (Array.isArray(content)) {
|
|
2664
|
-
const hasToolUse = content.some((block) => block?.type === "tool_use");
|
|
2665
|
-
for (const block of content) {
|
|
2666
|
-
if (block.type === "thinking" && block.thinking) {
|
|
2667
|
-
events.push({ kind: "thinking", text: block.thinking });
|
|
2668
|
-
} else if (block.type === "text" && block.text) {
|
|
2669
|
-
if (isProviderApiFailureText(block.text, hasToolUse)) {
|
|
2670
|
-
events.push({ kind: "error", message: block.text });
|
|
2671
|
-
} else {
|
|
2672
|
-
events.push({ kind: "text", text: block.text });
|
|
2673
|
-
}
|
|
2674
|
-
} else if (block.type === "tool_use") {
|
|
2675
|
-
events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
|
|
2676
|
-
}
|
|
2677
|
-
}
|
|
2678
|
-
}
|
|
2679
|
-
break;
|
|
2680
|
-
}
|
|
2681
|
-
case "user": {
|
|
2682
|
-
const content = event.message?.content;
|
|
2683
|
-
if (Array.isArray(content)) {
|
|
2684
|
-
for (const block of content) {
|
|
2685
|
-
if (block.type === "tool_result") {
|
|
2686
|
-
events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result" });
|
|
2687
|
-
}
|
|
2688
|
-
}
|
|
2689
|
-
}
|
|
2690
|
-
break;
|
|
2691
|
-
}
|
|
2692
|
-
case "result": {
|
|
2693
|
-
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
2694
|
-
const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
|
|
2695
|
-
switch (subtype) {
|
|
2696
|
-
case "success":
|
|
2697
|
-
if (event.is_error && stopReason !== "max_tokens") {
|
|
2698
|
-
pushResultError(event, "Execution failed");
|
|
2699
|
-
}
|
|
2700
|
-
break;
|
|
2701
|
-
case "error_during_execution":
|
|
2702
|
-
if (stopReason !== "max_tokens") {
|
|
2703
|
-
pushResultError(event, "Execution failed");
|
|
2704
|
-
}
|
|
2705
|
-
break;
|
|
2706
|
-
case "error_max_budget_usd":
|
|
2707
|
-
pushResultError(event, "Budget limit exceeded");
|
|
2708
|
-
break;
|
|
2709
|
-
case "error_max_turns":
|
|
2710
|
-
pushResultError(event, "Max turns exceeded");
|
|
2711
|
-
break;
|
|
2712
|
-
case "error_max_structured_output_retries":
|
|
2713
|
-
pushResultError(event, "Structured output retries exceeded");
|
|
2714
|
-
break;
|
|
2715
|
-
}
|
|
2716
|
-
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
2717
|
-
break;
|
|
2718
|
-
}
|
|
2719
|
-
}
|
|
2720
|
-
return events;
|
|
2856
|
+
return this.eventNormalizer.normalizeLine(line);
|
|
2721
2857
|
}
|
|
2722
2858
|
encodeStdinMessage(text, sessionId, _opts) {
|
|
2723
2859
|
return JSON.stringify({
|
|
@@ -2731,7 +2867,7 @@ var ClaudeDriver = class {
|
|
|
2731
2867
|
}
|
|
2732
2868
|
buildSystemPrompt(config, _agentId) {
|
|
2733
2869
|
return buildCliTransportSystemPrompt(config, {
|
|
2734
|
-
toolPrefix: "
|
|
2870
|
+
toolPrefix: "",
|
|
2735
2871
|
extraCriticalRules: [],
|
|
2736
2872
|
postStartupNotes: [
|
|
2737
2873
|
"**Claude runtime note:** While you are busy, Slock batches inbox-count notifications instead of injecting message content. Use `slock message check` at natural breakpoints to pull the pending messages before side-effect actions that depend on current context."
|
|
@@ -2744,8 +2880,8 @@ var ClaudeDriver = class {
|
|
|
2744
2880
|
|
|
2745
2881
|
// src/drivers/codex.ts
|
|
2746
2882
|
import { spawn as spawn2, execFileSync as execFileSync2, execSync } from "child_process";
|
|
2747
|
-
import { existsSync as
|
|
2748
|
-
import
|
|
2883
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
2884
|
+
import os2 from "os";
|
|
2749
2885
|
import path5 from "path";
|
|
2750
2886
|
|
|
2751
2887
|
// src/runtimeTurnState.ts
|
|
@@ -3107,7 +3243,7 @@ var CodexEventNormalizer = class {
|
|
|
3107
3243
|
|
|
3108
3244
|
// src/drivers/codex.ts
|
|
3109
3245
|
function ensureGitRepoForCodex(workingDirectory, deps = {}) {
|
|
3110
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3246
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
3111
3247
|
const execSyncFn = deps.execSyncFn ?? execSync;
|
|
3112
3248
|
const gitDir = path5.join(workingDirectory, ".git");
|
|
3113
3249
|
if (existsSyncFn(gitDir)) return;
|
|
@@ -3125,7 +3261,7 @@ function isWindowsSandboxRunner(commandPath) {
|
|
|
3125
3261
|
return path5.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
|
|
3126
3262
|
}
|
|
3127
3263
|
function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
3128
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3264
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
3129
3265
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
|
|
3130
3266
|
const env = deps.env ?? process.env;
|
|
3131
3267
|
const winPath = path5.win32;
|
|
@@ -3147,7 +3283,7 @@ function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
|
3147
3283
|
return null;
|
|
3148
3284
|
}
|
|
3149
3285
|
function resolveWindowsCodexDesktopEntry(deps = {}) {
|
|
3150
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3286
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
3151
3287
|
const env = deps.env ?? process.env;
|
|
3152
3288
|
const homeDir = deps.homeDir;
|
|
3153
3289
|
const winPath = path5.win32;
|
|
@@ -3299,11 +3435,15 @@ var CodexDriver = class {
|
|
|
3299
3435
|
// the daemon. They replace the previous transcript-mtime heuristic.
|
|
3300
3436
|
experimentalRawEvents: true
|
|
3301
3437
|
};
|
|
3302
|
-
|
|
3303
|
-
|
|
3438
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
3439
|
+
if (launchRuntimeFields.model) {
|
|
3440
|
+
threadParams.model = launchRuntimeFields.model;
|
|
3304
3441
|
}
|
|
3305
|
-
if (
|
|
3306
|
-
threadParams.config = { model_reasoning_effort:
|
|
3442
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
3443
|
+
threadParams.config = { model_reasoning_effort: launchRuntimeFields.reasoningEffort };
|
|
3444
|
+
}
|
|
3445
|
+
if (launchRuntimeFields.mode.kind === "fast") {
|
|
3446
|
+
threadParams.serviceTier = "priority";
|
|
3307
3447
|
}
|
|
3308
3448
|
if (ctx.config.sessionId) {
|
|
3309
3449
|
return {
|
|
@@ -3459,12 +3599,12 @@ var CodexDriver = class {
|
|
|
3459
3599
|
return detectCodexModels();
|
|
3460
3600
|
}
|
|
3461
3601
|
};
|
|
3462
|
-
function detectCodexModels(home =
|
|
3602
|
+
function detectCodexModels(home = os2.homedir()) {
|
|
3463
3603
|
const cachePath = path5.join(home, ".codex", "models_cache.json");
|
|
3464
3604
|
const configPath = path5.join(home, ".codex", "config.toml");
|
|
3465
3605
|
let models = [];
|
|
3466
3606
|
try {
|
|
3467
|
-
const raw =
|
|
3607
|
+
const raw = readFileSync2(cachePath, "utf8");
|
|
3468
3608
|
const parsed = JSON.parse(raw);
|
|
3469
3609
|
const entries = Array.isArray(parsed?.models) ? parsed.models : [];
|
|
3470
3610
|
for (const entry of entries) {
|
|
@@ -3481,7 +3621,7 @@ function detectCodexModels(home = os3.homedir()) {
|
|
|
3481
3621
|
if (models.length === 0) return null;
|
|
3482
3622
|
let defaultModel;
|
|
3483
3623
|
try {
|
|
3484
|
-
const raw =
|
|
3624
|
+
const raw = readFileSync2(configPath, "utf8");
|
|
3485
3625
|
const match = raw.match(/^\s*model\s*=\s*"([^"]+)"/m);
|
|
3486
3626
|
if (match) defaultModel = match[1];
|
|
3487
3627
|
} catch {
|
|
@@ -3512,7 +3652,7 @@ function buildAntigravityArgs(ctx) {
|
|
|
3512
3652
|
const args = [
|
|
3513
3653
|
"--print",
|
|
3514
3654
|
"--print-timeout",
|
|
3515
|
-
ctx.config.envVars?.ANTIGRAVITY_PRINT_TIMEOUT || DEFAULT_PRINT_TIMEOUT,
|
|
3655
|
+
runtimeConfigToLaunchFields(ctx.config).envVars?.ANTIGRAVITY_PRINT_TIMEOUT || DEFAULT_PRINT_TIMEOUT,
|
|
3516
3656
|
"--dangerously-skip-permissions"
|
|
3517
3657
|
];
|
|
3518
3658
|
if (ctx.config.sessionId) {
|
|
@@ -3691,11 +3831,12 @@ var CopilotDriver = class {
|
|
|
3691
3831
|
"-p",
|
|
3692
3832
|
ctx.prompt
|
|
3693
3833
|
];
|
|
3694
|
-
|
|
3695
|
-
|
|
3834
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
3835
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
3836
|
+
args.push("--model", launchRuntimeFields.model);
|
|
3696
3837
|
}
|
|
3697
|
-
if (
|
|
3698
|
-
args.push("--effort",
|
|
3838
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
3839
|
+
args.push("--effort", launchRuntimeFields.reasoningEffort);
|
|
3699
3840
|
}
|
|
3700
3841
|
if (ctx.config.sessionId) {
|
|
3701
3842
|
args.push(`--resume=${ctx.config.sessionId}`);
|
|
@@ -3795,7 +3936,7 @@ var CopilotDriver = class {
|
|
|
3795
3936
|
|
|
3796
3937
|
// src/drivers/cursor.ts
|
|
3797
3938
|
import { spawn as spawn5, spawnSync } from "child_process";
|
|
3798
|
-
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as
|
|
3939
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
3799
3940
|
import path7 from "path";
|
|
3800
3941
|
async function buildCursorSpawnEnv(ctx, deps = {}) {
|
|
3801
3942
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
@@ -3851,7 +3992,7 @@ var CursorDriver = class {
|
|
|
3851
3992
|
}
|
|
3852
3993
|
async spawn(ctx) {
|
|
3853
3994
|
const cursorDir = path7.join(ctx.workingDirectory, ".cursor");
|
|
3854
|
-
if (!
|
|
3995
|
+
if (!existsSync4(cursorDir)) {
|
|
3855
3996
|
mkdirSync2(cursorDir, { recursive: true });
|
|
3856
3997
|
}
|
|
3857
3998
|
const mcpConfigPath = path7.join(cursorDir, "mcp.json");
|
|
@@ -3864,8 +4005,9 @@ var CursorDriver = class {
|
|
|
3864
4005
|
"--approve-mcps",
|
|
3865
4006
|
"--trust"
|
|
3866
4007
|
];
|
|
3867
|
-
|
|
3868
|
-
|
|
4008
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4009
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4010
|
+
args.push("--model", launchRuntimeFields.model);
|
|
3869
4011
|
}
|
|
3870
4012
|
if (ctx.config.sessionId) {
|
|
3871
4013
|
args.push("--resume", ctx.config.sessionId);
|
|
@@ -3984,11 +4126,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
3984
4126
|
return parseCursorModelsOutput(String(result.stdout || ""));
|
|
3985
4127
|
}
|
|
3986
4128
|
function buildCursorModelProbeEnv(deps = {}) {
|
|
3987
|
-
return withWindowsUserEnvironment({
|
|
4129
|
+
return scrubDaemonChildEnv(withWindowsUserEnvironment({
|
|
3988
4130
|
...deps.env ?? process.env,
|
|
3989
4131
|
FORCE_COLOR: "0",
|
|
3990
4132
|
NO_COLOR: "1"
|
|
3991
|
-
}, deps);
|
|
4133
|
+
}, deps));
|
|
3992
4134
|
}
|
|
3993
4135
|
function runCursorModelsCommand() {
|
|
3994
4136
|
return spawnSync("cursor-agent", ["models"], {
|
|
@@ -4000,14 +4142,15 @@ function runCursorModelsCommand() {
|
|
|
4000
4142
|
|
|
4001
4143
|
// src/drivers/gemini.ts
|
|
4002
4144
|
import { execFileSync as execFileSync3, spawn as spawn6 } from "child_process";
|
|
4003
|
-
import { existsSync as
|
|
4145
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
4004
4146
|
import path8 from "path";
|
|
4005
4147
|
async function buildGeminiSpawnEnv(ctx, platform = process.platform) {
|
|
4006
4148
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
|
|
4007
|
-
|
|
4149
|
+
const launchEnvVars = runtimeConfigToLaunchFields(ctx.config).envVars;
|
|
4150
|
+
if (!Object.prototype.hasOwnProperty.call(launchEnvVars ?? {}, "GEMINI_CLI_TRUST_WORKSPACE")) {
|
|
4008
4151
|
spawnEnv.GEMINI_CLI_TRUST_WORKSPACE = "true";
|
|
4009
4152
|
}
|
|
4010
|
-
if (platform === "win32" && !Object.prototype.hasOwnProperty.call(
|
|
4153
|
+
if (platform === "win32" && !Object.prototype.hasOwnProperty.call(launchEnvVars ?? {}, "GEMINI_PTY_INFO")) {
|
|
4011
4154
|
spawnEnv.GEMINI_PTY_INFO = "child_process";
|
|
4012
4155
|
}
|
|
4013
4156
|
return spawnEnv;
|
|
@@ -4027,8 +4170,9 @@ function buildGeminiArgs(config) {
|
|
|
4027
4170
|
"-p",
|
|
4028
4171
|
""
|
|
4029
4172
|
];
|
|
4030
|
-
|
|
4031
|
-
|
|
4173
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(config);
|
|
4174
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4175
|
+
args.push("--model", launchRuntimeFields.model);
|
|
4032
4176
|
}
|
|
4033
4177
|
if (config.sessionId) {
|
|
4034
4178
|
args.push("--resume", config.sessionId);
|
|
@@ -4041,8 +4185,8 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
4041
4185
|
return { command: resolveCommandOnPath("gemini", deps) ?? "gemini", args: commandArgs };
|
|
4042
4186
|
}
|
|
4043
4187
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
4044
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
4045
|
-
const env = deps.env ?? process.env;
|
|
4188
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
4189
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
4046
4190
|
const winPath = path8.win32;
|
|
4047
4191
|
let geminiEntry = null;
|
|
4048
4192
|
try {
|
|
@@ -4214,13 +4358,16 @@ var GeminiDriver = class {
|
|
|
4214
4358
|
// src/drivers/kimi.ts
|
|
4215
4359
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4216
4360
|
import { spawn as spawn7 } from "child_process";
|
|
4217
|
-
import { existsSync as
|
|
4218
|
-
import
|
|
4361
|
+
import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
4362
|
+
import os3 from "os";
|
|
4219
4363
|
import path9 from "path";
|
|
4220
4364
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
4221
4365
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
4222
4366
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
4223
4367
|
var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
|
|
4368
|
+
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
4369
|
+
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
4370
|
+
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
4224
4371
|
function parseToolArguments(raw) {
|
|
4225
4372
|
if (typeof raw !== "string") return raw;
|
|
4226
4373
|
try {
|
|
@@ -4229,6 +4376,73 @@ function parseToolArguments(raw) {
|
|
|
4229
4376
|
return raw;
|
|
4230
4377
|
}
|
|
4231
4378
|
}
|
|
4379
|
+
function readKimiConfigSource(home = os3.homedir(), env = process.env) {
|
|
4380
|
+
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
4381
|
+
if (inlineConfig && inlineConfig.trim()) {
|
|
4382
|
+
return {
|
|
4383
|
+
raw: inlineConfig,
|
|
4384
|
+
explicitPath: null,
|
|
4385
|
+
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
4386
|
+
};
|
|
4387
|
+
}
|
|
4388
|
+
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
4389
|
+
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
|
|
4390
|
+
try {
|
|
4391
|
+
return {
|
|
4392
|
+
raw: readFileSync3(configPath, "utf8"),
|
|
4393
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
4394
|
+
sourcePath: configPath
|
|
4395
|
+
};
|
|
4396
|
+
} catch {
|
|
4397
|
+
return {
|
|
4398
|
+
raw: null,
|
|
4399
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
4400
|
+
sourcePath: configPath
|
|
4401
|
+
};
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
function buildKimiSpawnEnv(env = process.env) {
|
|
4405
|
+
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
4406
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
4407
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
4408
|
+
return scrubDaemonChildEnv(spawnEnv);
|
|
4409
|
+
}
|
|
4410
|
+
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
4411
|
+
return {
|
|
4412
|
+
...process.env,
|
|
4413
|
+
...ctx.config.envVars || {},
|
|
4414
|
+
...overrideEnv || {}
|
|
4415
|
+
};
|
|
4416
|
+
}
|
|
4417
|
+
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
4418
|
+
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
4419
|
+
const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
|
|
4420
|
+
const args = [];
|
|
4421
|
+
let configFilePath = null;
|
|
4422
|
+
let configContent = null;
|
|
4423
|
+
if (source.explicitPath) {
|
|
4424
|
+
configFilePath = source.explicitPath;
|
|
4425
|
+
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
4426
|
+
configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
4427
|
+
configContent = source.raw;
|
|
4428
|
+
if (opts.writeGeneratedConfig !== false) {
|
|
4429
|
+
writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
4430
|
+
chmodSync(configFilePath, 384);
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
if (configFilePath) {
|
|
4434
|
+
args.push("--config-file", configFilePath);
|
|
4435
|
+
}
|
|
4436
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
4437
|
+
args.push("--model", ctx.config.model);
|
|
4438
|
+
}
|
|
4439
|
+
return {
|
|
4440
|
+
args,
|
|
4441
|
+
env: buildKimiSpawnEnv(env),
|
|
4442
|
+
configFilePath,
|
|
4443
|
+
configContent
|
|
4444
|
+
};
|
|
4445
|
+
}
|
|
4232
4446
|
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
4233
4447
|
return {
|
|
4234
4448
|
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
@@ -4252,7 +4466,25 @@ var KimiDriver = class {
|
|
|
4252
4466
|
};
|
|
4253
4467
|
model = {
|
|
4254
4468
|
detectedModelsVerifiedAs: "launchable",
|
|
4255
|
-
toLaunchSpec: (modelId) =>
|
|
4469
|
+
toLaunchSpec: (modelId, ctx, opts) => {
|
|
4470
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
4471
|
+
const launchCtx = {
|
|
4472
|
+
...ctx,
|
|
4473
|
+
config: {
|
|
4474
|
+
...ctx.config,
|
|
4475
|
+
model: modelId
|
|
4476
|
+
}
|
|
4477
|
+
};
|
|
4478
|
+
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
4479
|
+
home: opts?.home,
|
|
4480
|
+
writeGeneratedConfig: false
|
|
4481
|
+
});
|
|
4482
|
+
return {
|
|
4483
|
+
args: launch.args,
|
|
4484
|
+
env: launch.env,
|
|
4485
|
+
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
4486
|
+
};
|
|
4487
|
+
}
|
|
4256
4488
|
};
|
|
4257
4489
|
supportsStdinNotification = true;
|
|
4258
4490
|
mcpToolPrefix = "";
|
|
@@ -4288,7 +4520,7 @@ var KimiDriver = class {
|
|
|
4288
4520
|
const systemPromptPath = path9.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
|
|
4289
4521
|
const agentFilePath = path9.join(ctx.workingDirectory, KIMI_AGENT_FILE);
|
|
4290
4522
|
const mcpConfigPath = path9.join(ctx.workingDirectory, KIMI_MCP_FILE);
|
|
4291
|
-
if (!isResume || !
|
|
4523
|
+
if (!isResume || !existsSync6(systemPromptPath)) {
|
|
4292
4524
|
writeFileSync6(systemPromptPath, ctx.prompt, "utf8");
|
|
4293
4525
|
}
|
|
4294
4526
|
writeFileSync6(agentFilePath, [
|
|
@@ -4306,6 +4538,7 @@ var KimiDriver = class {
|
|
|
4306
4538
|
}
|
|
4307
4539
|
}
|
|
4308
4540
|
}), "utf8");
|
|
4541
|
+
const launch = buildKimiLaunchOptions(ctx);
|
|
4309
4542
|
const args = [
|
|
4310
4543
|
"--wire",
|
|
4311
4544
|
"--yolo",
|
|
@@ -4314,14 +4547,16 @@ var KimiDriver = class {
|
|
|
4314
4547
|
"--mcp-config-file",
|
|
4315
4548
|
mcpConfigPath,
|
|
4316
4549
|
"--session",
|
|
4317
|
-
this.sessionId
|
|
4550
|
+
this.sessionId,
|
|
4551
|
+
...launch.args
|
|
4318
4552
|
];
|
|
4319
|
-
|
|
4320
|
-
|
|
4553
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4554
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4555
|
+
args.push("--model", launchRuntimeFields.model);
|
|
4321
4556
|
}
|
|
4322
4557
|
const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
4323
|
-
const
|
|
4324
|
-
const proc = spawn7(
|
|
4558
|
+
const spawnTarget = resolveKimiSpawn(args);
|
|
4559
|
+
const proc = spawn7(spawnTarget.command, spawnTarget.args, {
|
|
4325
4560
|
cwd: ctx.workingDirectory,
|
|
4326
4561
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4327
4562
|
env: spawnEnv,
|
|
@@ -4329,7 +4564,7 @@ var KimiDriver = class {
|
|
|
4329
4564
|
// and has an 8191-character command-line limit. Kimi's official
|
|
4330
4565
|
// installer/uv entrypoint is an executable, so launch it directly and
|
|
4331
4566
|
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
4332
|
-
shell:
|
|
4567
|
+
shell: spawnTarget.shell
|
|
4333
4568
|
});
|
|
4334
4569
|
proc.stdin?.write(JSON.stringify({
|
|
4335
4570
|
jsonrpc: "2.0",
|
|
@@ -4443,14 +4678,9 @@ var KimiDriver = class {
|
|
|
4443
4678
|
return detectKimiModels();
|
|
4444
4679
|
}
|
|
4445
4680
|
};
|
|
4446
|
-
function detectKimiModels(home =
|
|
4447
|
-
const
|
|
4448
|
-
|
|
4449
|
-
try {
|
|
4450
|
-
raw = readFileSync4(configPath, "utf8");
|
|
4451
|
-
} catch {
|
|
4452
|
-
return null;
|
|
4453
|
-
}
|
|
4681
|
+
function detectKimiModels(home = os3.homedir(), opts = {}) {
|
|
4682
|
+
const raw = readKimiConfigSource(home, opts.env).raw;
|
|
4683
|
+
if (raw === null) return null;
|
|
4454
4684
|
const models = [];
|
|
4455
4685
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
4456
4686
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -4471,8 +4701,8 @@ function detectKimiModels(home = os4.homedir()) {
|
|
|
4471
4701
|
|
|
4472
4702
|
// src/drivers/opencode.ts
|
|
4473
4703
|
import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
|
|
4474
|
-
import { existsSync as
|
|
4475
|
-
import
|
|
4704
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
|
|
4705
|
+
import os4 from "os";
|
|
4476
4706
|
import path10 from "path";
|
|
4477
4707
|
var CHAT_MCP_SERVER_NAME = "chat";
|
|
4478
4708
|
var CHAT_MCP_TOOL_PREFIX = `${CHAT_MCP_SERVER_NAME}_`;
|
|
@@ -4517,13 +4747,13 @@ function parseOpenCodeConfigContent(raw) {
|
|
|
4517
4747
|
return {};
|
|
4518
4748
|
}
|
|
4519
4749
|
function parseUserOpenCodeConfig(ctx) {
|
|
4520
|
-
const raw = ctx.config.envVars?.OPENCODE_CONFIG_CONTENT;
|
|
4750
|
+
const raw = runtimeConfigToLaunchFields(ctx.config).envVars?.OPENCODE_CONFIG_CONTENT;
|
|
4521
4751
|
return parseOpenCodeConfigContent(raw);
|
|
4522
4752
|
}
|
|
4523
|
-
function readLocalOpenCodeConfig(home =
|
|
4753
|
+
function readLocalOpenCodeConfig(home = os4.homedir()) {
|
|
4524
4754
|
const configPath = path10.join(home, ".config", "opencode", "opencode.json");
|
|
4525
4755
|
try {
|
|
4526
|
-
return parseOpenCodeConfigContent(
|
|
4756
|
+
return parseOpenCodeConfigContent(readFileSync4(configPath, "utf8"));
|
|
4527
4757
|
} catch {
|
|
4528
4758
|
}
|
|
4529
4759
|
return {};
|
|
@@ -4581,7 +4811,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
|
|
|
4581
4811
|
}
|
|
4582
4812
|
};
|
|
4583
4813
|
}
|
|
4584
|
-
function buildOpenCodeConfig(ctx, home =
|
|
4814
|
+
function buildOpenCodeConfig(ctx, home = os4.homedir()) {
|
|
4585
4815
|
const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
|
|
4586
4816
|
const userAgents = recordField(userConfig.agent);
|
|
4587
4817
|
const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
|
|
@@ -4606,7 +4836,7 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
|
|
|
4606
4836
|
}
|
|
4607
4837
|
};
|
|
4608
4838
|
}
|
|
4609
|
-
async function buildOpenCodeLaunchOptions(ctx, home =
|
|
4839
|
+
async function buildOpenCodeLaunchOptions(ctx, home = os4.homedir(), version = null) {
|
|
4610
4840
|
const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
4611
4841
|
const config = buildOpenCodeConfig(ctx, home);
|
|
4612
4842
|
const env = {
|
|
@@ -4622,8 +4852,9 @@ async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = n
|
|
|
4622
4852
|
"--dir",
|
|
4623
4853
|
ctx.workingDirectory
|
|
4624
4854
|
];
|
|
4625
|
-
|
|
4626
|
-
|
|
4855
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4856
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4857
|
+
args.push("--model", launchRuntimeFields.model);
|
|
4627
4858
|
}
|
|
4628
4859
|
if (requiresAgentCliFlag(version)) {
|
|
4629
4860
|
args.push("--agent", SLOCK_AGENT_NAME);
|
|
@@ -4704,7 +4935,7 @@ function formatOpenCodeLabelToken(token) {
|
|
|
4704
4935
|
if (/^\d/.test(token)) return token;
|
|
4705
4936
|
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
4706
4937
|
}
|
|
4707
|
-
function detectOpenCodeModels(home =
|
|
4938
|
+
function detectOpenCodeModels(home = os4.homedir(), runCommand = runOpenCodeModelsCommand) {
|
|
4708
4939
|
const commandResult = runCommand(home);
|
|
4709
4940
|
if (commandResult.error || commandResult.status !== 0) return null;
|
|
4710
4941
|
return parseOpenCodeModelsOutput(commandResult.stdout);
|
|
@@ -4713,7 +4944,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
4713
4944
|
const platform = deps.platform ?? process.platform;
|
|
4714
4945
|
const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
|
|
4715
4946
|
const result = spawnSyncFn("opencode", ["models"], {
|
|
4716
|
-
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
4947
|
+
env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
4717
4948
|
encoding: "utf8",
|
|
4718
4949
|
timeout: 5e3,
|
|
4719
4950
|
shell: platform === "win32"
|
|
@@ -4744,7 +4975,7 @@ function openCodeSpecForEntry(entry, commandArgs) {
|
|
|
4744
4975
|
return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
|
|
4745
4976
|
}
|
|
4746
4977
|
function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
4747
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
4978
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync7;
|
|
4748
4979
|
const execFileSyncFn = deps.execFileSyncFn;
|
|
4749
4980
|
const env = deps.env ?? process.env;
|
|
4750
4981
|
const winPath = path10.win32;
|
|
@@ -4774,7 +5005,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
|
4774
5005
|
}
|
|
4775
5006
|
function extractWindowsShimTargets(commandPath, deps = {}) {
|
|
4776
5007
|
if (!isWindowsCommandShim(commandPath)) return [];
|
|
4777
|
-
const readFileSyncFn = deps.readFileSyncFn ??
|
|
5008
|
+
const readFileSyncFn = deps.readFileSyncFn ?? readFileSync4;
|
|
4778
5009
|
const commandDir = path10.win32.dirname(commandPath);
|
|
4779
5010
|
let raw;
|
|
4780
5011
|
try {
|
|
@@ -4908,7 +5139,7 @@ var OpenCodeDriver = class {
|
|
|
4908
5139
|
if (unsupportedMessage) {
|
|
4909
5140
|
throw new Error(unsupportedMessage);
|
|
4910
5141
|
}
|
|
4911
|
-
const launch = await buildOpenCodeLaunchOptions(ctx,
|
|
5142
|
+
const launch = await buildOpenCodeLaunchOptions(ctx, os4.homedir(), version);
|
|
4912
5143
|
const spawnSpec = resolveOpenCodeSpawn(launch.args);
|
|
4913
5144
|
const proc = spawn8(spawnSpec.command, spawnSpec.args, {
|
|
4914
5145
|
cwd: ctx.workingDirectory,
|
|
@@ -4972,6 +5203,297 @@ var OpenCodeDriver = class {
|
|
|
4972
5203
|
}
|
|
4973
5204
|
};
|
|
4974
5205
|
|
|
5206
|
+
// src/drivers/pi.ts
|
|
5207
|
+
import { spawn as spawn9 } from "child_process";
|
|
5208
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
5209
|
+
import path11 from "path";
|
|
5210
|
+
import { fileURLToPath } from "url";
|
|
5211
|
+
import { getAgentDir, VERSION as PI_SDK_VERSION } from "@earendil-works/pi-coding-agent";
|
|
5212
|
+
var CHAT_MCP_TOOL_PREFIX2 = "chat_";
|
|
5213
|
+
var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
|
|
5214
|
+
var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
|
|
5215
|
+
var MIN_SUPPORTED_PI_VERSION = "0.74.0";
|
|
5216
|
+
function parseSemver2(version) {
|
|
5217
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
5218
|
+
if (!match) return null;
|
|
5219
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
5220
|
+
}
|
|
5221
|
+
function isSupportedPiVersion(version) {
|
|
5222
|
+
if (!version) return true;
|
|
5223
|
+
const actual = parseSemver2(version);
|
|
5224
|
+
const minimum = parseSemver2(MIN_SUPPORTED_PI_VERSION);
|
|
5225
|
+
if (!actual || !minimum) return true;
|
|
5226
|
+
for (let i = 0; i < 3; i += 1) {
|
|
5227
|
+
if (actual[i] > minimum[i]) return true;
|
|
5228
|
+
if (actual[i] < minimum[i]) return false;
|
|
5229
|
+
}
|
|
5230
|
+
return true;
|
|
5231
|
+
}
|
|
5232
|
+
function unsupportedPiVersionMessage(version) {
|
|
5233
|
+
if (!version || isSupportedPiVersion(version)) return null;
|
|
5234
|
+
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.`;
|
|
5235
|
+
}
|
|
5236
|
+
function probePi(version = PI_SDK_VERSION) {
|
|
5237
|
+
const unsupportedMessage = unsupportedPiVersionMessage(version);
|
|
5238
|
+
if (unsupportedMessage) {
|
|
5239
|
+
return {
|
|
5240
|
+
available: false,
|
|
5241
|
+
version: `${version} (requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION})`
|
|
5242
|
+
};
|
|
5243
|
+
}
|
|
5244
|
+
return { available: true, version };
|
|
5245
|
+
}
|
|
5246
|
+
function resolvePiSdkRunnerPath(moduleUrl = import.meta.url) {
|
|
5247
|
+
const moduleDir = path11.dirname(fileURLToPath(moduleUrl));
|
|
5248
|
+
const sourceSibling = path11.join(moduleDir, "piSdkRunner.ts");
|
|
5249
|
+
if (existsSync8(sourceSibling)) return sourceSibling;
|
|
5250
|
+
const bundledEntry = path11.join(moduleDir, "drivers", "piSdkRunner.js");
|
|
5251
|
+
if (existsSync8(bundledEntry)) return bundledEntry;
|
|
5252
|
+
return path11.join(moduleDir, "piSdkRunner.js");
|
|
5253
|
+
}
|
|
5254
|
+
function buildPiSdkNodeArgs(runnerPath = resolvePiSdkRunnerPath()) {
|
|
5255
|
+
if (runnerPath.endsWith(".ts")) {
|
|
5256
|
+
return [...process.execArgv, runnerPath];
|
|
5257
|
+
}
|
|
5258
|
+
return [runnerPath];
|
|
5259
|
+
}
|
|
5260
|
+
async function buildPiLaunchOptions(ctx, opts = {}) {
|
|
5261
|
+
const command = opts.command ?? process.execPath;
|
|
5262
|
+
const piDir = path11.join(ctx.workingDirectory, ".slock", "pi");
|
|
5263
|
+
const sessionDir = path11.join(piDir, "sessions");
|
|
5264
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
5265
|
+
const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
5266
|
+
const runnerPath = opts.runnerPath ?? resolvePiSdkRunnerPath();
|
|
5267
|
+
const agentDir = opts.agentDir ?? getAgentDir();
|
|
5268
|
+
const runnerConfigPath = path11.join(
|
|
5269
|
+
piDir,
|
|
5270
|
+
`sdk-run-${(ctx.launchId || "launch").replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`
|
|
5271
|
+
);
|
|
5272
|
+
const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
|
|
5273
|
+
const runnerConfig = {
|
|
5274
|
+
cwd: ctx.workingDirectory,
|
|
5275
|
+
agentDir,
|
|
5276
|
+
sessionDir,
|
|
5277
|
+
sessionId: ctx.config.sessionId || null,
|
|
5278
|
+
standingPrompt: ctx.standingPrompt,
|
|
5279
|
+
prompt: turnPrompt,
|
|
5280
|
+
model: ctx.config.model && ctx.config.model !== "default" ? ctx.config.model : null
|
|
5281
|
+
};
|
|
5282
|
+
writeFileSync7(runnerConfigPath, `${JSON.stringify(runnerConfig)}
|
|
5283
|
+
`, { encoding: "utf8", mode: 384 });
|
|
5284
|
+
const args = [
|
|
5285
|
+
...buildPiSdkNodeArgs(runnerPath),
|
|
5286
|
+
"--config",
|
|
5287
|
+
runnerConfigPath
|
|
5288
|
+
];
|
|
5289
|
+
return {
|
|
5290
|
+
command,
|
|
5291
|
+
args,
|
|
5292
|
+
env: slock.spawnEnv,
|
|
5293
|
+
sessionDir,
|
|
5294
|
+
agentDir,
|
|
5295
|
+
runnerConfigPath,
|
|
5296
|
+
sdkVersion: PI_SDK_VERSION
|
|
5297
|
+
};
|
|
5298
|
+
}
|
|
5299
|
+
function isSystemFirstMessageTask2(message) {
|
|
5300
|
+
return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
|
|
5301
|
+
}
|
|
5302
|
+
function buildPiSystemPrompt(config) {
|
|
5303
|
+
return buildCliTransportSystemPrompt(config, {
|
|
5304
|
+
toolPrefix: CHAT_MCP_TOOL_PREFIX2,
|
|
5305
|
+
extraCriticalRules: [
|
|
5306
|
+
"- 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."
|
|
5307
|
+
],
|
|
5308
|
+
postStartupNotes: [
|
|
5309
|
+
"**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."
|
|
5310
|
+
],
|
|
5311
|
+
includeStdinNotificationSection: false,
|
|
5312
|
+
messageNotificationStyle: "poll"
|
|
5313
|
+
});
|
|
5314
|
+
}
|
|
5315
|
+
function contentText(content) {
|
|
5316
|
+
if (!content) return "";
|
|
5317
|
+
const chunks = [];
|
|
5318
|
+
for (const item of content) {
|
|
5319
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
5320
|
+
chunks.push(item.text);
|
|
5321
|
+
}
|
|
5322
|
+
}
|
|
5323
|
+
return chunks.join("");
|
|
5324
|
+
}
|
|
5325
|
+
function apiKeyErrorMessage(line) {
|
|
5326
|
+
const trimmed = line.trim();
|
|
5327
|
+
if (!trimmed) return null;
|
|
5328
|
+
if (/no api key found/i.test(trimmed)) return trimmed;
|
|
5329
|
+
if (/api key.+required/i.test(trimmed)) return trimmed;
|
|
5330
|
+
if (/no models available/i.test(trimmed)) return trimmed;
|
|
5331
|
+
return null;
|
|
5332
|
+
}
|
|
5333
|
+
var PiDriver = class {
|
|
5334
|
+
id = "pi";
|
|
5335
|
+
lifecycle = {
|
|
5336
|
+
kind: "per_turn",
|
|
5337
|
+
start: "defer_until_concrete_message",
|
|
5338
|
+
exit: "terminate_on_turn_end",
|
|
5339
|
+
inFlightWake: "coalesce_into_pending"
|
|
5340
|
+
};
|
|
5341
|
+
communication = {
|
|
5342
|
+
chat: "slock_cli",
|
|
5343
|
+
runtimeControl: "none"
|
|
5344
|
+
};
|
|
5345
|
+
session = {
|
|
5346
|
+
recovery: "resume_or_fresh"
|
|
5347
|
+
};
|
|
5348
|
+
model = {
|
|
5349
|
+
detectedModelsVerifiedAs: "launchable",
|
|
5350
|
+
toLaunchSpec: async (modelId, ctx) => {
|
|
5351
|
+
if (!ctx) return modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] };
|
|
5352
|
+
const launchCtx = {
|
|
5353
|
+
...ctx,
|
|
5354
|
+
config: {
|
|
5355
|
+
...ctx.config,
|
|
5356
|
+
model: modelId
|
|
5357
|
+
}
|
|
5358
|
+
};
|
|
5359
|
+
const launch = await buildPiLaunchOptions(launchCtx);
|
|
5360
|
+
return {
|
|
5361
|
+
args: launch.args,
|
|
5362
|
+
env: launch.env,
|
|
5363
|
+
configFiles: [launch.runnerConfigPath],
|
|
5364
|
+
params: {
|
|
5365
|
+
agentDir: launch.agentDir,
|
|
5366
|
+
sessionDir: launch.sessionDir,
|
|
5367
|
+
sdkVersion: launch.sdkVersion,
|
|
5368
|
+
resources: "extensions/skills/prompt-templates/themes/context-files disabled by Slock policy"
|
|
5369
|
+
}
|
|
5370
|
+
};
|
|
5371
|
+
}
|
|
5372
|
+
};
|
|
5373
|
+
supportsStdinNotification = false;
|
|
5374
|
+
mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
|
|
5375
|
+
busyDeliveryMode = "none";
|
|
5376
|
+
terminateProcessOnTurnEnd = true;
|
|
5377
|
+
deferSpawnUntilMessage = true;
|
|
5378
|
+
usesSlockCliForCommunication = true;
|
|
5379
|
+
sessionId = null;
|
|
5380
|
+
sessionAnnounced = false;
|
|
5381
|
+
apiKeyErrorAnnounced = false;
|
|
5382
|
+
turnEnded = false;
|
|
5383
|
+
assistantTextByMessageId = /* @__PURE__ */ new Map();
|
|
5384
|
+
shouldDeferWakeMessage(message) {
|
|
5385
|
+
return isSystemFirstMessageTask2(message);
|
|
5386
|
+
}
|
|
5387
|
+
probe() {
|
|
5388
|
+
return probePi();
|
|
5389
|
+
}
|
|
5390
|
+
async detectModels() {
|
|
5391
|
+
return null;
|
|
5392
|
+
}
|
|
5393
|
+
async spawn(ctx) {
|
|
5394
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
5395
|
+
this.sessionAnnounced = false;
|
|
5396
|
+
this.apiKeyErrorAnnounced = false;
|
|
5397
|
+
this.turnEnded = false;
|
|
5398
|
+
this.assistantTextByMessageId.clear();
|
|
5399
|
+
const unsupportedMessage = unsupportedPiVersionMessage(PI_SDK_VERSION);
|
|
5400
|
+
if (unsupportedMessage) throw new Error(unsupportedMessage);
|
|
5401
|
+
const launch = await buildPiLaunchOptions(ctx);
|
|
5402
|
+
const proc = spawn9(launch.command, launch.args, {
|
|
5403
|
+
cwd: ctx.workingDirectory,
|
|
5404
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5405
|
+
env: launch.env,
|
|
5406
|
+
shell: false
|
|
5407
|
+
});
|
|
5408
|
+
proc.stdin?.end();
|
|
5409
|
+
return { process: proc };
|
|
5410
|
+
}
|
|
5411
|
+
parseLine(line) {
|
|
5412
|
+
let event;
|
|
5413
|
+
try {
|
|
5414
|
+
event = JSON.parse(line);
|
|
5415
|
+
} catch {
|
|
5416
|
+
if (this.apiKeyErrorAnnounced) return [];
|
|
5417
|
+
const message = apiKeyErrorMessage(line);
|
|
5418
|
+
if (!message) return [];
|
|
5419
|
+
this.apiKeyErrorAnnounced = true;
|
|
5420
|
+
this.turnEnded = true;
|
|
5421
|
+
return [
|
|
5422
|
+
{ kind: "error", message },
|
|
5423
|
+
{ kind: "turn_end", sessionId: this.sessionId || void 0 }
|
|
5424
|
+
];
|
|
5425
|
+
}
|
|
5426
|
+
const events = [];
|
|
5427
|
+
if (event.type === "session" && event.id) {
|
|
5428
|
+
this.sessionId = event.id;
|
|
5429
|
+
}
|
|
5430
|
+
if (!this.sessionAnnounced && this.sessionId) {
|
|
5431
|
+
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
5432
|
+
this.sessionAnnounced = true;
|
|
5433
|
+
}
|
|
5434
|
+
switch (event.type) {
|
|
5435
|
+
case "agent_start":
|
|
5436
|
+
case "turn_start":
|
|
5437
|
+
events.push({ kind: "thinking", text: "" });
|
|
5438
|
+
break;
|
|
5439
|
+
case "message_update":
|
|
5440
|
+
case "message_end":
|
|
5441
|
+
if (event.message?.role === "assistant") {
|
|
5442
|
+
const key = event.message.id || "current";
|
|
5443
|
+
const currentText = contentText(event.message.content);
|
|
5444
|
+
const previousText = this.assistantTextByMessageId.get(key) ?? "";
|
|
5445
|
+
if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
|
|
5446
|
+
events.push({ kind: "text", text: currentText.slice(previousText.length) });
|
|
5447
|
+
} else if (currentText && currentText !== previousText) {
|
|
5448
|
+
events.push({ kind: "text", text: currentText });
|
|
5449
|
+
}
|
|
5450
|
+
this.assistantTextByMessageId.set(key, currentText);
|
|
5451
|
+
if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
|
|
5452
|
+
events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
|
|
5453
|
+
}
|
|
5454
|
+
}
|
|
5455
|
+
break;
|
|
5456
|
+
case "tool_execution_start":
|
|
5457
|
+
events.push({
|
|
5458
|
+
kind: "tool_call",
|
|
5459
|
+
name: event.toolName || "unknown_tool",
|
|
5460
|
+
input: event.args
|
|
5461
|
+
});
|
|
5462
|
+
break;
|
|
5463
|
+
case "tool_execution_end":
|
|
5464
|
+
events.push({
|
|
5465
|
+
kind: "tool_output",
|
|
5466
|
+
name: event.toolName || "unknown_tool"
|
|
5467
|
+
});
|
|
5468
|
+
if (event.isError) {
|
|
5469
|
+
events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
|
|
5470
|
+
}
|
|
5471
|
+
break;
|
|
5472
|
+
case "compaction_start":
|
|
5473
|
+
events.push({ kind: "compaction_started" });
|
|
5474
|
+
break;
|
|
5475
|
+
case "compaction_end":
|
|
5476
|
+
events.push({ kind: "compaction_finished" });
|
|
5477
|
+
if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
|
|
5478
|
+
break;
|
|
5479
|
+
case "turn_end":
|
|
5480
|
+
case "agent_end":
|
|
5481
|
+
if (!this.turnEnded) {
|
|
5482
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
5483
|
+
this.turnEnded = true;
|
|
5484
|
+
}
|
|
5485
|
+
break;
|
|
5486
|
+
}
|
|
5487
|
+
return events;
|
|
5488
|
+
}
|
|
5489
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
5490
|
+
return null;
|
|
5491
|
+
}
|
|
5492
|
+
buildSystemPrompt(config, _agentId) {
|
|
5493
|
+
return buildPiSystemPrompt(config);
|
|
5494
|
+
}
|
|
5495
|
+
};
|
|
5496
|
+
|
|
4975
5497
|
// src/drivers/index.ts
|
|
4976
5498
|
var driverFactories = {
|
|
4977
5499
|
claude: () => new ClaudeDriver(),
|
|
@@ -4981,7 +5503,8 @@ var driverFactories = {
|
|
|
4981
5503
|
cursor: () => new CursorDriver(),
|
|
4982
5504
|
gemini: () => new GeminiDriver(),
|
|
4983
5505
|
kimi: () => new KimiDriver(),
|
|
4984
|
-
opencode: () => new OpenCodeDriver()
|
|
5506
|
+
opencode: () => new OpenCodeDriver(),
|
|
5507
|
+
pi: () => new PiDriver()
|
|
4985
5508
|
};
|
|
4986
5509
|
function getDriver(runtimeId) {
|
|
4987
5510
|
const createDriver = driverFactories[runtimeId];
|
|
@@ -4994,7 +5517,7 @@ function getDriver(runtimeId) {
|
|
|
4994
5517
|
|
|
4995
5518
|
// src/workspaces.ts
|
|
4996
5519
|
import { readdir, rm, stat } from "fs/promises";
|
|
4997
|
-
import
|
|
5520
|
+
import path12 from "path";
|
|
4998
5521
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
4999
5522
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
5000
5523
|
}
|
|
@@ -5002,7 +5525,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
5002
5525
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
5003
5526
|
return null;
|
|
5004
5527
|
}
|
|
5005
|
-
return
|
|
5528
|
+
return path12.join(dataDir, directoryName);
|
|
5006
5529
|
}
|
|
5007
5530
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
5008
5531
|
return {
|
|
@@ -5051,7 +5574,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
5051
5574
|
return summary;
|
|
5052
5575
|
}
|
|
5053
5576
|
const childSummaries = await Promise.all(
|
|
5054
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
5577
|
+
entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
|
|
5055
5578
|
);
|
|
5056
5579
|
for (const childSummary of childSummaries) {
|
|
5057
5580
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -5070,7 +5593,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
5070
5593
|
if (!entry.isDirectory()) {
|
|
5071
5594
|
return null;
|
|
5072
5595
|
}
|
|
5073
|
-
const dirPath =
|
|
5596
|
+
const dirPath = path12.join(dataDir, entry.name);
|
|
5074
5597
|
try {
|
|
5075
5598
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
5076
5599
|
return {
|
|
@@ -5500,19 +6023,19 @@ function findSessionJsonl(root, predicate) {
|
|
|
5500
6023
|
if (depth < 0 || visited >= maxEntries) return null;
|
|
5501
6024
|
let entries;
|
|
5502
6025
|
try {
|
|
5503
|
-
entries =
|
|
6026
|
+
entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
|
|
5504
6027
|
} catch {
|
|
5505
6028
|
return null;
|
|
5506
6029
|
}
|
|
5507
6030
|
for (const entry of entries) {
|
|
5508
6031
|
if (++visited > maxEntries) return null;
|
|
5509
6032
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
5510
|
-
return
|
|
6033
|
+
return path13.join(dir, entry.name);
|
|
5511
6034
|
}
|
|
5512
6035
|
for (const entry of entries) {
|
|
5513
6036
|
if (++visited > maxEntries) return null;
|
|
5514
6037
|
if (!entry.isDirectory()) continue;
|
|
5515
|
-
const found = visit(
|
|
6038
|
+
const found = visit(path13.join(dir, entry.name), depth - 1);
|
|
5516
6039
|
if (found) return found;
|
|
5517
6040
|
}
|
|
5518
6041
|
return null;
|
|
@@ -5525,10 +6048,10 @@ function safeSessionFilename(value) {
|
|
|
5525
6048
|
}
|
|
5526
6049
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
5527
6050
|
try {
|
|
5528
|
-
const dir =
|
|
5529
|
-
|
|
5530
|
-
const filePath =
|
|
5531
|
-
|
|
6051
|
+
const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
|
|
6052
|
+
mkdirSync5(dir, { recursive: true });
|
|
6053
|
+
const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
6054
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
5532
6055
|
type: "runtime_session_handoff",
|
|
5533
6056
|
runtime,
|
|
5534
6057
|
sessionId,
|
|
@@ -5546,17 +6069,17 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
5546
6069
|
return null;
|
|
5547
6070
|
}
|
|
5548
6071
|
}
|
|
5549
|
-
function resolveRuntimeSessionRef(runtime, sessionId, homeDir =
|
|
5550
|
-
const directPath =
|
|
6072
|
+
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
|
|
6073
|
+
const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
|
|
5551
6074
|
if (directPath) {
|
|
5552
6075
|
try {
|
|
5553
|
-
if (
|
|
6076
|
+
if (statSync(directPath).isFile()) {
|
|
5554
6077
|
return { label: sessionId, path: directPath, runtime, reachable: true };
|
|
5555
6078
|
}
|
|
5556
6079
|
} catch {
|
|
5557
6080
|
}
|
|
5558
6081
|
}
|
|
5559
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
6082
|
+
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;
|
|
5560
6083
|
if (!resolvedPath && fallbackDir) {
|
|
5561
6084
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
5562
6085
|
if (fallback) return fallback;
|
|
@@ -6504,7 +7027,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6504
7027
|
this.daemonApiKey = daemonApiKey;
|
|
6505
7028
|
this.serverUrl = opts.serverUrl;
|
|
6506
7029
|
this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
|
|
6507
|
-
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir ||
|
|
7030
|
+
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os5.homedir();
|
|
6508
7031
|
this.driverResolver = opts.driverResolver || getDriver;
|
|
6509
7032
|
this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
|
|
6510
7033
|
this.tracer = opts.tracer ?? noopTracer;
|
|
@@ -6916,7 +7439,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6916
7439
|
);
|
|
6917
7440
|
wakeMessage = void 0;
|
|
6918
7441
|
}
|
|
6919
|
-
const agentDataDir =
|
|
7442
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
6920
7443
|
await mkdir(agentDataDir, { recursive: true });
|
|
6921
7444
|
let runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
6922
7445
|
const legacyRuntimeProfileControl = runtimeConfig.runtimeProfileControl?.kind === "migration" ? runtimeConfig.runtimeProfileControl : null;
|
|
@@ -6930,23 +7453,23 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6930
7453
|
);
|
|
6931
7454
|
runtimeConfig = { ...runtimeConfig, runtimeProfileControl: null };
|
|
6932
7455
|
}
|
|
6933
|
-
const memoryMdPath =
|
|
7456
|
+
const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
|
|
6934
7457
|
try {
|
|
6935
7458
|
await access(memoryMdPath);
|
|
6936
7459
|
} catch {
|
|
6937
7460
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
6938
7461
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
6939
7462
|
}
|
|
6940
|
-
const notesDir =
|
|
7463
|
+
const notesDir = path13.join(agentDataDir, "notes");
|
|
6941
7464
|
await mkdir(notesDir, { recursive: true });
|
|
6942
7465
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
6943
7466
|
const seedFiles = buildOnboardingSeedFiles();
|
|
6944
7467
|
for (const { relativePath, content } of seedFiles) {
|
|
6945
|
-
const fullPath =
|
|
7468
|
+
const fullPath = path13.join(agentDataDir, relativePath);
|
|
6946
7469
|
try {
|
|
6947
7470
|
await access(fullPath);
|
|
6948
7471
|
} catch {
|
|
6949
|
-
await mkdir(
|
|
7472
|
+
await mkdir(path13.dirname(fullPath), { recursive: true });
|
|
6950
7473
|
await writeFile(fullPath, content);
|
|
6951
7474
|
}
|
|
6952
7475
|
}
|
|
@@ -7388,7 +7911,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7388
7911
|
"X-Slock-Client": "daemon-server-session-worker"
|
|
7389
7912
|
},
|
|
7390
7913
|
body: JSON.stringify({
|
|
7391
|
-
scopes: ["send", "read", "mentions", "tasks", "reactions", "server", "channels"],
|
|
7914
|
+
scopes: ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"],
|
|
7392
7915
|
name: `runner:${config.runtime}:${agentId.slice(0, 8)}`
|
|
7393
7916
|
})
|
|
7394
7917
|
});
|
|
@@ -7823,7 +8346,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7823
8346
|
return true;
|
|
7824
8347
|
}
|
|
7825
8348
|
async resetWorkspace(agentId) {
|
|
7826
|
-
const agentDataDir =
|
|
8349
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
7827
8350
|
try {
|
|
7828
8351
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
7829
8352
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -7884,7 +8407,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7884
8407
|
return result;
|
|
7885
8408
|
}
|
|
7886
8409
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
7887
|
-
const workspacePath =
|
|
8410
|
+
const workspacePath = path13.join(this.dataDir, agentId);
|
|
7888
8411
|
return {
|
|
7889
8412
|
agentId,
|
|
7890
8413
|
launchId,
|
|
@@ -8141,7 +8664,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8141
8664
|
}
|
|
8142
8665
|
// Workspace file browsing
|
|
8143
8666
|
async getFileTree(agentId, dirPath) {
|
|
8144
|
-
const agentDir =
|
|
8667
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
8145
8668
|
try {
|
|
8146
8669
|
await stat2(agentDir);
|
|
8147
8670
|
} catch {
|
|
@@ -8149,8 +8672,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8149
8672
|
}
|
|
8150
8673
|
let targetDir = agentDir;
|
|
8151
8674
|
if (dirPath) {
|
|
8152
|
-
const resolved =
|
|
8153
|
-
if (!resolved.startsWith(agentDir +
|
|
8675
|
+
const resolved = path13.resolve(agentDir, dirPath);
|
|
8676
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
8154
8677
|
return [];
|
|
8155
8678
|
}
|
|
8156
8679
|
targetDir = resolved;
|
|
@@ -8158,14 +8681,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8158
8681
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
8159
8682
|
}
|
|
8160
8683
|
async readFile(agentId, filePath) {
|
|
8161
|
-
const agentDir =
|
|
8162
|
-
const resolved =
|
|
8163
|
-
if (!resolved.startsWith(agentDir +
|
|
8684
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
8685
|
+
const resolved = path13.resolve(agentDir, filePath);
|
|
8686
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
8164
8687
|
throw new Error("Access denied");
|
|
8165
8688
|
}
|
|
8166
8689
|
const info = await stat2(resolved);
|
|
8167
8690
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
8168
|
-
const ext =
|
|
8691
|
+
const ext = path13.extname(resolved).toLowerCase();
|
|
8169
8692
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
8170
8693
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
8171
8694
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -8199,14 +8722,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8199
8722
|
async listSkills(agentId, runtimeHint) {
|
|
8200
8723
|
const agent = this.agents.get(agentId);
|
|
8201
8724
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
8202
|
-
const home =
|
|
8203
|
-
const workspaceDir =
|
|
8725
|
+
const home = os5.homedir();
|
|
8726
|
+
const workspaceDir = path13.join(this.dataDir, agentId);
|
|
8204
8727
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
8205
8728
|
const globalResults = await Promise.all(
|
|
8206
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
8729
|
+
paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
|
|
8207
8730
|
);
|
|
8208
8731
|
const workspaceResults = await Promise.all(
|
|
8209
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
8732
|
+
paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
|
|
8210
8733
|
);
|
|
8211
8734
|
const dedup = (skills) => {
|
|
8212
8735
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -8235,7 +8758,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8235
8758
|
const skills = [];
|
|
8236
8759
|
for (const entry of entries) {
|
|
8237
8760
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
8238
|
-
const skillMd =
|
|
8761
|
+
const skillMd = path13.join(dir, entry.name, "SKILL.md");
|
|
8239
8762
|
try {
|
|
8240
8763
|
const content = await readFile(skillMd, "utf-8");
|
|
8241
8764
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -8246,7 +8769,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8246
8769
|
} else if (entry.name.endsWith(".md")) {
|
|
8247
8770
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
8248
8771
|
try {
|
|
8249
|
-
const content = await readFile(
|
|
8772
|
+
const content = await readFile(path13.join(dir, entry.name), "utf-8");
|
|
8250
8773
|
const skill = this.parseSkillMd(cmdName, content);
|
|
8251
8774
|
skill.sourcePath = dir;
|
|
8252
8775
|
skills.push(skill);
|
|
@@ -9320,8 +9843,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
9320
9843
|
const nodes = [];
|
|
9321
9844
|
for (const entry of entries) {
|
|
9322
9845
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
9323
|
-
const fullPath =
|
|
9324
|
-
const relativePath =
|
|
9846
|
+
const fullPath = path13.join(dir, entry.name);
|
|
9847
|
+
const relativePath = path13.relative(rootDir, fullPath);
|
|
9325
9848
|
let info;
|
|
9326
9849
|
try {
|
|
9327
9850
|
info = await stat2(fullPath);
|
|
@@ -9626,9 +10149,9 @@ var ReminderCache = class {
|
|
|
9626
10149
|
|
|
9627
10150
|
// src/machineLock.ts
|
|
9628
10151
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
9629
|
-
import { mkdirSync as
|
|
9630
|
-
import
|
|
9631
|
-
import
|
|
10152
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
10153
|
+
import os6 from "os";
|
|
10154
|
+
import path14 from "path";
|
|
9632
10155
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
9633
10156
|
var DaemonMachineLockConflictError = class extends Error {
|
|
9634
10157
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -9650,18 +10173,18 @@ function resolveDefaultMachineStateRoot() {
|
|
|
9650
10173
|
return resolveSlockHomePath("machines");
|
|
9651
10174
|
}
|
|
9652
10175
|
function ownerPath(lockDir) {
|
|
9653
|
-
return
|
|
10176
|
+
return path14.join(lockDir, "owner.json");
|
|
9654
10177
|
}
|
|
9655
10178
|
function readOwner(lockDir) {
|
|
9656
10179
|
try {
|
|
9657
|
-
return JSON.parse(
|
|
10180
|
+
return JSON.parse(readFileSync5(ownerPath(lockDir), "utf8"));
|
|
9658
10181
|
} catch {
|
|
9659
10182
|
return null;
|
|
9660
10183
|
}
|
|
9661
10184
|
}
|
|
9662
10185
|
function lockAgeMs(lockDir) {
|
|
9663
10186
|
try {
|
|
9664
|
-
return Date.now() -
|
|
10187
|
+
return Date.now() - statSync2(lockDir).mtimeMs;
|
|
9665
10188
|
} catch {
|
|
9666
10189
|
return null;
|
|
9667
10190
|
}
|
|
@@ -9680,23 +10203,23 @@ function acquireDaemonMachineLock(options) {
|
|
|
9680
10203
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
9681
10204
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
9682
10205
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
9683
|
-
const machineDir =
|
|
9684
|
-
const lockDir =
|
|
10206
|
+
const machineDir = path14.join(rootDir, lockId);
|
|
10207
|
+
const lockDir = path14.join(machineDir, "daemon.lock");
|
|
9685
10208
|
const token = randomUUID3();
|
|
9686
|
-
|
|
10209
|
+
mkdirSync6(machineDir, { recursive: true });
|
|
9687
10210
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
9688
10211
|
try {
|
|
9689
|
-
|
|
10212
|
+
mkdirSync6(lockDir);
|
|
9690
10213
|
const owner = {
|
|
9691
10214
|
pid: process.pid,
|
|
9692
10215
|
token,
|
|
9693
|
-
hostname:
|
|
10216
|
+
hostname: os6.hostname(),
|
|
9694
10217
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9695
10218
|
serverUrl: options.serverUrl,
|
|
9696
10219
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
9697
10220
|
};
|
|
9698
10221
|
try {
|
|
9699
|
-
|
|
10222
|
+
writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
9700
10223
|
`, { mode: 384 });
|
|
9701
10224
|
} catch (err) {
|
|
9702
10225
|
rmSync3(lockDir, { recursive: true, force: true });
|
|
@@ -9733,8 +10256,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
9733
10256
|
}
|
|
9734
10257
|
|
|
9735
10258
|
// src/localTraceSink.ts
|
|
9736
|
-
import { appendFileSync, mkdirSync as
|
|
9737
|
-
import
|
|
10259
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync10 } from "fs";
|
|
10260
|
+
import path15 from "path";
|
|
9738
10261
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
9739
10262
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
9740
10263
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -9770,7 +10293,7 @@ var LocalRotatingTraceSink = class {
|
|
|
9770
10293
|
currentSize = 0;
|
|
9771
10294
|
sequence = 0;
|
|
9772
10295
|
constructor(options) {
|
|
9773
|
-
this.traceDir =
|
|
10296
|
+
this.traceDir = path15.join(options.machineDir, "traces");
|
|
9774
10297
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
9775
10298
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
9776
10299
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -9796,26 +10319,26 @@ var LocalRotatingTraceSink = class {
|
|
|
9796
10319
|
return this.currentFile;
|
|
9797
10320
|
}
|
|
9798
10321
|
ensureFile(nextBytes) {
|
|
9799
|
-
|
|
10322
|
+
mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
|
|
9800
10323
|
const nowMs = this.nowMsProvider();
|
|
9801
10324
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
9802
10325
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
9803
|
-
this.currentFile =
|
|
10326
|
+
this.currentFile = path15.join(
|
|
9804
10327
|
this.traceDir,
|
|
9805
10328
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
9806
10329
|
);
|
|
9807
|
-
|
|
9808
|
-
this.currentSize =
|
|
10330
|
+
writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
|
|
10331
|
+
this.currentSize = statSync3(this.currentFile).size;
|
|
9809
10332
|
this.currentFileOpenedAtMs = nowMs;
|
|
9810
10333
|
this.pruneOldFiles();
|
|
9811
10334
|
}
|
|
9812
10335
|
}
|
|
9813
10336
|
pruneOldFiles() {
|
|
9814
|
-
const files =
|
|
10337
|
+
const files = readdirSync2(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
|
|
9815
10338
|
const excess = files.length - this.maxFiles;
|
|
9816
10339
|
if (excess <= 0) return;
|
|
9817
10340
|
for (const file of files.slice(0, excess)) {
|
|
9818
|
-
rmSync4(
|
|
10341
|
+
rmSync4(path15.join(this.traceDir, file), { force: true });
|
|
9819
10342
|
}
|
|
9820
10343
|
}
|
|
9821
10344
|
};
|
|
@@ -9902,11 +10425,11 @@ function isDiagnosticErrorAttr(key) {
|
|
|
9902
10425
|
import { createHash as createHash5, randomUUID as randomUUID4 } from "crypto";
|
|
9903
10426
|
import { gzipSync } from "zlib";
|
|
9904
10427
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
9905
|
-
import
|
|
10428
|
+
import path16 from "path";
|
|
9906
10429
|
|
|
9907
10430
|
// src/directUploadCapability.ts
|
|
9908
|
-
function joinUrl(base,
|
|
9909
|
-
return `${base.replace(/\/+$/, "")}${
|
|
10431
|
+
function joinUrl(base, path18) {
|
|
10432
|
+
return `${base.replace(/\/+$/, "")}${path18}`;
|
|
9910
10433
|
}
|
|
9911
10434
|
function jsonHeaders(apiKey) {
|
|
9912
10435
|
return {
|
|
@@ -10125,7 +10648,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
10125
10648
|
}, nextMs);
|
|
10126
10649
|
}
|
|
10127
10650
|
async findUploadCandidates() {
|
|
10128
|
-
const traceDir =
|
|
10651
|
+
const traceDir = path16.join(this.options.machineDir, "traces");
|
|
10129
10652
|
let names;
|
|
10130
10653
|
try {
|
|
10131
10654
|
names = await readdir3(traceDir);
|
|
@@ -10137,8 +10660,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
10137
10660
|
const currentFile = this.options.currentFileProvider?.();
|
|
10138
10661
|
const candidates = [];
|
|
10139
10662
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
10140
|
-
const file =
|
|
10141
|
-
if (currentFile &&
|
|
10663
|
+
const file = path16.join(traceDir, name);
|
|
10664
|
+
if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
|
|
10142
10665
|
if (await this.isUploaded(file)) continue;
|
|
10143
10666
|
try {
|
|
10144
10667
|
const info = await stat3(file);
|
|
@@ -10212,8 +10735,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
10212
10735
|
}
|
|
10213
10736
|
}
|
|
10214
10737
|
uploadStatePath(file) {
|
|
10215
|
-
const stateDir =
|
|
10216
|
-
return
|
|
10738
|
+
const stateDir = path16.join(this.options.machineDir, "trace-uploads");
|
|
10739
|
+
return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
|
|
10217
10740
|
}
|
|
10218
10741
|
async isUploaded(file) {
|
|
10219
10742
|
try {
|
|
@@ -10225,9 +10748,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
10225
10748
|
}
|
|
10226
10749
|
async markUploaded(file, metadata) {
|
|
10227
10750
|
const stateFile = this.uploadStatePath(file);
|
|
10228
|
-
await mkdir2(
|
|
10751
|
+
await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
|
|
10229
10752
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
10230
|
-
file:
|
|
10753
|
+
file: path16.basename(file),
|
|
10231
10754
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10232
10755
|
...metadata
|
|
10233
10756
|
}, null, 2)}
|
|
@@ -10246,10 +10769,10 @@ function readPositiveIntegerEnv2(name, fallback) {
|
|
|
10246
10769
|
|
|
10247
10770
|
// src/core.ts
|
|
10248
10771
|
var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
10249
|
-
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels"];
|
|
10772
|
+
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
|
|
10250
10773
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
10251
10774
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
10252
|
-
var DAEMON_CLI_USAGE =
|
|
10775
|
+
var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
|
|
10253
10776
|
var RunnerCredentialMintError2 = class extends Error {
|
|
10254
10777
|
code;
|
|
10255
10778
|
retryable;
|
|
@@ -10285,9 +10808,9 @@ function runnerCredentialErrorDetail2(error) {
|
|
|
10285
10808
|
async function waitForRunnerCredentialRetry2() {
|
|
10286
10809
|
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
10287
10810
|
}
|
|
10288
|
-
function parseDaemonCliArgs(args) {
|
|
10811
|
+
function parseDaemonCliArgs(args, env = {}) {
|
|
10289
10812
|
let serverUrl = "";
|
|
10290
|
-
let apiKey = "";
|
|
10813
|
+
let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
|
|
10291
10814
|
for (let i = 0; i < args.length; i++) {
|
|
10292
10815
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
10293
10816
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -10304,23 +10827,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
10304
10827
|
}
|
|
10305
10828
|
}
|
|
10306
10829
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
10307
|
-
const dirname =
|
|
10308
|
-
const jsPath =
|
|
10830
|
+
const dirname = path17.dirname(fileURLToPath2(moduleUrl));
|
|
10831
|
+
const jsPath = path17.resolve(dirname, "chat-bridge.js");
|
|
10309
10832
|
try {
|
|
10310
10833
|
accessSync(jsPath);
|
|
10311
10834
|
return jsPath;
|
|
10312
10835
|
} catch {
|
|
10313
|
-
return
|
|
10836
|
+
return path17.resolve(dirname, "chat-bridge.ts");
|
|
10314
10837
|
}
|
|
10315
10838
|
}
|
|
10316
10839
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
10317
|
-
const thisDir =
|
|
10318
|
-
const bundledDistPath =
|
|
10840
|
+
const thisDir = path17.dirname(fileURLToPath2(moduleUrl));
|
|
10841
|
+
const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
|
|
10319
10842
|
try {
|
|
10320
10843
|
accessSync(bundledDistPath);
|
|
10321
10844
|
return bundledDistPath;
|
|
10322
10845
|
} catch {
|
|
10323
|
-
const workspaceDistPath =
|
|
10846
|
+
const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
10324
10847
|
accessSync(workspaceDistPath);
|
|
10325
10848
|
return workspaceDistPath;
|
|
10326
10849
|
}
|
|
@@ -10499,7 +11022,7 @@ var DaemonCore = class {
|
|
|
10499
11022
|
}
|
|
10500
11023
|
resolveMachineStateRoot() {
|
|
10501
11024
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
10502
|
-
if (this.options.dataDir) return
|
|
11025
|
+
if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
|
|
10503
11026
|
return resolveDefaultMachineStateRoot();
|
|
10504
11027
|
}
|
|
10505
11028
|
shouldEnableLocalTrace() {
|
|
@@ -10947,8 +11470,8 @@ var DaemonCore = class {
|
|
|
10947
11470
|
capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
|
|
10948
11471
|
runtimes,
|
|
10949
11472
|
runningAgents: runningAgentIds,
|
|
10950
|
-
hostname: this.options.hostname ??
|
|
10951
|
-
os: this.options.osDescription ?? `${
|
|
11473
|
+
hostname: this.options.hostname ?? os7.hostname(),
|
|
11474
|
+
os: this.options.osDescription ?? `${os7.platform()} ${os7.arch()}`,
|
|
10952
11475
|
daemonVersion: this.daemonVersion
|
|
10953
11476
|
});
|
|
10954
11477
|
this.recordDaemonTrace("daemon.ready.sent", {
|
|
@@ -11010,6 +11533,8 @@ var DaemonCore = class {
|
|
|
11010
11533
|
};
|
|
11011
11534
|
|
|
11012
11535
|
export {
|
|
11536
|
+
DAEMON_API_KEY_ENV,
|
|
11537
|
+
scrubDaemonAuthEnv,
|
|
11013
11538
|
resolveWorkspaceDirectoryPath,
|
|
11014
11539
|
scanWorkspaceDirectories,
|
|
11015
11540
|
deleteWorkspaceDirectory,
|