@slock-ai/daemon 0.54.2 → 0.55.0-play.20260529181703
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-UGD4YF4V.js} +1157 -473
- package/dist/cli/index.js +2129 -920
- 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";
|
|
@@ -881,13 +1069,14 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
|
|
|
881
1069
|
19. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--avatar-url pixel:random:<seed>\`, \`--display-name <name>\`, and \`--description <text>\`. Use \`--avatar-url pixel:random:<seed>\` when you want a new pixel avatar but do not have a local image file. Values must be non-empty. Provide at least one flag per call; multiple flags can be combined.
|
|
882
1070
|
20. **\`slock integration list\`** \u2014 List registered third-party services and this agent's active Slock Agent Logins.
|
|
883
1071
|
21. **\`slock integration login\`** \u2014 Provision or reuse this agent's login for a registered third-party service.
|
|
884
|
-
22. **\`slock
|
|
885
|
-
23. **\`slock reminder
|
|
886
|
-
24. **\`slock reminder
|
|
887
|
-
25. **\`slock reminder
|
|
888
|
-
26. **\`slock reminder
|
|
889
|
-
27. **\`slock reminder
|
|
890
|
-
28. **\`slock
|
|
1072
|
+
22. **\`slock integration env\`** \u2014 Print per-agent local CLI environment for a manifest-backed service that requires isolated HOME/XDG state.
|
|
1073
|
+
23. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
1074
|
+
24. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
|
|
1075
|
+
25. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
|
|
1076
|
+
26. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
|
|
1077
|
+
27. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
|
|
1078
|
+
28. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
|
|
1079
|
+
29. **\`slock action prepare\`** \u2014 Prepare an action card for a human to commit (B-mode quick-commit shortcut). Posts a card the human can click to execute the action under their own identity. Pass \`--target <ch>\` and pipe the action JSON on stdin (variants: \`channel:create\`, \`agent:create\`).
|
|
891
1080
|
|
|
892
1081
|
The CLI prints human-readable canonical text on success (matching the format you see in received messages and history). On failure it prints JSON to stderr:
|
|
893
1082
|
- failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
|
|
@@ -993,7 +1182,7 @@ Each channel has a **name** and optionally a **description** that define its pur
|
|
|
993
1182
|
- If unsure where something belongs, call ${serverInfoCmd} to review channel descriptions.`;
|
|
994
1183
|
const thirdPartyIntegrationsSection = isCli ? `### Third-party integrations
|
|
995
1184
|
|
|
996
|
-
If a registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app, first run \`slock integration list\` and match the app to a registered service before browsing the app. Use \`slock integration login --service <service>\` to provision or reuse your agent login for that service. If the CLI reports that the \`integration\` command is unknown, the local daemon/CLI is too old for Slock Agent Login; report that the machine must be upgraded/restarted instead of calling internal HTTP endpoints yourself. When the command returns \`Agent login ready\` or \`Already logged in\`, the agent-side login is ready. If the output includes an app URL, open that URL as the service-provided third-party app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes, call internal Slock integration endpoints directly, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`slock profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the registered service / Slock Agent Login path.` : `### Third-party integrations
|
|
1185
|
+
If a registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app, first run \`slock integration list\` and match the app to a registered service before browsing the app. Use \`slock integration login --service <service>\` to provision or reuse your agent login for that service. If the service exposes an agent behavior manifest and you need to run its local CLI, run \`slock integration env --service <service>\` before invoking that CLI; if it prints exports, apply them first so service credentials stay under a per-agent profile HOME/XDG tree instead of the host user's global HOME. If it reports that no local env is required, do not invent HOME/XDG overrides. If it fails, do not run that local CLI with the host user's HOME; report that the service manifest is unsupported. Slock does not execute commands from remote manifests automatically. If the CLI reports that the \`integration\` command is unknown, the local daemon/CLI is too old for Slock Agent Login; report that the machine must be upgraded/restarted instead of calling internal HTTP endpoints yourself. When the command returns \`Agent login ready\` or \`Already logged in\`, the agent-side login is ready. If the output includes an app URL, open that URL as the service-provided third-party app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes, call internal Slock integration endpoints directly, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`slock profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the registered service / Slock Agent Login path.` : `### Third-party integrations
|
|
997
1186
|
|
|
998
1187
|
If a registered third-party service requires login, use Slock Agent Login through the available registered-service interface instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app, first inspect the registered-service interface and match the app to a registered service before browsing the app. Once the registered-service interface reports the agent login is ready, the agent-side login is ready. If that interface provides an app URL, use it as the service-provided third-party app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow or treat internal request IDs as OAuth callback codes unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use your Slock profile view. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the registered service / Slock Agent Login path.`;
|
|
999
1188
|
const readingHistorySection = isCli ? `### Reading history
|
|
@@ -1359,6 +1548,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1359
1548
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1360
1549
|
}
|
|
1361
1550
|
|
|
1551
|
+
// src/authEnv.ts
|
|
1552
|
+
var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
|
|
1553
|
+
var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
|
|
1554
|
+
function scrubDaemonAuthEnv(env) {
|
|
1555
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1556
|
+
return env;
|
|
1557
|
+
}
|
|
1558
|
+
function scrubDaemonChildEnv(env) {
|
|
1559
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1560
|
+
delete env[SLOCK_AGENT_TOKEN_ENV];
|
|
1561
|
+
return env;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1362
1564
|
// src/agentCredentialProxy.ts
|
|
1363
1565
|
import { randomBytes } from "crypto";
|
|
1364
1566
|
import http from "http";
|
|
@@ -1368,6 +1570,17 @@ var proxyServerState = null;
|
|
|
1368
1570
|
var proxyServerStartPromise = null;
|
|
1369
1571
|
var proxyServerFactory = createProxyServer;
|
|
1370
1572
|
var DECODED_RESPONSE_HEADERS = /* @__PURE__ */ new Set(["content-encoding", "content-length", "transfer-encoding"]);
|
|
1573
|
+
var HOP_BY_HOP_REQUEST_HEADERS = /* @__PURE__ */ new Set([
|
|
1574
|
+
"connection",
|
|
1575
|
+
"keep-alive",
|
|
1576
|
+
"proxy-authenticate",
|
|
1577
|
+
"proxy-authorization",
|
|
1578
|
+
"proxy-connection",
|
|
1579
|
+
"te",
|
|
1580
|
+
"trailer",
|
|
1581
|
+
"transfer-encoding",
|
|
1582
|
+
"upgrade"
|
|
1583
|
+
]);
|
|
1371
1584
|
var LOCAL_HELD_CONTEXT_LIMIT = 3;
|
|
1372
1585
|
var AGENT_CREDENTIAL_PROXY_HOST = "127.0.0.1";
|
|
1373
1586
|
var AGENT_CREDENTIAL_PROXY_BIND_MAX_ATTEMPTS = 3;
|
|
@@ -1467,9 +1680,12 @@ async function handleProxyRequest(req, res) {
|
|
|
1467
1680
|
target = new URL2(req.url ?? "/", registration.serverUrl);
|
|
1468
1681
|
const headers = new Headers();
|
|
1469
1682
|
for (const [name, value] of Object.entries(req.headers)) {
|
|
1683
|
+
const normalizedName = name.toLowerCase();
|
|
1470
1684
|
if (value === void 0) continue;
|
|
1471
|
-
if (
|
|
1472
|
-
if (
|
|
1685
|
+
if (normalizedName === "host") continue;
|
|
1686
|
+
if (normalizedName === "authorization") continue;
|
|
1687
|
+
if (normalizedName === "content-length") continue;
|
|
1688
|
+
if (HOP_BY_HOP_REQUEST_HEADERS.has(normalizedName)) continue;
|
|
1473
1689
|
if (Array.isArray(value)) {
|
|
1474
1690
|
for (const item of value) headers.append(name, item);
|
|
1475
1691
|
} else {
|
|
@@ -1480,7 +1696,15 @@ async function handleProxyRequest(req, res) {
|
|
|
1480
1696
|
headers.set("X-Agent-Id", registration.agentId);
|
|
1481
1697
|
headers.set("X-Slock-Client", "cli");
|
|
1482
1698
|
headers.set("X-Slock-Agent-Active-Capabilities", registration.activeCapabilities);
|
|
1483
|
-
let body
|
|
1699
|
+
let body;
|
|
1700
|
+
let rawBodyBuffer;
|
|
1701
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
1702
|
+
rawBodyBuffer = await readRequestBody(req);
|
|
1703
|
+
const bodyBuffer = new ArrayBuffer(rawBodyBuffer.byteLength);
|
|
1704
|
+
new Uint8Array(bodyBuffer).set(rawBodyBuffer);
|
|
1705
|
+
body = bodyBuffer;
|
|
1706
|
+
headers.set("content-length", String(rawBodyBuffer.byteLength));
|
|
1707
|
+
}
|
|
1484
1708
|
let sendTarget;
|
|
1485
1709
|
const sideEffectAction = agentApiSideEffectAction(target.pathname);
|
|
1486
1710
|
if (method === "GET" && target.pathname === "/internal/agent-api/events") {
|
|
@@ -1492,7 +1716,7 @@ async function handleProxyRequest(req, res) {
|
|
|
1492
1716
|
}
|
|
1493
1717
|
}
|
|
1494
1718
|
if (method === "POST" && sideEffectAction) {
|
|
1495
|
-
const rawBody =
|
|
1719
|
+
const rawBody = rawBodyBuffer?.toString("utf8") ?? "";
|
|
1496
1720
|
const prepared = await prepareAgentApiSideEffectForward(registration, headers, rawBody, sideEffectAction);
|
|
1497
1721
|
if (prepared.localResponse) {
|
|
1498
1722
|
const responseText = JSON.stringify(prepared.localResponse);
|
|
@@ -1508,10 +1732,13 @@ async function handleProxyRequest(req, res) {
|
|
|
1508
1732
|
const upstream = await daemonFetch(target, {
|
|
1509
1733
|
method,
|
|
1510
1734
|
headers,
|
|
1511
|
-
body
|
|
1512
|
-
// Required by undici when forwarding a Node stream.
|
|
1513
|
-
duplex: body ? "half" : void 0
|
|
1735
|
+
body
|
|
1514
1736
|
});
|
|
1737
|
+
if (upstream.status >= 500) {
|
|
1738
|
+
registration.inboxCoordinator?.recordTransportNormalizedError?.(
|
|
1739
|
+
transportNormalizedErrorForHttpStatus(target, upstream.status, registration.launchId)
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1515
1742
|
if (shouldBufferJsonResponse(upstream, target.pathname, registration)) {
|
|
1516
1743
|
const responseText = await upstream.text();
|
|
1517
1744
|
consumeVisibleResponse(registration, target, sendTarget, responseText);
|
|
@@ -1534,9 +1761,12 @@ async function handleProxyRequest(req, res) {
|
|
|
1534
1761
|
} catch (err) {
|
|
1535
1762
|
const failure = proxyFailureForError(method, target, err);
|
|
1536
1763
|
logger.warn(
|
|
1537
|
-
`[Agent Credential Proxy] request failed (agent=${registration.agentId}, launch=${registration.launchId ?? "none"}, method=${failure.method}, path=${failure.pathname}, query_keys=${failure.queryKeys.join(",") || "none"}): ${failure.errorName}: ${failure.errorMessage}`
|
|
1764
|
+
`[Agent Credential Proxy] request failed (agent=${registration.agentId}, launch=${registration.launchId ?? "none"}, method=${failure.method}, path=${failure.pathname}, query_keys=${failure.queryKeys.join(",") || "none"}): ${failure.errorName}: ${failure.errorMessage}${failure.errorCause ? ` (cause=${failure.errorCause})` : ""}`
|
|
1538
1765
|
);
|
|
1539
1766
|
registration.inboxCoordinator?.recordProxyFailure?.(failure);
|
|
1767
|
+
registration.inboxCoordinator?.recordTransportNormalizedError?.(
|
|
1768
|
+
transportNormalizedErrorForError(target, err, registration.launchId)
|
|
1769
|
+
);
|
|
1540
1770
|
writeProxyFailureResponse(res, failure);
|
|
1541
1771
|
}
|
|
1542
1772
|
}
|
|
@@ -1555,25 +1785,125 @@ function writeProxyFailureResponse(res, failure) {
|
|
|
1555
1785
|
}
|
|
1556
1786
|
function proxyFailureForError(method, target, err) {
|
|
1557
1787
|
const queryKeys = target ? [.../* @__PURE__ */ new Set([...target.searchParams.keys()])].sort() : [];
|
|
1558
|
-
|
|
1788
|
+
const cause = err instanceof Error ? err.cause : void 0;
|
|
1789
|
+
const failure = {
|
|
1559
1790
|
method,
|
|
1560
1791
|
pathname: target?.pathname ?? "unknown",
|
|
1561
1792
|
queryKeys,
|
|
1562
1793
|
errorName: err instanceof Error ? err.name : typeof err,
|
|
1563
1794
|
errorMessage: truncateProxyErrorMessage(err instanceof Error ? err.message : String(err))
|
|
1564
1795
|
};
|
|
1796
|
+
const errorCause = describeProxyErrorCause(cause);
|
|
1797
|
+
if (errorCause) failure.errorCause = errorCause;
|
|
1798
|
+
return failure;
|
|
1799
|
+
}
|
|
1800
|
+
function describeProxyErrorCause(cause) {
|
|
1801
|
+
if (!cause) return void 0;
|
|
1802
|
+
if (cause instanceof Error) {
|
|
1803
|
+
const errorWithCode = cause;
|
|
1804
|
+
const code = typeof errorWithCode.code === "string" ? errorWithCode.code : void 0;
|
|
1805
|
+
return truncateProxyErrorMessage([code, cause.message].filter(Boolean).join(" "));
|
|
1806
|
+
}
|
|
1807
|
+
if (typeof cause === "object") {
|
|
1808
|
+
const causeObject = cause;
|
|
1809
|
+
const code = typeof causeObject.code === "string" ? causeObject.code : void 0;
|
|
1810
|
+
const message = typeof causeObject.message === "string" ? causeObject.message : void 0;
|
|
1811
|
+
const detail = [code, message].filter(Boolean).join(" ");
|
|
1812
|
+
if (detail) return truncateProxyErrorMessage(detail);
|
|
1813
|
+
}
|
|
1814
|
+
return truncateProxyErrorMessage(String(cause));
|
|
1565
1815
|
}
|
|
1566
1816
|
function truncateProxyErrorMessage(message) {
|
|
1567
1817
|
const normalized = message.replace(/\s+/g, " ").trim();
|
|
1568
1818
|
return normalized.length > 500 ? `${normalized.slice(0, 497)}...` : normalized;
|
|
1569
1819
|
}
|
|
1820
|
+
function transportNormalizedErrorForHttpStatus(target, status, launchId) {
|
|
1821
|
+
return {
|
|
1822
|
+
normalizedCode: "server_5xx",
|
|
1823
|
+
routeFamily: routeFamilyForPath(target.pathname),
|
|
1824
|
+
responseStarted: true,
|
|
1825
|
+
upstreamLayer: "http_status",
|
|
1826
|
+
upstreamStatus: status,
|
|
1827
|
+
launchId,
|
|
1828
|
+
targetHostClass: daemonUpstreamTargetHostClass(target),
|
|
1829
|
+
// Today the local credential proxy is only used by CLI wrappers. If runtime
|
|
1830
|
+
// or daemon-internal callers use it later, plumb the caller identity here.
|
|
1831
|
+
downstreamCaller: "cli",
|
|
1832
|
+
upstream: "server"
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
function transportNormalizedErrorForError(target, err, launchId) {
|
|
1836
|
+
return {
|
|
1837
|
+
normalizedCode: "transport_failure",
|
|
1838
|
+
routeFamily: routeFamilyForPath(target?.pathname ?? "unknown"),
|
|
1839
|
+
responseStarted: false,
|
|
1840
|
+
upstreamLayer: target ? upstreamLayerForProxyError(err) : "unknown",
|
|
1841
|
+
originalMessage: sanitizeTransportOriginalMessage(err instanceof Error ? err.message : String(err)),
|
|
1842
|
+
launchId,
|
|
1843
|
+
targetHostClass: target ? daemonUpstreamTargetHostClass(target) : "custom_server",
|
|
1844
|
+
// Today the local credential proxy is only used by CLI wrappers. If runtime
|
|
1845
|
+
// or daemon-internal callers use it later, plumb the caller identity here.
|
|
1846
|
+
downstreamCaller: "cli",
|
|
1847
|
+
upstream: "server"
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
function routeFamilyForPath(pathname) {
|
|
1851
|
+
if (pathname === "/internal/agent-api/send") return "agent-api/send";
|
|
1852
|
+
if (pathname === "/internal/agent-api/events") return "agent-api/events";
|
|
1853
|
+
if (pathname === "/internal/agent-api/receive-ack") return "agent-api/events";
|
|
1854
|
+
if (pathname === "/internal/agent-api/tasks/claim") return "tasks/claim";
|
|
1855
|
+
if (pathname === "/internal/agent-api/tasks/update-status") return "tasks/update";
|
|
1856
|
+
if (pathname === "/internal/agent-api/tasks" || pathname.startsWith("/internal/agent-api/tasks/")) return "tasks";
|
|
1857
|
+
if (pathname.startsWith("/internal/agent-api/attachments/")) return "agent-api/attachments";
|
|
1858
|
+
if (/^\/internal\/agent-api\/messages\/[^/]+\/reactions$/.test(pathname)) return "agent-api/messages/reactions";
|
|
1859
|
+
if (pathname === "/internal/agent-api/server") return "server";
|
|
1860
|
+
if (pathname.startsWith("/internal/agent-api/history")) return "agent-api/events";
|
|
1861
|
+
if (pathname.startsWith("/internal/agent-api/search")) return "agent-api/events";
|
|
1862
|
+
if (pathname.startsWith("/internal/agent-api/channel-members")) return "channel-members";
|
|
1863
|
+
if (pathname.startsWith("/internal/agent-api/knowledge")) return "knowledge";
|
|
1864
|
+
if (pathname === "/internal/agent-api/profile" || pathname.startsWith("/internal/agent-api/profile/")) return "profile";
|
|
1865
|
+
if (pathname === "/internal/agent-api/integrations" || pathname.startsWith("/internal/agent-api/integrations/")) return "integrations";
|
|
1866
|
+
if (pathname === "/internal/agent-api/upload") return "attachments/upload";
|
|
1867
|
+
if (pathname === "/internal/agent-api/resolve-channel") return "resolve-channel";
|
|
1868
|
+
if (pathname === "/internal/agent-api/threads/unfollow") return "threads/unfollow";
|
|
1869
|
+
if (pathname === "/internal/agent-api/prepare-action") return "action/prepare";
|
|
1870
|
+
if (pathname === "/internal/agent-api/reminders" || pathname.startsWith("/internal/agent-api/reminders/")) return "reminders";
|
|
1871
|
+
if (/^\/internal\/agent-api\/channels\/[^/]+\/join$/.test(pathname)) return "channels/join";
|
|
1872
|
+
if (/^\/internal\/agent-api\/channels\/[^/]+\/leave$/.test(pathname)) return "channels/leave";
|
|
1873
|
+
return "unknown";
|
|
1874
|
+
}
|
|
1875
|
+
function daemonUpstreamTargetHostClass(url) {
|
|
1876
|
+
const hostname = url.hostname.toLowerCase();
|
|
1877
|
+
if (hostname === "api.slock.ai") return "api.slock.ai";
|
|
1878
|
+
return "custom_server";
|
|
1879
|
+
}
|
|
1880
|
+
function upstreamLayerForProxyError(err) {
|
|
1881
|
+
const code = errorCode(err).toUpperCase();
|
|
1882
|
+
const message = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
1883
|
+
if (code === "ENOTFOUND" || code === "EAI_AGAIN" || message.includes("dns")) return "dns";
|
|
1884
|
+
if (code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "UND_ERR_SOCKET" || message.includes("socket")) return "tcp";
|
|
1885
|
+
if (code.includes("TLS") || message.includes("certificate") || message.includes("tls")) return "tls";
|
|
1886
|
+
if (code === "UND_ERR_HEADERS_TIMEOUT" || code === "UND_ERR_BODY_TIMEOUT" || message.includes("timeout")) return "read_timeout";
|
|
1887
|
+
if (message.includes("proxy")) return "proxy_connect";
|
|
1888
|
+
if (message.includes("fly")) return "fly_edge";
|
|
1889
|
+
return "unknown";
|
|
1890
|
+
}
|
|
1891
|
+
function errorCode(err) {
|
|
1892
|
+
if (typeof err === "object" && err && "code" in err && typeof err.code === "string") {
|
|
1893
|
+
return err.code;
|
|
1894
|
+
}
|
|
1895
|
+
if (typeof err === "object" && err && "cause" in err) return errorCode(err.cause);
|
|
1896
|
+
return "";
|
|
1897
|
+
}
|
|
1898
|
+
function sanitizeTransportOriginalMessage(message) {
|
|
1899
|
+
return truncateProxyErrorMessage(message).replace(/sk_(?:agent|machine|computer)_[A-Za-z0-9_-]+/g, "sk_[redacted]").replace(/sap_[A-Za-z0-9_-]+/g, "sap_[redacted]").replace(/https?:\/\/\S+/g, "[url]");
|
|
1900
|
+
}
|
|
1570
1901
|
async function readRequestBody(req) {
|
|
1571
|
-
|
|
1572
|
-
req.setEncoding("utf8");
|
|
1902
|
+
const chunks = [];
|
|
1573
1903
|
for await (const chunk of req) {
|
|
1574
|
-
|
|
1904
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
1575
1905
|
}
|
|
1576
|
-
return
|
|
1906
|
+
return Buffer.concat(chunks);
|
|
1577
1907
|
}
|
|
1578
1908
|
function messageSeq(message) {
|
|
1579
1909
|
return Number(message.seq ?? 0);
|
|
@@ -1748,7 +2078,7 @@ async function loadRecentTargetMessages(registration, headers, target) {
|
|
|
1748
2078
|
const historyHeaders = new Headers(headers);
|
|
1749
2079
|
historyHeaders.delete("content-length");
|
|
1750
2080
|
historyHeaders.delete("content-type");
|
|
1751
|
-
const res = await
|
|
2081
|
+
const res = await fetch(historyUrl, { method: "GET", headers: historyHeaders });
|
|
1752
2082
|
if (!res.ok) return [];
|
|
1753
2083
|
const parsed = await res.json().catch(() => null);
|
|
1754
2084
|
return Array.isArray(parsed?.messages) ? normalizeVisibleMessages(parsed.messages, target) : [];
|
|
@@ -1949,11 +2279,14 @@ function unregisterAgentCredentialProxyForLaunch(input) {
|
|
|
1949
2279
|
// src/drivers/cliTransport.ts
|
|
1950
2280
|
var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1951
2281
|
var powershellSingleQuote = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
1952
|
-
var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels";
|
|
2282
|
+
var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels,knowledge";
|
|
1953
2283
|
var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
|
|
2284
|
+
var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
|
|
1954
2285
|
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
1955
2286
|
var RAW_CREDENTIAL_ENV_DENYLIST = [
|
|
1956
|
-
"
|
|
2287
|
+
"SLOCK_AGENT_TOKEN",
|
|
2288
|
+
"SLOCK_AGENT_CREDENTIAL_KEY",
|
|
2289
|
+
"SLOCK_AGENT_CREDENTIAL_KEY_FILE"
|
|
1957
2290
|
];
|
|
1958
2291
|
var cachedOpencliBinPath;
|
|
1959
2292
|
function resolveOpencliBinPath() {
|
|
@@ -2152,21 +2485,23 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2152
2485
|
}
|
|
2153
2486
|
}
|
|
2154
2487
|
const wrapperPath = platform === "win32" ? path2.join(slockDir, "slock.cmd") : posixWrapper;
|
|
2488
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
2155
2489
|
const spawnEnv = {
|
|
2156
2490
|
...process.env,
|
|
2157
2491
|
FORCE_COLOR: "0",
|
|
2158
|
-
...
|
|
2492
|
+
...launchRuntimeFields.envVars || {},
|
|
2159
2493
|
...extraEnv,
|
|
2160
2494
|
...platform === "win32" ? windowsUtf8Env() : {},
|
|
2161
2495
|
...runtimeContextEnv(ctx.config),
|
|
2162
2496
|
[SLOCK_HOME_ENV]: slockHome,
|
|
2163
2497
|
SLOCK_AGENT_ID: ctx.agentId,
|
|
2164
2498
|
...ctx.launchId ? { SLOCK_AGENT_LAUNCH_ID: ctx.launchId } : {},
|
|
2499
|
+
...ctx.cliTransportTraceDir ? { [CLI_TRANSPORT_TRACE_DIR_ENV]: ctx.cliTransportTraceDir } : {},
|
|
2165
2500
|
SLOCK_SERVER_URL: ctx.config.serverUrl,
|
|
2166
2501
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
2167
2502
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
2168
2503
|
};
|
|
2169
|
-
|
|
2504
|
+
scrubDaemonChildEnv(spawnEnv);
|
|
2170
2505
|
for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
|
|
2171
2506
|
delete spawnEnv[key];
|
|
2172
2507
|
}
|
|
@@ -2186,6 +2521,129 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2186
2521
|
};
|
|
2187
2522
|
}
|
|
2188
2523
|
|
|
2524
|
+
// src/drivers/claudeEventNormalizer.ts
|
|
2525
|
+
function collectResultErrorDetail(message, fallback) {
|
|
2526
|
+
const parts = [];
|
|
2527
|
+
if (Array.isArray(message.errors)) {
|
|
2528
|
+
for (const err of message.errors) {
|
|
2529
|
+
if (typeof err === "string" && err.trim()) parts.push(err.trim());
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
if (typeof message.result === "string" && message.result.trim()) {
|
|
2533
|
+
parts.push(message.result.trim());
|
|
2534
|
+
}
|
|
2535
|
+
return parts.join(" | ") || fallback;
|
|
2536
|
+
}
|
|
2537
|
+
function isProviderApiFailureText(value, hasToolUse) {
|
|
2538
|
+
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));
|
|
2539
|
+
}
|
|
2540
|
+
var ClaudeEventNormalizer = class {
|
|
2541
|
+
normalizeLine(line) {
|
|
2542
|
+
let event;
|
|
2543
|
+
try {
|
|
2544
|
+
event = JSON.parse(line);
|
|
2545
|
+
} catch {
|
|
2546
|
+
return [];
|
|
2547
|
+
}
|
|
2548
|
+
const events = [];
|
|
2549
|
+
const pushResultError = (message, fallback) => {
|
|
2550
|
+
events.push({ kind: "error", message: collectResultErrorDetail(message, fallback) });
|
|
2551
|
+
};
|
|
2552
|
+
switch (event.type) {
|
|
2553
|
+
case "system":
|
|
2554
|
+
if (event.subtype === "init" && event.session_id) {
|
|
2555
|
+
events.push({ kind: "session_init", sessionId: event.session_id });
|
|
2556
|
+
}
|
|
2557
|
+
if (event.subtype === "status" && event.status === "compacting") {
|
|
2558
|
+
events.push({ kind: "compaction_started" });
|
|
2559
|
+
}
|
|
2560
|
+
if (event.subtype === "status" && event.status === "requesting") {
|
|
2561
|
+
events.push({
|
|
2562
|
+
kind: "internal_progress",
|
|
2563
|
+
source: "claude_system_status",
|
|
2564
|
+
itemType: "requesting",
|
|
2565
|
+
payloadBytes: Buffer.byteLength(line, "utf8")
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
if (event.subtype === "compact_boundary") {
|
|
2569
|
+
events.push({ kind: "compaction_finished" });
|
|
2570
|
+
}
|
|
2571
|
+
break;
|
|
2572
|
+
case "stream_event":
|
|
2573
|
+
events.push({
|
|
2574
|
+
kind: "internal_progress",
|
|
2575
|
+
source: "claude_stream_event",
|
|
2576
|
+
itemType: typeof event.event?.type === "string" && event.event.type.length > 0 ? event.event.type : "unknown",
|
|
2577
|
+
payloadBytes: Buffer.byteLength(line, "utf8")
|
|
2578
|
+
});
|
|
2579
|
+
break;
|
|
2580
|
+
case "assistant": {
|
|
2581
|
+
const content = event.message?.content;
|
|
2582
|
+
if (Array.isArray(content)) {
|
|
2583
|
+
const hasToolUse = content.some((block) => block?.type === "tool_use");
|
|
2584
|
+
for (const block of content) {
|
|
2585
|
+
if (block.type === "thinking" && block.thinking) {
|
|
2586
|
+
events.push({ kind: "thinking", text: block.thinking });
|
|
2587
|
+
} else if (block.type === "text" && block.text) {
|
|
2588
|
+
if (isProviderApiFailureText(block.text, hasToolUse)) {
|
|
2589
|
+
events.push({ kind: "error", message: block.text });
|
|
2590
|
+
} else {
|
|
2591
|
+
events.push({ kind: "text", text: block.text });
|
|
2592
|
+
}
|
|
2593
|
+
} else if (block.type === "tool_use") {
|
|
2594
|
+
events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
break;
|
|
2599
|
+
}
|
|
2600
|
+
case "user": {
|
|
2601
|
+
const content = event.message?.content;
|
|
2602
|
+
if (Array.isArray(content)) {
|
|
2603
|
+
for (const block of content) {
|
|
2604
|
+
if (block.type === "tool_result") {
|
|
2605
|
+
events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result" });
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
break;
|
|
2610
|
+
}
|
|
2611
|
+
case "result": {
|
|
2612
|
+
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
2613
|
+
const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
|
|
2614
|
+
switch (subtype) {
|
|
2615
|
+
case "success":
|
|
2616
|
+
if (event.is_error && stopReason !== "max_tokens") {
|
|
2617
|
+
pushResultError(event, "Execution failed");
|
|
2618
|
+
}
|
|
2619
|
+
break;
|
|
2620
|
+
case "error_during_execution":
|
|
2621
|
+
if (stopReason !== "max_tokens") {
|
|
2622
|
+
pushResultError(event, "Execution failed");
|
|
2623
|
+
}
|
|
2624
|
+
break;
|
|
2625
|
+
case "error_max_budget_usd":
|
|
2626
|
+
pushResultError(event, "Budget limit exceeded");
|
|
2627
|
+
break;
|
|
2628
|
+
case "error_max_turns":
|
|
2629
|
+
pushResultError(event, "Max turns exceeded");
|
|
2630
|
+
break;
|
|
2631
|
+
case "error_max_structured_output_retries":
|
|
2632
|
+
pushResultError(event, "Structured output retries exceeded");
|
|
2633
|
+
break;
|
|
2634
|
+
}
|
|
2635
|
+
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
2636
|
+
break;
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
return events;
|
|
2640
|
+
}
|
|
2641
|
+
};
|
|
2642
|
+
|
|
2643
|
+
// src/drivers/claudeLaunch.ts
|
|
2644
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
2645
|
+
import path4 from "path";
|
|
2646
|
+
|
|
2189
2647
|
// src/drivers/probe.ts
|
|
2190
2648
|
import { execFileSync } from "child_process";
|
|
2191
2649
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -2342,7 +2800,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
|
|
|
2342
2800
|
}
|
|
2343
2801
|
function resolveCommandOnPath(command, deps = {}) {
|
|
2344
2802
|
const platform = deps.platform ?? process.platform;
|
|
2345
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
2803
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
2346
2804
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
2347
2805
|
const existsSyncFn = deps.existsSyncFn ?? existsSync2;
|
|
2348
2806
|
if (platform === "win32") {
|
|
@@ -2368,7 +2826,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
2368
2826
|
return null;
|
|
2369
2827
|
}
|
|
2370
2828
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
2371
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
2829
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
2372
2830
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
2373
2831
|
try {
|
|
2374
2832
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -2386,12 +2844,10 @@ function resolveHomePath(relativePath, deps = {}) {
|
|
|
2386
2844
|
return path3.join(homeDir, relativePath);
|
|
2387
2845
|
}
|
|
2388
2846
|
|
|
2389
|
-
// src/drivers/
|
|
2847
|
+
// src/drivers/claudeLaunch.ts
|
|
2390
2848
|
var CLAUDE_DESKTOP_CLI_RELATIVE_PATH = path4.join("Applications", "Claude Code URL Handler.app", "Contents", "MacOS", "claude");
|
|
2391
2849
|
var CLAUDE_DESKTOP_CLI_SYSTEM_PATH = "/Applications/Claude Code URL Handler.app/Contents/MacOS/claude";
|
|
2392
2850
|
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
2851
|
var CLAUDE_DISALLOWED_TOOLS = [
|
|
2396
2852
|
"EnterPlanMode",
|
|
2397
2853
|
"ExitPlanMode",
|
|
@@ -2417,81 +2873,54 @@ function probeClaude(deps = {}) {
|
|
|
2417
2873
|
version: readCommandVersion(command, [], deps) ?? void 0
|
|
2418
2874
|
};
|
|
2419
2875
|
}
|
|
2420
|
-
function
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2876
|
+
function buildClaudeArgs(config, opts) {
|
|
2877
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(config);
|
|
2878
|
+
const args = [
|
|
2879
|
+
"--allow-dangerously-skip-permissions",
|
|
2880
|
+
"--dangerously-skip-permissions",
|
|
2881
|
+
"--verbose",
|
|
2882
|
+
"--permission-mode",
|
|
2883
|
+
"bypassPermissions",
|
|
2884
|
+
"--output-format",
|
|
2885
|
+
"stream-json",
|
|
2886
|
+
"--input-format",
|
|
2887
|
+
"stream-json",
|
|
2888
|
+
"--include-partial-messages",
|
|
2889
|
+
"--model",
|
|
2890
|
+
launchRuntimeFields.model || "sonnet",
|
|
2891
|
+
"--disallowed-tools",
|
|
2892
|
+
CLAUDE_DISALLOWED_TOOLS,
|
|
2893
|
+
"--append-system-prompt-file",
|
|
2894
|
+
opts.standingPromptFilePath
|
|
2895
|
+
];
|
|
2896
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
2897
|
+
args.push("--effort", launchRuntimeFields.reasoningEffort);
|
|
2428
2898
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
function readClaudeMcpServers(configPath, vars = {}) {
|
|
2432
|
-
try {
|
|
2433
|
-
const parsed = JSON.parse(
|
|
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;
|
|
2899
|
+
if (launchRuntimeFields.mode.kind === "fast") {
|
|
2900
|
+
args.push("--bare");
|
|
2443
2901
|
}
|
|
2902
|
+
if (config.sessionId) {
|
|
2903
|
+
args.push("--resume", config.sessionId);
|
|
2904
|
+
}
|
|
2905
|
+
return args;
|
|
2444
2906
|
}
|
|
2445
|
-
function
|
|
2446
|
-
const
|
|
2447
|
-
|
|
2907
|
+
function writeClaudeSystemPromptFile(standingPrompt, slockDir) {
|
|
2908
|
+
const systemPromptPath = path4.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
|
|
2909
|
+
writeFileSync2(systemPromptPath, standingPrompt, { mode: 384 });
|
|
2910
|
+
return systemPromptPath;
|
|
2448
2911
|
}
|
|
2449
|
-
function
|
|
2450
|
-
const
|
|
2451
|
-
const
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
}
|
|
2912
|
+
function buildClaudeSpawnSpec(claudeCommand, platform = process.platform) {
|
|
2913
|
+
const lowerClaudeCommand = claudeCommand?.toLowerCase();
|
|
2914
|
+
const isBatchFile = Boolean(
|
|
2915
|
+
platform === "win32" && lowerClaudeCommand && (lowerClaudeCommand.endsWith(".cmd") || lowerClaudeCommand.endsWith(".bat"))
|
|
2916
|
+
);
|
|
2917
|
+
return {
|
|
2918
|
+
command: claudeCommand ?? "claude",
|
|
2919
|
+
shell: platform === "win32" && (!claudeCommand || isBatchFile)
|
|
2458
2920
|
};
|
|
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
2921
|
}
|
|
2922
|
+
|
|
2923
|
+
// src/drivers/claude.ts
|
|
2495
2924
|
var ClaudeDriver = class {
|
|
2496
2925
|
id = "claude";
|
|
2497
2926
|
lifecycle = {
|
|
@@ -2501,223 +2930,61 @@ var ClaudeDriver = class {
|
|
|
2501
2930
|
};
|
|
2502
2931
|
communication = {
|
|
2503
2932
|
chat: "slock_cli",
|
|
2504
|
-
runtimeControl: "
|
|
2933
|
+
runtimeControl: "none"
|
|
2505
2934
|
};
|
|
2506
2935
|
session = {
|
|
2507
2936
|
recovery: "resume_or_fresh"
|
|
2508
2937
|
};
|
|
2509
2938
|
model = {
|
|
2510
|
-
detectedModelsVerifiedAs: "launchable",
|
|
2511
|
-
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
2512
|
-
};
|
|
2513
|
-
supportsStdinNotification = true;
|
|
2514
|
-
mcpToolPrefix = "
|
|
2515
|
-
usesSlockCliForCommunication = true;
|
|
2516
|
-
// Claude Code supports same-turn steering, but raw stdin injection at an
|
|
2517
|
-
// arbitrary busy instant can collide with active signed thinking blocks. The
|
|
2518
|
-
// daemon therefore gates busy delivery on Claude stream-json boundaries.
|
|
2519
|
-
busyDeliveryMode = "gated";
|
|
2520
|
-
supportsNativeStandingPrompt = true;
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
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 };
|
|
2591
|
-
}
|
|
2592
|
-
async spawn(ctx) {
|
|
2593
|
-
const { slockDir, tokenFile, spawnEnv } = await prepareCliTransport(ctx);
|
|
2594
|
-
const { systemPromptPath, mcpConfigPath } = this.writeClaudeLaunchFiles(ctx, slockDir);
|
|
2595
|
-
const args = this.buildClaudeArgs(ctx.config, ctx.standingPrompt, {
|
|
2596
|
-
standingPromptFilePath: systemPromptPath
|
|
2597
|
-
});
|
|
2598
|
-
args.push("--mcp-config", mcpConfigPath, "--strict-mcp-config");
|
|
2599
|
-
delete spawnEnv.CLAUDECODE;
|
|
2600
|
-
logger.info(
|
|
2601
|
-
`[Agent ${ctx.agentId}] transport=cli cli=${ctx.slockCliPath} token_file=${tokenFile}`
|
|
2602
|
-
);
|
|
2603
|
-
const claudeCommand = resolveClaudeCommand();
|
|
2604
|
-
const isWindows = process.platform === "win32";
|
|
2605
|
-
const lowerClaudeCommand = claudeCommand?.toLowerCase();
|
|
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, {
|
|
2611
|
-
cwd: ctx.workingDirectory,
|
|
2612
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
2613
|
-
env: spawnEnv,
|
|
2614
|
-
shell: useShell
|
|
2615
|
-
});
|
|
2616
|
-
const stdinMsg = JSON.stringify({
|
|
2617
|
-
type: "user",
|
|
2618
|
-
message: {
|
|
2619
|
-
role: "user",
|
|
2620
|
-
content: [{ type: "text", text: ctx.prompt }]
|
|
2621
|
-
},
|
|
2622
|
-
...ctx.config.sessionId ? { session_id: ctx.config.sessionId } : {}
|
|
2623
|
-
});
|
|
2624
|
-
proc.stdin?.write(stdinMsg + "\n");
|
|
2625
|
-
return { process: proc };
|
|
2626
|
-
}
|
|
2627
|
-
parseLine(line) {
|
|
2628
|
-
let event;
|
|
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;
|
|
2939
|
+
detectedModelsVerifiedAs: "launchable",
|
|
2940
|
+
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
2941
|
+
};
|
|
2942
|
+
supportsStdinNotification = true;
|
|
2943
|
+
mcpToolPrefix = "";
|
|
2944
|
+
usesSlockCliForCommunication = true;
|
|
2945
|
+
// Claude Code supports same-turn steering, but raw stdin injection at an
|
|
2946
|
+
// arbitrary busy instant can collide with active signed thinking blocks. The
|
|
2947
|
+
// daemon therefore gates busy delivery on Claude stream-json boundaries.
|
|
2948
|
+
busyDeliveryMode = "gated";
|
|
2949
|
+
supportsNativeStandingPrompt = true;
|
|
2950
|
+
eventNormalizer = new ClaudeEventNormalizer();
|
|
2951
|
+
probe() {
|
|
2952
|
+
return probeClaude();
|
|
2953
|
+
}
|
|
2954
|
+
buildClaudeArgs(config, opts) {
|
|
2955
|
+
return buildClaudeArgs(config, opts);
|
|
2956
|
+
}
|
|
2957
|
+
async spawn(ctx) {
|
|
2958
|
+
const { slockDir, tokenFile, spawnEnv } = await prepareCliTransport(ctx);
|
|
2959
|
+
const systemPromptPath = writeClaudeSystemPromptFile(ctx.standingPrompt, slockDir);
|
|
2960
|
+
const args = this.buildClaudeArgs(ctx.config, {
|
|
2961
|
+
standingPromptFilePath: systemPromptPath
|
|
2962
|
+
});
|
|
2963
|
+
delete spawnEnv.CLAUDECODE;
|
|
2964
|
+
logger.info(
|
|
2965
|
+
`[Agent ${ctx.agentId}] transport=cli cli=${ctx.slockCliPath} token_file=${tokenFile}`
|
|
2966
|
+
);
|
|
2967
|
+
const claudeCommand = resolveClaudeCommand();
|
|
2968
|
+
const spawnSpec = buildClaudeSpawnSpec(claudeCommand);
|
|
2969
|
+
const proc = spawn(spawnSpec.command, args, {
|
|
2970
|
+
cwd: ctx.workingDirectory,
|
|
2971
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
2972
|
+
env: spawnEnv,
|
|
2973
|
+
shell: spawnSpec.shell
|
|
2974
|
+
});
|
|
2975
|
+
const stdinMsg = JSON.stringify({
|
|
2976
|
+
type: "user",
|
|
2977
|
+
message: {
|
|
2978
|
+
role: "user",
|
|
2979
|
+
content: [{ type: "text", text: ctx.prompt }]
|
|
2980
|
+
},
|
|
2981
|
+
...ctx.config.sessionId ? { session_id: ctx.config.sessionId } : {}
|
|
2982
|
+
});
|
|
2983
|
+
proc.stdin?.write(stdinMsg + "\n");
|
|
2984
|
+
return { process: proc };
|
|
2985
|
+
}
|
|
2986
|
+
parseLine(line) {
|
|
2987
|
+
return this.eventNormalizer.normalizeLine(line);
|
|
2721
2988
|
}
|
|
2722
2989
|
encodeStdinMessage(text, sessionId, _opts) {
|
|
2723
2990
|
return JSON.stringify({
|
|
@@ -2731,7 +2998,7 @@ var ClaudeDriver = class {
|
|
|
2731
2998
|
}
|
|
2732
2999
|
buildSystemPrompt(config, _agentId) {
|
|
2733
3000
|
return buildCliTransportSystemPrompt(config, {
|
|
2734
|
-
toolPrefix: "
|
|
3001
|
+
toolPrefix: "",
|
|
2735
3002
|
extraCriticalRules: [],
|
|
2736
3003
|
postStartupNotes: [
|
|
2737
3004
|
"**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 +3011,8 @@ var ClaudeDriver = class {
|
|
|
2744
3011
|
|
|
2745
3012
|
// src/drivers/codex.ts
|
|
2746
3013
|
import { spawn as spawn2, execFileSync as execFileSync2, execSync } from "child_process";
|
|
2747
|
-
import { existsSync as
|
|
2748
|
-
import
|
|
3014
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
3015
|
+
import os2 from "os";
|
|
2749
3016
|
import path5 from "path";
|
|
2750
3017
|
|
|
2751
3018
|
// src/runtimeTurnState.ts
|
|
@@ -3107,7 +3374,7 @@ var CodexEventNormalizer = class {
|
|
|
3107
3374
|
|
|
3108
3375
|
// src/drivers/codex.ts
|
|
3109
3376
|
function ensureGitRepoForCodex(workingDirectory, deps = {}) {
|
|
3110
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3377
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
3111
3378
|
const execSyncFn = deps.execSyncFn ?? execSync;
|
|
3112
3379
|
const gitDir = path5.join(workingDirectory, ".git");
|
|
3113
3380
|
if (existsSyncFn(gitDir)) return;
|
|
@@ -3125,7 +3392,7 @@ function isWindowsSandboxRunner(commandPath) {
|
|
|
3125
3392
|
return path5.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
|
|
3126
3393
|
}
|
|
3127
3394
|
function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
3128
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3395
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
3129
3396
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
|
|
3130
3397
|
const env = deps.env ?? process.env;
|
|
3131
3398
|
const winPath = path5.win32;
|
|
@@ -3147,7 +3414,7 @@ function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
|
3147
3414
|
return null;
|
|
3148
3415
|
}
|
|
3149
3416
|
function resolveWindowsCodexDesktopEntry(deps = {}) {
|
|
3150
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3417
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
3151
3418
|
const env = deps.env ?? process.env;
|
|
3152
3419
|
const homeDir = deps.homeDir;
|
|
3153
3420
|
const winPath = path5.win32;
|
|
@@ -3299,11 +3566,15 @@ var CodexDriver = class {
|
|
|
3299
3566
|
// the daemon. They replace the previous transcript-mtime heuristic.
|
|
3300
3567
|
experimentalRawEvents: true
|
|
3301
3568
|
};
|
|
3302
|
-
|
|
3303
|
-
|
|
3569
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
3570
|
+
if (launchRuntimeFields.model) {
|
|
3571
|
+
threadParams.model = launchRuntimeFields.model;
|
|
3572
|
+
}
|
|
3573
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
3574
|
+
threadParams.config = { model_reasoning_effort: launchRuntimeFields.reasoningEffort };
|
|
3304
3575
|
}
|
|
3305
|
-
if (
|
|
3306
|
-
threadParams.
|
|
3576
|
+
if (launchRuntimeFields.mode.kind === "fast") {
|
|
3577
|
+
threadParams.serviceTier = "priority";
|
|
3307
3578
|
}
|
|
3308
3579
|
if (ctx.config.sessionId) {
|
|
3309
3580
|
return {
|
|
@@ -3459,12 +3730,12 @@ var CodexDriver = class {
|
|
|
3459
3730
|
return detectCodexModels();
|
|
3460
3731
|
}
|
|
3461
3732
|
};
|
|
3462
|
-
function detectCodexModels(home =
|
|
3733
|
+
function detectCodexModels(home = os2.homedir()) {
|
|
3463
3734
|
const cachePath = path5.join(home, ".codex", "models_cache.json");
|
|
3464
3735
|
const configPath = path5.join(home, ".codex", "config.toml");
|
|
3465
3736
|
let models = [];
|
|
3466
3737
|
try {
|
|
3467
|
-
const raw =
|
|
3738
|
+
const raw = readFileSync2(cachePath, "utf8");
|
|
3468
3739
|
const parsed = JSON.parse(raw);
|
|
3469
3740
|
const entries = Array.isArray(parsed?.models) ? parsed.models : [];
|
|
3470
3741
|
for (const entry of entries) {
|
|
@@ -3481,7 +3752,7 @@ function detectCodexModels(home = os3.homedir()) {
|
|
|
3481
3752
|
if (models.length === 0) return null;
|
|
3482
3753
|
let defaultModel;
|
|
3483
3754
|
try {
|
|
3484
|
-
const raw =
|
|
3755
|
+
const raw = readFileSync2(configPath, "utf8");
|
|
3485
3756
|
const match = raw.match(/^\s*model\s*=\s*"([^"]+)"/m);
|
|
3486
3757
|
if (match) defaultModel = match[1];
|
|
3487
3758
|
} catch {
|
|
@@ -3512,7 +3783,7 @@ function buildAntigravityArgs(ctx) {
|
|
|
3512
3783
|
const args = [
|
|
3513
3784
|
"--print",
|
|
3514
3785
|
"--print-timeout",
|
|
3515
|
-
ctx.config.envVars?.ANTIGRAVITY_PRINT_TIMEOUT || DEFAULT_PRINT_TIMEOUT,
|
|
3786
|
+
runtimeConfigToLaunchFields(ctx.config).envVars?.ANTIGRAVITY_PRINT_TIMEOUT || DEFAULT_PRINT_TIMEOUT,
|
|
3516
3787
|
"--dangerously-skip-permissions"
|
|
3517
3788
|
];
|
|
3518
3789
|
if (ctx.config.sessionId) {
|
|
@@ -3691,11 +3962,12 @@ var CopilotDriver = class {
|
|
|
3691
3962
|
"-p",
|
|
3692
3963
|
ctx.prompt
|
|
3693
3964
|
];
|
|
3694
|
-
|
|
3695
|
-
|
|
3965
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
3966
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
3967
|
+
args.push("--model", launchRuntimeFields.model);
|
|
3696
3968
|
}
|
|
3697
|
-
if (
|
|
3698
|
-
args.push("--effort",
|
|
3969
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
3970
|
+
args.push("--effort", launchRuntimeFields.reasoningEffort);
|
|
3699
3971
|
}
|
|
3700
3972
|
if (ctx.config.sessionId) {
|
|
3701
3973
|
args.push(`--resume=${ctx.config.sessionId}`);
|
|
@@ -3795,7 +4067,7 @@ var CopilotDriver = class {
|
|
|
3795
4067
|
|
|
3796
4068
|
// src/drivers/cursor.ts
|
|
3797
4069
|
import { spawn as spawn5, spawnSync } from "child_process";
|
|
3798
|
-
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as
|
|
4070
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
3799
4071
|
import path7 from "path";
|
|
3800
4072
|
async function buildCursorSpawnEnv(ctx, deps = {}) {
|
|
3801
4073
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
@@ -3851,7 +4123,7 @@ var CursorDriver = class {
|
|
|
3851
4123
|
}
|
|
3852
4124
|
async spawn(ctx) {
|
|
3853
4125
|
const cursorDir = path7.join(ctx.workingDirectory, ".cursor");
|
|
3854
|
-
if (!
|
|
4126
|
+
if (!existsSync4(cursorDir)) {
|
|
3855
4127
|
mkdirSync2(cursorDir, { recursive: true });
|
|
3856
4128
|
}
|
|
3857
4129
|
const mcpConfigPath = path7.join(cursorDir, "mcp.json");
|
|
@@ -3864,8 +4136,9 @@ var CursorDriver = class {
|
|
|
3864
4136
|
"--approve-mcps",
|
|
3865
4137
|
"--trust"
|
|
3866
4138
|
];
|
|
3867
|
-
|
|
3868
|
-
|
|
4139
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4140
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4141
|
+
args.push("--model", launchRuntimeFields.model);
|
|
3869
4142
|
}
|
|
3870
4143
|
if (ctx.config.sessionId) {
|
|
3871
4144
|
args.push("--resume", ctx.config.sessionId);
|
|
@@ -3984,11 +4257,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
3984
4257
|
return parseCursorModelsOutput(String(result.stdout || ""));
|
|
3985
4258
|
}
|
|
3986
4259
|
function buildCursorModelProbeEnv(deps = {}) {
|
|
3987
|
-
return withWindowsUserEnvironment({
|
|
4260
|
+
return scrubDaemonChildEnv(withWindowsUserEnvironment({
|
|
3988
4261
|
...deps.env ?? process.env,
|
|
3989
4262
|
FORCE_COLOR: "0",
|
|
3990
4263
|
NO_COLOR: "1"
|
|
3991
|
-
}, deps);
|
|
4264
|
+
}, deps));
|
|
3992
4265
|
}
|
|
3993
4266
|
function runCursorModelsCommand() {
|
|
3994
4267
|
return spawnSync("cursor-agent", ["models"], {
|
|
@@ -4000,14 +4273,15 @@ function runCursorModelsCommand() {
|
|
|
4000
4273
|
|
|
4001
4274
|
// src/drivers/gemini.ts
|
|
4002
4275
|
import { execFileSync as execFileSync3, spawn as spawn6 } from "child_process";
|
|
4003
|
-
import { existsSync as
|
|
4276
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
4004
4277
|
import path8 from "path";
|
|
4005
4278
|
async function buildGeminiSpawnEnv(ctx, platform = process.platform) {
|
|
4006
4279
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
|
|
4007
|
-
|
|
4280
|
+
const launchEnvVars = runtimeConfigToLaunchFields(ctx.config).envVars;
|
|
4281
|
+
if (!Object.prototype.hasOwnProperty.call(launchEnvVars ?? {}, "GEMINI_CLI_TRUST_WORKSPACE")) {
|
|
4008
4282
|
spawnEnv.GEMINI_CLI_TRUST_WORKSPACE = "true";
|
|
4009
4283
|
}
|
|
4010
|
-
if (platform === "win32" && !Object.prototype.hasOwnProperty.call(
|
|
4284
|
+
if (platform === "win32" && !Object.prototype.hasOwnProperty.call(launchEnvVars ?? {}, "GEMINI_PTY_INFO")) {
|
|
4011
4285
|
spawnEnv.GEMINI_PTY_INFO = "child_process";
|
|
4012
4286
|
}
|
|
4013
4287
|
return spawnEnv;
|
|
@@ -4027,8 +4301,9 @@ function buildGeminiArgs(config) {
|
|
|
4027
4301
|
"-p",
|
|
4028
4302
|
""
|
|
4029
4303
|
];
|
|
4030
|
-
|
|
4031
|
-
|
|
4304
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(config);
|
|
4305
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4306
|
+
args.push("--model", launchRuntimeFields.model);
|
|
4032
4307
|
}
|
|
4033
4308
|
if (config.sessionId) {
|
|
4034
4309
|
args.push("--resume", config.sessionId);
|
|
@@ -4041,8 +4316,8 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
4041
4316
|
return { command: resolveCommandOnPath("gemini", deps) ?? "gemini", args: commandArgs };
|
|
4042
4317
|
}
|
|
4043
4318
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
4044
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
4045
|
-
const env = deps.env ?? process.env;
|
|
4319
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
4320
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
4046
4321
|
const winPath = path8.win32;
|
|
4047
4322
|
let geminiEntry = null;
|
|
4048
4323
|
try {
|
|
@@ -4214,13 +4489,16 @@ var GeminiDriver = class {
|
|
|
4214
4489
|
// src/drivers/kimi.ts
|
|
4215
4490
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4216
4491
|
import { spawn as spawn7 } from "child_process";
|
|
4217
|
-
import { existsSync as
|
|
4218
|
-
import
|
|
4492
|
+
import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
4493
|
+
import os3 from "os";
|
|
4219
4494
|
import path9 from "path";
|
|
4220
4495
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
4221
4496
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
4222
4497
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
4223
4498
|
var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
|
|
4499
|
+
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
4500
|
+
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
4501
|
+
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
4224
4502
|
function parseToolArguments(raw) {
|
|
4225
4503
|
if (typeof raw !== "string") return raw;
|
|
4226
4504
|
try {
|
|
@@ -4229,6 +4507,73 @@ function parseToolArguments(raw) {
|
|
|
4229
4507
|
return raw;
|
|
4230
4508
|
}
|
|
4231
4509
|
}
|
|
4510
|
+
function readKimiConfigSource(home = os3.homedir(), env = process.env) {
|
|
4511
|
+
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
4512
|
+
if (inlineConfig && inlineConfig.trim()) {
|
|
4513
|
+
return {
|
|
4514
|
+
raw: inlineConfig,
|
|
4515
|
+
explicitPath: null,
|
|
4516
|
+
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
4517
|
+
};
|
|
4518
|
+
}
|
|
4519
|
+
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
4520
|
+
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
|
|
4521
|
+
try {
|
|
4522
|
+
return {
|
|
4523
|
+
raw: readFileSync3(configPath, "utf8"),
|
|
4524
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
4525
|
+
sourcePath: configPath
|
|
4526
|
+
};
|
|
4527
|
+
} catch {
|
|
4528
|
+
return {
|
|
4529
|
+
raw: null,
|
|
4530
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
4531
|
+
sourcePath: configPath
|
|
4532
|
+
};
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
function buildKimiSpawnEnv(env = process.env) {
|
|
4536
|
+
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
4537
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
4538
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
4539
|
+
return scrubDaemonChildEnv(spawnEnv);
|
|
4540
|
+
}
|
|
4541
|
+
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
4542
|
+
return {
|
|
4543
|
+
...process.env,
|
|
4544
|
+
...ctx.config.envVars || {},
|
|
4545
|
+
...overrideEnv || {}
|
|
4546
|
+
};
|
|
4547
|
+
}
|
|
4548
|
+
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
4549
|
+
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
4550
|
+
const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
|
|
4551
|
+
const args = [];
|
|
4552
|
+
let configFilePath = null;
|
|
4553
|
+
let configContent = null;
|
|
4554
|
+
if (source.explicitPath) {
|
|
4555
|
+
configFilePath = source.explicitPath;
|
|
4556
|
+
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
4557
|
+
configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
4558
|
+
configContent = source.raw;
|
|
4559
|
+
if (opts.writeGeneratedConfig !== false) {
|
|
4560
|
+
writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
4561
|
+
chmodSync(configFilePath, 384);
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
if (configFilePath) {
|
|
4565
|
+
args.push("--config-file", configFilePath);
|
|
4566
|
+
}
|
|
4567
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
4568
|
+
args.push("--model", ctx.config.model);
|
|
4569
|
+
}
|
|
4570
|
+
return {
|
|
4571
|
+
args,
|
|
4572
|
+
env: buildKimiSpawnEnv(env),
|
|
4573
|
+
configFilePath,
|
|
4574
|
+
configContent
|
|
4575
|
+
};
|
|
4576
|
+
}
|
|
4232
4577
|
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
4233
4578
|
return {
|
|
4234
4579
|
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
@@ -4252,7 +4597,25 @@ var KimiDriver = class {
|
|
|
4252
4597
|
};
|
|
4253
4598
|
model = {
|
|
4254
4599
|
detectedModelsVerifiedAs: "launchable",
|
|
4255
|
-
toLaunchSpec: (modelId) =>
|
|
4600
|
+
toLaunchSpec: (modelId, ctx, opts) => {
|
|
4601
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
4602
|
+
const launchCtx = {
|
|
4603
|
+
...ctx,
|
|
4604
|
+
config: {
|
|
4605
|
+
...ctx.config,
|
|
4606
|
+
model: modelId
|
|
4607
|
+
}
|
|
4608
|
+
};
|
|
4609
|
+
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
4610
|
+
home: opts?.home,
|
|
4611
|
+
writeGeneratedConfig: false
|
|
4612
|
+
});
|
|
4613
|
+
return {
|
|
4614
|
+
args: launch.args,
|
|
4615
|
+
env: launch.env,
|
|
4616
|
+
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
4617
|
+
};
|
|
4618
|
+
}
|
|
4256
4619
|
};
|
|
4257
4620
|
supportsStdinNotification = true;
|
|
4258
4621
|
mcpToolPrefix = "";
|
|
@@ -4288,7 +4651,7 @@ var KimiDriver = class {
|
|
|
4288
4651
|
const systemPromptPath = path9.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
|
|
4289
4652
|
const agentFilePath = path9.join(ctx.workingDirectory, KIMI_AGENT_FILE);
|
|
4290
4653
|
const mcpConfigPath = path9.join(ctx.workingDirectory, KIMI_MCP_FILE);
|
|
4291
|
-
if (!isResume || !
|
|
4654
|
+
if (!isResume || !existsSync6(systemPromptPath)) {
|
|
4292
4655
|
writeFileSync6(systemPromptPath, ctx.prompt, "utf8");
|
|
4293
4656
|
}
|
|
4294
4657
|
writeFileSync6(agentFilePath, [
|
|
@@ -4306,6 +4669,7 @@ var KimiDriver = class {
|
|
|
4306
4669
|
}
|
|
4307
4670
|
}
|
|
4308
4671
|
}), "utf8");
|
|
4672
|
+
const launch = buildKimiLaunchOptions(ctx);
|
|
4309
4673
|
const args = [
|
|
4310
4674
|
"--wire",
|
|
4311
4675
|
"--yolo",
|
|
@@ -4314,14 +4678,16 @@ var KimiDriver = class {
|
|
|
4314
4678
|
"--mcp-config-file",
|
|
4315
4679
|
mcpConfigPath,
|
|
4316
4680
|
"--session",
|
|
4317
|
-
this.sessionId
|
|
4681
|
+
this.sessionId,
|
|
4682
|
+
...launch.args
|
|
4318
4683
|
];
|
|
4319
|
-
|
|
4320
|
-
|
|
4684
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4685
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4686
|
+
args.push("--model", launchRuntimeFields.model);
|
|
4321
4687
|
}
|
|
4322
4688
|
const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
4323
|
-
const
|
|
4324
|
-
const proc = spawn7(
|
|
4689
|
+
const spawnTarget = resolveKimiSpawn(args);
|
|
4690
|
+
const proc = spawn7(spawnTarget.command, spawnTarget.args, {
|
|
4325
4691
|
cwd: ctx.workingDirectory,
|
|
4326
4692
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4327
4693
|
env: spawnEnv,
|
|
@@ -4329,7 +4695,7 @@ var KimiDriver = class {
|
|
|
4329
4695
|
// and has an 8191-character command-line limit. Kimi's official
|
|
4330
4696
|
// installer/uv entrypoint is an executable, so launch it directly and
|
|
4331
4697
|
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
4332
|
-
shell:
|
|
4698
|
+
shell: spawnTarget.shell
|
|
4333
4699
|
});
|
|
4334
4700
|
proc.stdin?.write(JSON.stringify({
|
|
4335
4701
|
jsonrpc: "2.0",
|
|
@@ -4443,14 +4809,9 @@ var KimiDriver = class {
|
|
|
4443
4809
|
return detectKimiModels();
|
|
4444
4810
|
}
|
|
4445
4811
|
};
|
|
4446
|
-
function detectKimiModels(home =
|
|
4447
|
-
const
|
|
4448
|
-
|
|
4449
|
-
try {
|
|
4450
|
-
raw = readFileSync4(configPath, "utf8");
|
|
4451
|
-
} catch {
|
|
4452
|
-
return null;
|
|
4453
|
-
}
|
|
4812
|
+
function detectKimiModels(home = os3.homedir(), opts = {}) {
|
|
4813
|
+
const raw = readKimiConfigSource(home, opts.env).raw;
|
|
4814
|
+
if (raw === null) return null;
|
|
4454
4815
|
const models = [];
|
|
4455
4816
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
4456
4817
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -4471,8 +4832,8 @@ function detectKimiModels(home = os4.homedir()) {
|
|
|
4471
4832
|
|
|
4472
4833
|
// src/drivers/opencode.ts
|
|
4473
4834
|
import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
|
|
4474
|
-
import { existsSync as
|
|
4475
|
-
import
|
|
4835
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
|
|
4836
|
+
import os4 from "os";
|
|
4476
4837
|
import path10 from "path";
|
|
4477
4838
|
var CHAT_MCP_SERVER_NAME = "chat";
|
|
4478
4839
|
var CHAT_MCP_TOOL_PREFIX = `${CHAT_MCP_SERVER_NAME}_`;
|
|
@@ -4517,13 +4878,13 @@ function parseOpenCodeConfigContent(raw) {
|
|
|
4517
4878
|
return {};
|
|
4518
4879
|
}
|
|
4519
4880
|
function parseUserOpenCodeConfig(ctx) {
|
|
4520
|
-
const raw = ctx.config.envVars?.OPENCODE_CONFIG_CONTENT;
|
|
4881
|
+
const raw = runtimeConfigToLaunchFields(ctx.config).envVars?.OPENCODE_CONFIG_CONTENT;
|
|
4521
4882
|
return parseOpenCodeConfigContent(raw);
|
|
4522
4883
|
}
|
|
4523
|
-
function readLocalOpenCodeConfig(home =
|
|
4884
|
+
function readLocalOpenCodeConfig(home = os4.homedir()) {
|
|
4524
4885
|
const configPath = path10.join(home, ".config", "opencode", "opencode.json");
|
|
4525
4886
|
try {
|
|
4526
|
-
return parseOpenCodeConfigContent(
|
|
4887
|
+
return parseOpenCodeConfigContent(readFileSync4(configPath, "utf8"));
|
|
4527
4888
|
} catch {
|
|
4528
4889
|
}
|
|
4529
4890
|
return {};
|
|
@@ -4581,7 +4942,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
|
|
|
4581
4942
|
}
|
|
4582
4943
|
};
|
|
4583
4944
|
}
|
|
4584
|
-
function buildOpenCodeConfig(ctx, home =
|
|
4945
|
+
function buildOpenCodeConfig(ctx, home = os4.homedir()) {
|
|
4585
4946
|
const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
|
|
4586
4947
|
const userAgents = recordField(userConfig.agent);
|
|
4587
4948
|
const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
|
|
@@ -4606,7 +4967,7 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
|
|
|
4606
4967
|
}
|
|
4607
4968
|
};
|
|
4608
4969
|
}
|
|
4609
|
-
async function buildOpenCodeLaunchOptions(ctx, home =
|
|
4970
|
+
async function buildOpenCodeLaunchOptions(ctx, home = os4.homedir(), version = null) {
|
|
4610
4971
|
const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
4611
4972
|
const config = buildOpenCodeConfig(ctx, home);
|
|
4612
4973
|
const env = {
|
|
@@ -4622,8 +4983,9 @@ async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = n
|
|
|
4622
4983
|
"--dir",
|
|
4623
4984
|
ctx.workingDirectory
|
|
4624
4985
|
];
|
|
4625
|
-
|
|
4626
|
-
|
|
4986
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4987
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4988
|
+
args.push("--model", launchRuntimeFields.model);
|
|
4627
4989
|
}
|
|
4628
4990
|
if (requiresAgentCliFlag(version)) {
|
|
4629
4991
|
args.push("--agent", SLOCK_AGENT_NAME);
|
|
@@ -4704,7 +5066,7 @@ function formatOpenCodeLabelToken(token) {
|
|
|
4704
5066
|
if (/^\d/.test(token)) return token;
|
|
4705
5067
|
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
4706
5068
|
}
|
|
4707
|
-
function detectOpenCodeModels(home =
|
|
5069
|
+
function detectOpenCodeModels(home = os4.homedir(), runCommand = runOpenCodeModelsCommand) {
|
|
4708
5070
|
const commandResult = runCommand(home);
|
|
4709
5071
|
if (commandResult.error || commandResult.status !== 0) return null;
|
|
4710
5072
|
return parseOpenCodeModelsOutput(commandResult.stdout);
|
|
@@ -4713,7 +5075,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
4713
5075
|
const platform = deps.platform ?? process.platform;
|
|
4714
5076
|
const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
|
|
4715
5077
|
const result = spawnSyncFn("opencode", ["models"], {
|
|
4716
|
-
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
5078
|
+
env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
4717
5079
|
encoding: "utf8",
|
|
4718
5080
|
timeout: 5e3,
|
|
4719
5081
|
shell: platform === "win32"
|
|
@@ -4744,7 +5106,7 @@ function openCodeSpecForEntry(entry, commandArgs) {
|
|
|
4744
5106
|
return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
|
|
4745
5107
|
}
|
|
4746
5108
|
function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
4747
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
5109
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync7;
|
|
4748
5110
|
const execFileSyncFn = deps.execFileSyncFn;
|
|
4749
5111
|
const env = deps.env ?? process.env;
|
|
4750
5112
|
const winPath = path10.win32;
|
|
@@ -4774,7 +5136,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
|
4774
5136
|
}
|
|
4775
5137
|
function extractWindowsShimTargets(commandPath, deps = {}) {
|
|
4776
5138
|
if (!isWindowsCommandShim(commandPath)) return [];
|
|
4777
|
-
const readFileSyncFn = deps.readFileSyncFn ??
|
|
5139
|
+
const readFileSyncFn = deps.readFileSyncFn ?? readFileSync4;
|
|
4778
5140
|
const commandDir = path10.win32.dirname(commandPath);
|
|
4779
5141
|
let raw;
|
|
4780
5142
|
try {
|
|
@@ -4908,7 +5270,7 @@ var OpenCodeDriver = class {
|
|
|
4908
5270
|
if (unsupportedMessage) {
|
|
4909
5271
|
throw new Error(unsupportedMessage);
|
|
4910
5272
|
}
|
|
4911
|
-
const launch = await buildOpenCodeLaunchOptions(ctx,
|
|
5273
|
+
const launch = await buildOpenCodeLaunchOptions(ctx, os4.homedir(), version);
|
|
4912
5274
|
const spawnSpec = resolveOpenCodeSpawn(launch.args);
|
|
4913
5275
|
const proc = spawn8(spawnSpec.command, spawnSpec.args, {
|
|
4914
5276
|
cwd: ctx.workingDirectory,
|
|
@@ -4972,6 +5334,297 @@ var OpenCodeDriver = class {
|
|
|
4972
5334
|
}
|
|
4973
5335
|
};
|
|
4974
5336
|
|
|
5337
|
+
// src/drivers/pi.ts
|
|
5338
|
+
import { spawn as spawn9 } from "child_process";
|
|
5339
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
5340
|
+
import path11 from "path";
|
|
5341
|
+
import { fileURLToPath } from "url";
|
|
5342
|
+
import { getAgentDir, VERSION as PI_SDK_VERSION } from "@earendil-works/pi-coding-agent";
|
|
5343
|
+
var CHAT_MCP_TOOL_PREFIX2 = "chat_";
|
|
5344
|
+
var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
|
|
5345
|
+
var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
|
|
5346
|
+
var MIN_SUPPORTED_PI_VERSION = "0.74.0";
|
|
5347
|
+
function parseSemver2(version) {
|
|
5348
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
5349
|
+
if (!match) return null;
|
|
5350
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
5351
|
+
}
|
|
5352
|
+
function isSupportedPiVersion(version) {
|
|
5353
|
+
if (!version) return true;
|
|
5354
|
+
const actual = parseSemver2(version);
|
|
5355
|
+
const minimum = parseSemver2(MIN_SUPPORTED_PI_VERSION);
|
|
5356
|
+
if (!actual || !minimum) return true;
|
|
5357
|
+
for (let i = 0; i < 3; i += 1) {
|
|
5358
|
+
if (actual[i] > minimum[i]) return true;
|
|
5359
|
+
if (actual[i] < minimum[i]) return false;
|
|
5360
|
+
}
|
|
5361
|
+
return true;
|
|
5362
|
+
}
|
|
5363
|
+
function unsupportedPiVersionMessage(version) {
|
|
5364
|
+
if (!version || isSupportedPiVersion(version)) return null;
|
|
5365
|
+
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.`;
|
|
5366
|
+
}
|
|
5367
|
+
function probePi(version = PI_SDK_VERSION) {
|
|
5368
|
+
const unsupportedMessage = unsupportedPiVersionMessage(version);
|
|
5369
|
+
if (unsupportedMessage) {
|
|
5370
|
+
return {
|
|
5371
|
+
available: false,
|
|
5372
|
+
version: `${version} (requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION})`
|
|
5373
|
+
};
|
|
5374
|
+
}
|
|
5375
|
+
return { available: true, version };
|
|
5376
|
+
}
|
|
5377
|
+
function resolvePiSdkRunnerPath(moduleUrl = import.meta.url) {
|
|
5378
|
+
const moduleDir = path11.dirname(fileURLToPath(moduleUrl));
|
|
5379
|
+
const sourceSibling = path11.join(moduleDir, "piSdkRunner.ts");
|
|
5380
|
+
if (existsSync8(sourceSibling)) return sourceSibling;
|
|
5381
|
+
const bundledEntry = path11.join(moduleDir, "drivers", "piSdkRunner.js");
|
|
5382
|
+
if (existsSync8(bundledEntry)) return bundledEntry;
|
|
5383
|
+
return path11.join(moduleDir, "piSdkRunner.js");
|
|
5384
|
+
}
|
|
5385
|
+
function buildPiSdkNodeArgs(runnerPath = resolvePiSdkRunnerPath()) {
|
|
5386
|
+
if (runnerPath.endsWith(".ts")) {
|
|
5387
|
+
return [...process.execArgv, runnerPath];
|
|
5388
|
+
}
|
|
5389
|
+
return [runnerPath];
|
|
5390
|
+
}
|
|
5391
|
+
async function buildPiLaunchOptions(ctx, opts = {}) {
|
|
5392
|
+
const command = opts.command ?? process.execPath;
|
|
5393
|
+
const piDir = path11.join(ctx.workingDirectory, ".slock", "pi");
|
|
5394
|
+
const sessionDir = path11.join(piDir, "sessions");
|
|
5395
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
5396
|
+
const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
5397
|
+
const runnerPath = opts.runnerPath ?? resolvePiSdkRunnerPath();
|
|
5398
|
+
const agentDir = opts.agentDir ?? getAgentDir();
|
|
5399
|
+
const runnerConfigPath = path11.join(
|
|
5400
|
+
piDir,
|
|
5401
|
+
`sdk-run-${(ctx.launchId || "launch").replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`
|
|
5402
|
+
);
|
|
5403
|
+
const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
|
|
5404
|
+
const runnerConfig = {
|
|
5405
|
+
cwd: ctx.workingDirectory,
|
|
5406
|
+
agentDir,
|
|
5407
|
+
sessionDir,
|
|
5408
|
+
sessionId: ctx.config.sessionId || null,
|
|
5409
|
+
standingPrompt: ctx.standingPrompt,
|
|
5410
|
+
prompt: turnPrompt,
|
|
5411
|
+
model: ctx.config.model && ctx.config.model !== "default" ? ctx.config.model : null
|
|
5412
|
+
};
|
|
5413
|
+
writeFileSync7(runnerConfigPath, `${JSON.stringify(runnerConfig)}
|
|
5414
|
+
`, { encoding: "utf8", mode: 384 });
|
|
5415
|
+
const args = [
|
|
5416
|
+
...buildPiSdkNodeArgs(runnerPath),
|
|
5417
|
+
"--config",
|
|
5418
|
+
runnerConfigPath
|
|
5419
|
+
];
|
|
5420
|
+
return {
|
|
5421
|
+
command,
|
|
5422
|
+
args,
|
|
5423
|
+
env: slock.spawnEnv,
|
|
5424
|
+
sessionDir,
|
|
5425
|
+
agentDir,
|
|
5426
|
+
runnerConfigPath,
|
|
5427
|
+
sdkVersion: PI_SDK_VERSION
|
|
5428
|
+
};
|
|
5429
|
+
}
|
|
5430
|
+
function isSystemFirstMessageTask2(message) {
|
|
5431
|
+
return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
|
|
5432
|
+
}
|
|
5433
|
+
function buildPiSystemPrompt(config) {
|
|
5434
|
+
return buildCliTransportSystemPrompt(config, {
|
|
5435
|
+
toolPrefix: CHAT_MCP_TOOL_PREFIX2,
|
|
5436
|
+
extraCriticalRules: [
|
|
5437
|
+
"- 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."
|
|
5438
|
+
],
|
|
5439
|
+
postStartupNotes: [
|
|
5440
|
+
"**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."
|
|
5441
|
+
],
|
|
5442
|
+
includeStdinNotificationSection: false,
|
|
5443
|
+
messageNotificationStyle: "poll"
|
|
5444
|
+
});
|
|
5445
|
+
}
|
|
5446
|
+
function contentText(content) {
|
|
5447
|
+
if (!content) return "";
|
|
5448
|
+
const chunks = [];
|
|
5449
|
+
for (const item of content) {
|
|
5450
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
5451
|
+
chunks.push(item.text);
|
|
5452
|
+
}
|
|
5453
|
+
}
|
|
5454
|
+
return chunks.join("");
|
|
5455
|
+
}
|
|
5456
|
+
function apiKeyErrorMessage(line) {
|
|
5457
|
+
const trimmed = line.trim();
|
|
5458
|
+
if (!trimmed) return null;
|
|
5459
|
+
if (/no api key found/i.test(trimmed)) return trimmed;
|
|
5460
|
+
if (/api key.+required/i.test(trimmed)) return trimmed;
|
|
5461
|
+
if (/no models available/i.test(trimmed)) return trimmed;
|
|
5462
|
+
return null;
|
|
5463
|
+
}
|
|
5464
|
+
var PiDriver = class {
|
|
5465
|
+
id = "pi";
|
|
5466
|
+
lifecycle = {
|
|
5467
|
+
kind: "per_turn",
|
|
5468
|
+
start: "defer_until_concrete_message",
|
|
5469
|
+
exit: "terminate_on_turn_end",
|
|
5470
|
+
inFlightWake: "coalesce_into_pending"
|
|
5471
|
+
};
|
|
5472
|
+
communication = {
|
|
5473
|
+
chat: "slock_cli",
|
|
5474
|
+
runtimeControl: "none"
|
|
5475
|
+
};
|
|
5476
|
+
session = {
|
|
5477
|
+
recovery: "resume_or_fresh"
|
|
5478
|
+
};
|
|
5479
|
+
model = {
|
|
5480
|
+
detectedModelsVerifiedAs: "launchable",
|
|
5481
|
+
toLaunchSpec: async (modelId, ctx) => {
|
|
5482
|
+
if (!ctx) return modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] };
|
|
5483
|
+
const launchCtx = {
|
|
5484
|
+
...ctx,
|
|
5485
|
+
config: {
|
|
5486
|
+
...ctx.config,
|
|
5487
|
+
model: modelId
|
|
5488
|
+
}
|
|
5489
|
+
};
|
|
5490
|
+
const launch = await buildPiLaunchOptions(launchCtx);
|
|
5491
|
+
return {
|
|
5492
|
+
args: launch.args,
|
|
5493
|
+
env: launch.env,
|
|
5494
|
+
configFiles: [launch.runnerConfigPath],
|
|
5495
|
+
params: {
|
|
5496
|
+
agentDir: launch.agentDir,
|
|
5497
|
+
sessionDir: launch.sessionDir,
|
|
5498
|
+
sdkVersion: launch.sdkVersion,
|
|
5499
|
+
resources: "extensions/skills/prompt-templates/themes/context-files disabled by Slock policy"
|
|
5500
|
+
}
|
|
5501
|
+
};
|
|
5502
|
+
}
|
|
5503
|
+
};
|
|
5504
|
+
supportsStdinNotification = false;
|
|
5505
|
+
mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
|
|
5506
|
+
busyDeliveryMode = "none";
|
|
5507
|
+
terminateProcessOnTurnEnd = true;
|
|
5508
|
+
deferSpawnUntilMessage = true;
|
|
5509
|
+
usesSlockCliForCommunication = true;
|
|
5510
|
+
sessionId = null;
|
|
5511
|
+
sessionAnnounced = false;
|
|
5512
|
+
apiKeyErrorAnnounced = false;
|
|
5513
|
+
turnEnded = false;
|
|
5514
|
+
assistantTextByMessageId = /* @__PURE__ */ new Map();
|
|
5515
|
+
shouldDeferWakeMessage(message) {
|
|
5516
|
+
return isSystemFirstMessageTask2(message);
|
|
5517
|
+
}
|
|
5518
|
+
probe() {
|
|
5519
|
+
return probePi();
|
|
5520
|
+
}
|
|
5521
|
+
async detectModels() {
|
|
5522
|
+
return null;
|
|
5523
|
+
}
|
|
5524
|
+
async spawn(ctx) {
|
|
5525
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
5526
|
+
this.sessionAnnounced = false;
|
|
5527
|
+
this.apiKeyErrorAnnounced = false;
|
|
5528
|
+
this.turnEnded = false;
|
|
5529
|
+
this.assistantTextByMessageId.clear();
|
|
5530
|
+
const unsupportedMessage = unsupportedPiVersionMessage(PI_SDK_VERSION);
|
|
5531
|
+
if (unsupportedMessage) throw new Error(unsupportedMessage);
|
|
5532
|
+
const launch = await buildPiLaunchOptions(ctx);
|
|
5533
|
+
const proc = spawn9(launch.command, launch.args, {
|
|
5534
|
+
cwd: ctx.workingDirectory,
|
|
5535
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5536
|
+
env: launch.env,
|
|
5537
|
+
shell: false
|
|
5538
|
+
});
|
|
5539
|
+
proc.stdin?.end();
|
|
5540
|
+
return { process: proc };
|
|
5541
|
+
}
|
|
5542
|
+
parseLine(line) {
|
|
5543
|
+
let event;
|
|
5544
|
+
try {
|
|
5545
|
+
event = JSON.parse(line);
|
|
5546
|
+
} catch {
|
|
5547
|
+
if (this.apiKeyErrorAnnounced) return [];
|
|
5548
|
+
const message = apiKeyErrorMessage(line);
|
|
5549
|
+
if (!message) return [];
|
|
5550
|
+
this.apiKeyErrorAnnounced = true;
|
|
5551
|
+
this.turnEnded = true;
|
|
5552
|
+
return [
|
|
5553
|
+
{ kind: "error", message },
|
|
5554
|
+
{ kind: "turn_end", sessionId: this.sessionId || void 0 }
|
|
5555
|
+
];
|
|
5556
|
+
}
|
|
5557
|
+
const events = [];
|
|
5558
|
+
if (event.type === "session" && event.id) {
|
|
5559
|
+
this.sessionId = event.id;
|
|
5560
|
+
}
|
|
5561
|
+
if (!this.sessionAnnounced && this.sessionId) {
|
|
5562
|
+
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
5563
|
+
this.sessionAnnounced = true;
|
|
5564
|
+
}
|
|
5565
|
+
switch (event.type) {
|
|
5566
|
+
case "agent_start":
|
|
5567
|
+
case "turn_start":
|
|
5568
|
+
events.push({ kind: "thinking", text: "" });
|
|
5569
|
+
break;
|
|
5570
|
+
case "message_update":
|
|
5571
|
+
case "message_end":
|
|
5572
|
+
if (event.message?.role === "assistant") {
|
|
5573
|
+
const key = event.message.id || "current";
|
|
5574
|
+
const currentText = contentText(event.message.content);
|
|
5575
|
+
const previousText = this.assistantTextByMessageId.get(key) ?? "";
|
|
5576
|
+
if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
|
|
5577
|
+
events.push({ kind: "text", text: currentText.slice(previousText.length) });
|
|
5578
|
+
} else if (currentText && currentText !== previousText) {
|
|
5579
|
+
events.push({ kind: "text", text: currentText });
|
|
5580
|
+
}
|
|
5581
|
+
this.assistantTextByMessageId.set(key, currentText);
|
|
5582
|
+
if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
|
|
5583
|
+
events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
|
|
5584
|
+
}
|
|
5585
|
+
}
|
|
5586
|
+
break;
|
|
5587
|
+
case "tool_execution_start":
|
|
5588
|
+
events.push({
|
|
5589
|
+
kind: "tool_call",
|
|
5590
|
+
name: event.toolName || "unknown_tool",
|
|
5591
|
+
input: event.args
|
|
5592
|
+
});
|
|
5593
|
+
break;
|
|
5594
|
+
case "tool_execution_end":
|
|
5595
|
+
events.push({
|
|
5596
|
+
kind: "tool_output",
|
|
5597
|
+
name: event.toolName || "unknown_tool"
|
|
5598
|
+
});
|
|
5599
|
+
if (event.isError) {
|
|
5600
|
+
events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
|
|
5601
|
+
}
|
|
5602
|
+
break;
|
|
5603
|
+
case "compaction_start":
|
|
5604
|
+
events.push({ kind: "compaction_started" });
|
|
5605
|
+
break;
|
|
5606
|
+
case "compaction_end":
|
|
5607
|
+
events.push({ kind: "compaction_finished" });
|
|
5608
|
+
if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
|
|
5609
|
+
break;
|
|
5610
|
+
case "turn_end":
|
|
5611
|
+
case "agent_end":
|
|
5612
|
+
if (!this.turnEnded) {
|
|
5613
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
5614
|
+
this.turnEnded = true;
|
|
5615
|
+
}
|
|
5616
|
+
break;
|
|
5617
|
+
}
|
|
5618
|
+
return events;
|
|
5619
|
+
}
|
|
5620
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
5621
|
+
return null;
|
|
5622
|
+
}
|
|
5623
|
+
buildSystemPrompt(config, _agentId) {
|
|
5624
|
+
return buildPiSystemPrompt(config);
|
|
5625
|
+
}
|
|
5626
|
+
};
|
|
5627
|
+
|
|
4975
5628
|
// src/drivers/index.ts
|
|
4976
5629
|
var driverFactories = {
|
|
4977
5630
|
claude: () => new ClaudeDriver(),
|
|
@@ -4981,7 +5634,8 @@ var driverFactories = {
|
|
|
4981
5634
|
cursor: () => new CursorDriver(),
|
|
4982
5635
|
gemini: () => new GeminiDriver(),
|
|
4983
5636
|
kimi: () => new KimiDriver(),
|
|
4984
|
-
opencode: () => new OpenCodeDriver()
|
|
5637
|
+
opencode: () => new OpenCodeDriver(),
|
|
5638
|
+
pi: () => new PiDriver()
|
|
4985
5639
|
};
|
|
4986
5640
|
function getDriver(runtimeId) {
|
|
4987
5641
|
const createDriver = driverFactories[runtimeId];
|
|
@@ -4994,7 +5648,7 @@ function getDriver(runtimeId) {
|
|
|
4994
5648
|
|
|
4995
5649
|
// src/workspaces.ts
|
|
4996
5650
|
import { readdir, rm, stat } from "fs/promises";
|
|
4997
|
-
import
|
|
5651
|
+
import path12 from "path";
|
|
4998
5652
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
4999
5653
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
5000
5654
|
}
|
|
@@ -5002,7 +5656,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
5002
5656
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
5003
5657
|
return null;
|
|
5004
5658
|
}
|
|
5005
|
-
return
|
|
5659
|
+
return path12.join(dataDir, directoryName);
|
|
5006
5660
|
}
|
|
5007
5661
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
5008
5662
|
return {
|
|
@@ -5051,7 +5705,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
5051
5705
|
return summary;
|
|
5052
5706
|
}
|
|
5053
5707
|
const childSummaries = await Promise.all(
|
|
5054
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
5708
|
+
entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
|
|
5055
5709
|
);
|
|
5056
5710
|
for (const childSummary of childSummaries) {
|
|
5057
5711
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -5070,7 +5724,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
5070
5724
|
if (!entry.isDirectory()) {
|
|
5071
5725
|
return null;
|
|
5072
5726
|
}
|
|
5073
|
-
const dirPath =
|
|
5727
|
+
const dirPath = path12.join(dataDir, entry.name);
|
|
5074
5728
|
try {
|
|
5075
5729
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
5076
5730
|
return {
|
|
@@ -5500,19 +6154,19 @@ function findSessionJsonl(root, predicate) {
|
|
|
5500
6154
|
if (depth < 0 || visited >= maxEntries) return null;
|
|
5501
6155
|
let entries;
|
|
5502
6156
|
try {
|
|
5503
|
-
entries =
|
|
6157
|
+
entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
|
|
5504
6158
|
} catch {
|
|
5505
6159
|
return null;
|
|
5506
6160
|
}
|
|
5507
6161
|
for (const entry of entries) {
|
|
5508
6162
|
if (++visited > maxEntries) return null;
|
|
5509
6163
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
5510
|
-
return
|
|
6164
|
+
return path13.join(dir, entry.name);
|
|
5511
6165
|
}
|
|
5512
6166
|
for (const entry of entries) {
|
|
5513
6167
|
if (++visited > maxEntries) return null;
|
|
5514
6168
|
if (!entry.isDirectory()) continue;
|
|
5515
|
-
const found = visit(
|
|
6169
|
+
const found = visit(path13.join(dir, entry.name), depth - 1);
|
|
5516
6170
|
if (found) return found;
|
|
5517
6171
|
}
|
|
5518
6172
|
return null;
|
|
@@ -5525,10 +6179,10 @@ function safeSessionFilename(value) {
|
|
|
5525
6179
|
}
|
|
5526
6180
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
5527
6181
|
try {
|
|
5528
|
-
const dir =
|
|
5529
|
-
|
|
5530
|
-
const filePath =
|
|
5531
|
-
|
|
6182
|
+
const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
|
|
6183
|
+
mkdirSync5(dir, { recursive: true });
|
|
6184
|
+
const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
6185
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
5532
6186
|
type: "runtime_session_handoff",
|
|
5533
6187
|
runtime,
|
|
5534
6188
|
sessionId,
|
|
@@ -5546,17 +6200,17 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
5546
6200
|
return null;
|
|
5547
6201
|
}
|
|
5548
6202
|
}
|
|
5549
|
-
function resolveRuntimeSessionRef(runtime, sessionId, homeDir =
|
|
5550
|
-
const directPath =
|
|
6203
|
+
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
|
|
6204
|
+
const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
|
|
5551
6205
|
if (directPath) {
|
|
5552
6206
|
try {
|
|
5553
|
-
if (
|
|
6207
|
+
if (statSync(directPath).isFile()) {
|
|
5554
6208
|
return { label: sessionId, path: directPath, runtime, reachable: true };
|
|
5555
6209
|
}
|
|
5556
6210
|
} catch {
|
|
5557
6211
|
}
|
|
5558
6212
|
}
|
|
5559
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
6213
|
+
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
6214
|
if (!resolvedPath && fallbackDir) {
|
|
5561
6215
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
5562
6216
|
if (fallback) return fallback;
|
|
@@ -6494,6 +7148,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6494
7148
|
driverResolver;
|
|
6495
7149
|
defaultAgentEnvVarsProvider;
|
|
6496
7150
|
tracer;
|
|
7151
|
+
cliTransportTraceDir = null;
|
|
6497
7152
|
deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
|
|
6498
7153
|
processExitTraceAttrs = /* @__PURE__ */ new WeakMap();
|
|
6499
7154
|
agentVisibleBoundaries = /* @__PURE__ */ new Map();
|
|
@@ -6504,7 +7159,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6504
7159
|
this.daemonApiKey = daemonApiKey;
|
|
6505
7160
|
this.serverUrl = opts.serverUrl;
|
|
6506
7161
|
this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
|
|
6507
|
-
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir ||
|
|
7162
|
+
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os5.homedir();
|
|
6508
7163
|
this.driverResolver = opts.driverResolver || getDriver;
|
|
6509
7164
|
this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
|
|
6510
7165
|
this.tracer = opts.tracer ?? noopTracer;
|
|
@@ -6524,6 +7179,9 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6524
7179
|
setTracer(tracer) {
|
|
6525
7180
|
this.tracer = tracer;
|
|
6526
7181
|
}
|
|
7182
|
+
setCliTransportTraceDir(traceDir) {
|
|
7183
|
+
this.cliTransportTraceDir = traceDir;
|
|
7184
|
+
}
|
|
6527
7185
|
visibleBoundaryMap(agentId) {
|
|
6528
7186
|
let map = this.agentVisibleBoundaries.get(agentId);
|
|
6529
7187
|
if (!map) {
|
|
@@ -6628,6 +7286,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6628
7286
|
});
|
|
6629
7287
|
},
|
|
6630
7288
|
recordProxyFailure: (input) => this.recordAgentProxyFailure(agentId, input),
|
|
7289
|
+
recordTransportNormalizedError: (input) => this.recordAgentProxyTransportNormalizedError(agentId, input),
|
|
6631
7290
|
recordFreshnessDecision: (input) => {
|
|
6632
7291
|
this.recordDaemonTrace("daemon.agent.inbox.freshness_decision", {
|
|
6633
7292
|
agentId,
|
|
@@ -6656,6 +7315,22 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6656
7315
|
error_message: input.errorMessage
|
|
6657
7316
|
}, "error");
|
|
6658
7317
|
}
|
|
7318
|
+
recordAgentProxyTransportNormalizedError(agentId, input) {
|
|
7319
|
+
this.recordDaemonTrace("daemon.transport.normalized_error", {
|
|
7320
|
+
producer: "daemon",
|
|
7321
|
+
agentId,
|
|
7322
|
+
normalized_code: input.normalizedCode,
|
|
7323
|
+
route_family: input.routeFamily,
|
|
7324
|
+
response_started: input.responseStarted,
|
|
7325
|
+
upstream_layer: input.upstreamLayer,
|
|
7326
|
+
...typeof input.upstreamStatus === "number" ? { upstream_status: input.upstreamStatus } : {},
|
|
7327
|
+
...input.originalMessage ? { original_message: input.originalMessage } : {},
|
|
7328
|
+
launchId: input.launchId,
|
|
7329
|
+
target_host_class: input.targetHostClass,
|
|
7330
|
+
downstream_caller: input.downstreamCaller,
|
|
7331
|
+
upstream: input.upstream
|
|
7332
|
+
}, "error");
|
|
7333
|
+
}
|
|
6659
7334
|
recordFreshnessDecisionActivity(agentId, input) {
|
|
6660
7335
|
if (input.decision !== "local_hold" && input.decision !== "syncing_hold") return;
|
|
6661
7336
|
const ap = this.agents.get(agentId);
|
|
@@ -6916,7 +7591,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6916
7591
|
);
|
|
6917
7592
|
wakeMessage = void 0;
|
|
6918
7593
|
}
|
|
6919
|
-
const agentDataDir =
|
|
7594
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
6920
7595
|
await mkdir(agentDataDir, { recursive: true });
|
|
6921
7596
|
let runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
6922
7597
|
const legacyRuntimeProfileControl = runtimeConfig.runtimeProfileControl?.kind === "migration" ? runtimeConfig.runtimeProfileControl : null;
|
|
@@ -6930,23 +7605,23 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6930
7605
|
);
|
|
6931
7606
|
runtimeConfig = { ...runtimeConfig, runtimeProfileControl: null };
|
|
6932
7607
|
}
|
|
6933
|
-
const memoryMdPath =
|
|
7608
|
+
const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
|
|
6934
7609
|
try {
|
|
6935
7610
|
await access(memoryMdPath);
|
|
6936
7611
|
} catch {
|
|
6937
7612
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
6938
7613
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
6939
7614
|
}
|
|
6940
|
-
const notesDir =
|
|
7615
|
+
const notesDir = path13.join(agentDataDir, "notes");
|
|
6941
7616
|
await mkdir(notesDir, { recursive: true });
|
|
6942
7617
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
6943
7618
|
const seedFiles = buildOnboardingSeedFiles();
|
|
6944
7619
|
for (const { relativePath, content } of seedFiles) {
|
|
6945
|
-
const fullPath =
|
|
7620
|
+
const fullPath = path13.join(agentDataDir, relativePath);
|
|
6946
7621
|
try {
|
|
6947
7622
|
await access(fullPath);
|
|
6948
7623
|
} catch {
|
|
6949
|
-
await mkdir(
|
|
7624
|
+
await mkdir(path13.dirname(fullPath), { recursive: true });
|
|
6950
7625
|
await writeFile(fullPath, content);
|
|
6951
7626
|
}
|
|
6952
7627
|
}
|
|
@@ -7055,7 +7730,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7055
7730
|
slockCliPath: this.slockCliPath,
|
|
7056
7731
|
daemonApiKey: this.daemonApiKey,
|
|
7057
7732
|
launchId: launchId || null,
|
|
7058
|
-
agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId)
|
|
7733
|
+
agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId),
|
|
7734
|
+
cliTransportTraceDir: this.cliTransportTraceDir
|
|
7059
7735
|
});
|
|
7060
7736
|
this.recordDaemonTrace("daemon.agent.spawn.created", {
|
|
7061
7737
|
...this.startQueueTraceAttrs(agentId, effectiveConfig, wakeMessage, unreadSummary, resumePrompt, launchId),
|
|
@@ -7388,7 +8064,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7388
8064
|
"X-Slock-Client": "daemon-server-session-worker"
|
|
7389
8065
|
},
|
|
7390
8066
|
body: JSON.stringify({
|
|
7391
|
-
scopes: ["send", "read", "mentions", "tasks", "reactions", "server", "channels"],
|
|
8067
|
+
scopes: ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"],
|
|
7392
8068
|
name: `runner:${config.runtime}:${agentId.slice(0, 8)}`
|
|
7393
8069
|
})
|
|
7394
8070
|
});
|
|
@@ -7823,7 +8499,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7823
8499
|
return true;
|
|
7824
8500
|
}
|
|
7825
8501
|
async resetWorkspace(agentId) {
|
|
7826
|
-
const agentDataDir =
|
|
8502
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
7827
8503
|
try {
|
|
7828
8504
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
7829
8505
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -7884,7 +8560,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7884
8560
|
return result;
|
|
7885
8561
|
}
|
|
7886
8562
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
7887
|
-
const workspacePath =
|
|
8563
|
+
const workspacePath = path13.join(this.dataDir, agentId);
|
|
7888
8564
|
return {
|
|
7889
8565
|
agentId,
|
|
7890
8566
|
launchId,
|
|
@@ -8141,7 +8817,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8141
8817
|
}
|
|
8142
8818
|
// Workspace file browsing
|
|
8143
8819
|
async getFileTree(agentId, dirPath) {
|
|
8144
|
-
const agentDir =
|
|
8820
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
8145
8821
|
try {
|
|
8146
8822
|
await stat2(agentDir);
|
|
8147
8823
|
} catch {
|
|
@@ -8149,8 +8825,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8149
8825
|
}
|
|
8150
8826
|
let targetDir = agentDir;
|
|
8151
8827
|
if (dirPath) {
|
|
8152
|
-
const resolved =
|
|
8153
|
-
if (!resolved.startsWith(agentDir +
|
|
8828
|
+
const resolved = path13.resolve(agentDir, dirPath);
|
|
8829
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
8154
8830
|
return [];
|
|
8155
8831
|
}
|
|
8156
8832
|
targetDir = resolved;
|
|
@@ -8158,14 +8834,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8158
8834
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
8159
8835
|
}
|
|
8160
8836
|
async readFile(agentId, filePath) {
|
|
8161
|
-
const agentDir =
|
|
8162
|
-
const resolved =
|
|
8163
|
-
if (!resolved.startsWith(agentDir +
|
|
8837
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
8838
|
+
const resolved = path13.resolve(agentDir, filePath);
|
|
8839
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
8164
8840
|
throw new Error("Access denied");
|
|
8165
8841
|
}
|
|
8166
8842
|
const info = await stat2(resolved);
|
|
8167
8843
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
8168
|
-
const ext =
|
|
8844
|
+
const ext = path13.extname(resolved).toLowerCase();
|
|
8169
8845
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
8170
8846
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
8171
8847
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -8199,14 +8875,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8199
8875
|
async listSkills(agentId, runtimeHint) {
|
|
8200
8876
|
const agent = this.agents.get(agentId);
|
|
8201
8877
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
8202
|
-
const home =
|
|
8203
|
-
const workspaceDir =
|
|
8878
|
+
const home = os5.homedir();
|
|
8879
|
+
const workspaceDir = path13.join(this.dataDir, agentId);
|
|
8204
8880
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
8205
8881
|
const globalResults = await Promise.all(
|
|
8206
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
8882
|
+
paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
|
|
8207
8883
|
);
|
|
8208
8884
|
const workspaceResults = await Promise.all(
|
|
8209
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
8885
|
+
paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
|
|
8210
8886
|
);
|
|
8211
8887
|
const dedup = (skills) => {
|
|
8212
8888
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -8235,7 +8911,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8235
8911
|
const skills = [];
|
|
8236
8912
|
for (const entry of entries) {
|
|
8237
8913
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
8238
|
-
const skillMd =
|
|
8914
|
+
const skillMd = path13.join(dir, entry.name, "SKILL.md");
|
|
8239
8915
|
try {
|
|
8240
8916
|
const content = await readFile(skillMd, "utf-8");
|
|
8241
8917
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -8246,7 +8922,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8246
8922
|
} else if (entry.name.endsWith(".md")) {
|
|
8247
8923
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
8248
8924
|
try {
|
|
8249
|
-
const content = await readFile(
|
|
8925
|
+
const content = await readFile(path13.join(dir, entry.name), "utf-8");
|
|
8250
8926
|
const skill = this.parseSkillMd(cmdName, content);
|
|
8251
8927
|
skill.sourcePath = dir;
|
|
8252
8928
|
skills.push(skill);
|
|
@@ -9320,8 +9996,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
9320
9996
|
const nodes = [];
|
|
9321
9997
|
for (const entry of entries) {
|
|
9322
9998
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
9323
|
-
const fullPath =
|
|
9324
|
-
const relativePath =
|
|
9999
|
+
const fullPath = path13.join(dir, entry.name);
|
|
10000
|
+
const relativePath = path13.relative(rootDir, fullPath);
|
|
9325
10001
|
let info;
|
|
9326
10002
|
try {
|
|
9327
10003
|
info = await stat2(fullPath);
|
|
@@ -9626,9 +10302,9 @@ var ReminderCache = class {
|
|
|
9626
10302
|
|
|
9627
10303
|
// src/machineLock.ts
|
|
9628
10304
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
9629
|
-
import { mkdirSync as
|
|
9630
|
-
import
|
|
9631
|
-
import
|
|
10305
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
10306
|
+
import os6 from "os";
|
|
10307
|
+
import path14 from "path";
|
|
9632
10308
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
9633
10309
|
var DaemonMachineLockConflictError = class extends Error {
|
|
9634
10310
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -9650,18 +10326,18 @@ function resolveDefaultMachineStateRoot() {
|
|
|
9650
10326
|
return resolveSlockHomePath("machines");
|
|
9651
10327
|
}
|
|
9652
10328
|
function ownerPath(lockDir) {
|
|
9653
|
-
return
|
|
10329
|
+
return path14.join(lockDir, "owner.json");
|
|
9654
10330
|
}
|
|
9655
10331
|
function readOwner(lockDir) {
|
|
9656
10332
|
try {
|
|
9657
|
-
return JSON.parse(
|
|
10333
|
+
return JSON.parse(readFileSync5(ownerPath(lockDir), "utf8"));
|
|
9658
10334
|
} catch {
|
|
9659
10335
|
return null;
|
|
9660
10336
|
}
|
|
9661
10337
|
}
|
|
9662
10338
|
function lockAgeMs(lockDir) {
|
|
9663
10339
|
try {
|
|
9664
|
-
return Date.now() -
|
|
10340
|
+
return Date.now() - statSync2(lockDir).mtimeMs;
|
|
9665
10341
|
} catch {
|
|
9666
10342
|
return null;
|
|
9667
10343
|
}
|
|
@@ -9680,23 +10356,23 @@ function acquireDaemonMachineLock(options) {
|
|
|
9680
10356
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
9681
10357
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
9682
10358
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
9683
|
-
const machineDir =
|
|
9684
|
-
const lockDir =
|
|
10359
|
+
const machineDir = path14.join(rootDir, lockId);
|
|
10360
|
+
const lockDir = path14.join(machineDir, "daemon.lock");
|
|
9685
10361
|
const token = randomUUID3();
|
|
9686
|
-
|
|
10362
|
+
mkdirSync6(machineDir, { recursive: true });
|
|
9687
10363
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
9688
10364
|
try {
|
|
9689
|
-
|
|
10365
|
+
mkdirSync6(lockDir);
|
|
9690
10366
|
const owner = {
|
|
9691
10367
|
pid: process.pid,
|
|
9692
10368
|
token,
|
|
9693
|
-
hostname:
|
|
10369
|
+
hostname: os6.hostname(),
|
|
9694
10370
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9695
10371
|
serverUrl: options.serverUrl,
|
|
9696
10372
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
9697
10373
|
};
|
|
9698
10374
|
try {
|
|
9699
|
-
|
|
10375
|
+
writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
9700
10376
|
`, { mode: 384 });
|
|
9701
10377
|
} catch (err) {
|
|
9702
10378
|
rmSync3(lockDir, { recursive: true, force: true });
|
|
@@ -9733,8 +10409,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
9733
10409
|
}
|
|
9734
10410
|
|
|
9735
10411
|
// src/localTraceSink.ts
|
|
9736
|
-
import { appendFileSync, mkdirSync as
|
|
9737
|
-
import
|
|
10412
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync10 } from "fs";
|
|
10413
|
+
import path15 from "path";
|
|
9738
10414
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
9739
10415
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
9740
10416
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -9757,7 +10433,8 @@ var DIAGNOSTIC_ERROR_ATTRS = /* @__PURE__ */ new Set([
|
|
|
9757
10433
|
"runtime_error_message_present",
|
|
9758
10434
|
"runtime_error_message_length_bucket",
|
|
9759
10435
|
"runtime_error_message_truncated",
|
|
9760
|
-
"runtime_error_message_excerpt"
|
|
10436
|
+
"runtime_error_message_excerpt",
|
|
10437
|
+
"original_message"
|
|
9761
10438
|
]);
|
|
9762
10439
|
var LocalRotatingTraceSink = class {
|
|
9763
10440
|
traceDir;
|
|
@@ -9770,7 +10447,7 @@ var LocalRotatingTraceSink = class {
|
|
|
9770
10447
|
currentSize = 0;
|
|
9771
10448
|
sequence = 0;
|
|
9772
10449
|
constructor(options) {
|
|
9773
|
-
this.traceDir =
|
|
10450
|
+
this.traceDir = path15.join(options.machineDir, "traces");
|
|
9774
10451
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
9775
10452
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
9776
10453
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -9796,26 +10473,26 @@ var LocalRotatingTraceSink = class {
|
|
|
9796
10473
|
return this.currentFile;
|
|
9797
10474
|
}
|
|
9798
10475
|
ensureFile(nextBytes) {
|
|
9799
|
-
|
|
10476
|
+
mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
|
|
9800
10477
|
const nowMs = this.nowMsProvider();
|
|
9801
10478
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
9802
10479
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
9803
|
-
this.currentFile =
|
|
10480
|
+
this.currentFile = path15.join(
|
|
9804
10481
|
this.traceDir,
|
|
9805
10482
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
9806
10483
|
);
|
|
9807
|
-
|
|
9808
|
-
this.currentSize =
|
|
10484
|
+
writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
|
|
10485
|
+
this.currentSize = statSync3(this.currentFile).size;
|
|
9809
10486
|
this.currentFileOpenedAtMs = nowMs;
|
|
9810
10487
|
this.pruneOldFiles();
|
|
9811
10488
|
}
|
|
9812
10489
|
}
|
|
9813
10490
|
pruneOldFiles() {
|
|
9814
|
-
const files =
|
|
10491
|
+
const files = readdirSync2(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
|
|
9815
10492
|
const excess = files.length - this.maxFiles;
|
|
9816
10493
|
if (excess <= 0) return;
|
|
9817
10494
|
for (const file of files.slice(0, excess)) {
|
|
9818
|
-
rmSync4(
|
|
10495
|
+
rmSync4(path15.join(this.traceDir, file), { force: true });
|
|
9819
10496
|
}
|
|
9820
10497
|
}
|
|
9821
10498
|
};
|
|
@@ -9851,14 +10528,13 @@ function sanitizeAttrs(attrs) {
|
|
|
9851
10528
|
if (!attrs) return void 0;
|
|
9852
10529
|
const sanitized = {};
|
|
9853
10530
|
for (const [key, value] of Object.entries(attrs)) {
|
|
10531
|
+
if (value === null || value === void 0 || value === "") continue;
|
|
9854
10532
|
if (isDiagnosticIdAttr(key)) {
|
|
9855
|
-
if (value === null || value === void 0 || value === "") continue;
|
|
9856
10533
|
sanitized[key] = sanitizeValue(value);
|
|
9857
10534
|
continue;
|
|
9858
10535
|
}
|
|
9859
10536
|
if (isDiagnosticErrorAttr(key)) {
|
|
9860
|
-
|
|
9861
|
-
sanitized[key] = sanitizeValue(value);
|
|
10537
|
+
sanitized[key] = sanitizeDiagnosticErrorValue(key, value);
|
|
9862
10538
|
continue;
|
|
9863
10539
|
}
|
|
9864
10540
|
if (shouldDropAttr(key)) continue;
|
|
@@ -9878,6 +10554,11 @@ function sanitizeValue(value) {
|
|
|
9878
10554
|
}
|
|
9879
10555
|
return String(value);
|
|
9880
10556
|
}
|
|
10557
|
+
function sanitizeDiagnosticErrorValue(key, value) {
|
|
10558
|
+
if (key !== "original_message" || typeof value !== "string") return sanitizeValue(value);
|
|
10559
|
+
const normalized = value.replace(/sk_(?:agent|machine|computer)_[A-Za-z0-9_-]+/g, "sk_[redacted]").replace(/sap_[A-Za-z0-9_-]+/g, "sap_[redacted]").replace(/https?:\/\/\S+/g, "[url]").replace(/\s+/g, " ").trim();
|
|
10560
|
+
return normalized.length > 240 ? `${normalized.slice(0, 237)}...` : normalized;
|
|
10561
|
+
}
|
|
9881
10562
|
function shouldDropAttr(key) {
|
|
9882
10563
|
const normalized = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
9883
10564
|
if (/(^|_)(api_key|auth_token|token|secret|password|cookie|credential)(_|$)/i.test(normalized)) {
|
|
@@ -9902,11 +10583,11 @@ function isDiagnosticErrorAttr(key) {
|
|
|
9902
10583
|
import { createHash as createHash5, randomUUID as randomUUID4 } from "crypto";
|
|
9903
10584
|
import { gzipSync } from "zlib";
|
|
9904
10585
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
9905
|
-
import
|
|
10586
|
+
import path16 from "path";
|
|
9906
10587
|
|
|
9907
10588
|
// src/directUploadCapability.ts
|
|
9908
|
-
function joinUrl(base,
|
|
9909
|
-
return `${base.replace(/\/+$/, "")}${
|
|
10589
|
+
function joinUrl(base, path18) {
|
|
10590
|
+
return `${base.replace(/\/+$/, "")}${path18}`;
|
|
9910
10591
|
}
|
|
9911
10592
|
function jsonHeaders(apiKey) {
|
|
9912
10593
|
return {
|
|
@@ -10125,7 +10806,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
10125
10806
|
}, nextMs);
|
|
10126
10807
|
}
|
|
10127
10808
|
async findUploadCandidates() {
|
|
10128
|
-
const traceDir =
|
|
10809
|
+
const traceDir = path16.join(this.options.machineDir, "traces");
|
|
10129
10810
|
let names;
|
|
10130
10811
|
try {
|
|
10131
10812
|
names = await readdir3(traceDir);
|
|
@@ -10137,8 +10818,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
10137
10818
|
const currentFile = this.options.currentFileProvider?.();
|
|
10138
10819
|
const candidates = [];
|
|
10139
10820
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
10140
|
-
const file =
|
|
10141
|
-
if (currentFile &&
|
|
10821
|
+
const file = path16.join(traceDir, name);
|
|
10822
|
+
if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
|
|
10142
10823
|
if (await this.isUploaded(file)) continue;
|
|
10143
10824
|
try {
|
|
10144
10825
|
const info = await stat3(file);
|
|
@@ -10212,8 +10893,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
10212
10893
|
}
|
|
10213
10894
|
}
|
|
10214
10895
|
uploadStatePath(file) {
|
|
10215
|
-
const stateDir =
|
|
10216
|
-
return
|
|
10896
|
+
const stateDir = path16.join(this.options.machineDir, "trace-uploads");
|
|
10897
|
+
return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
|
|
10217
10898
|
}
|
|
10218
10899
|
async isUploaded(file) {
|
|
10219
10900
|
try {
|
|
@@ -10225,9 +10906,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
10225
10906
|
}
|
|
10226
10907
|
async markUploaded(file, metadata) {
|
|
10227
10908
|
const stateFile = this.uploadStatePath(file);
|
|
10228
|
-
await mkdir2(
|
|
10909
|
+
await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
|
|
10229
10910
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
10230
|
-
file:
|
|
10911
|
+
file: path16.basename(file),
|
|
10231
10912
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10232
10913
|
...metadata
|
|
10233
10914
|
}, null, 2)}
|
|
@@ -10246,10 +10927,10 @@ function readPositiveIntegerEnv2(name, fallback) {
|
|
|
10246
10927
|
|
|
10247
10928
|
// src/core.ts
|
|
10248
10929
|
var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
10249
|
-
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels"];
|
|
10930
|
+
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
|
|
10250
10931
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
10251
10932
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
10252
|
-
var DAEMON_CLI_USAGE =
|
|
10933
|
+
var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
|
|
10253
10934
|
var RunnerCredentialMintError2 = class extends Error {
|
|
10254
10935
|
code;
|
|
10255
10936
|
retryable;
|
|
@@ -10285,9 +10966,9 @@ function runnerCredentialErrorDetail2(error) {
|
|
|
10285
10966
|
async function waitForRunnerCredentialRetry2() {
|
|
10286
10967
|
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
10287
10968
|
}
|
|
10288
|
-
function parseDaemonCliArgs(args) {
|
|
10969
|
+
function parseDaemonCliArgs(args, env = {}) {
|
|
10289
10970
|
let serverUrl = "";
|
|
10290
|
-
let apiKey = "";
|
|
10971
|
+
let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
|
|
10291
10972
|
for (let i = 0; i < args.length; i++) {
|
|
10292
10973
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
10293
10974
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -10304,23 +10985,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
10304
10985
|
}
|
|
10305
10986
|
}
|
|
10306
10987
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
10307
|
-
const dirname =
|
|
10308
|
-
const jsPath =
|
|
10988
|
+
const dirname = path17.dirname(fileURLToPath2(moduleUrl));
|
|
10989
|
+
const jsPath = path17.resolve(dirname, "chat-bridge.js");
|
|
10309
10990
|
try {
|
|
10310
10991
|
accessSync(jsPath);
|
|
10311
10992
|
return jsPath;
|
|
10312
10993
|
} catch {
|
|
10313
|
-
return
|
|
10994
|
+
return path17.resolve(dirname, "chat-bridge.ts");
|
|
10314
10995
|
}
|
|
10315
10996
|
}
|
|
10316
10997
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
10317
|
-
const thisDir =
|
|
10318
|
-
const bundledDistPath =
|
|
10998
|
+
const thisDir = path17.dirname(fileURLToPath2(moduleUrl));
|
|
10999
|
+
const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
|
|
10319
11000
|
try {
|
|
10320
11001
|
accessSync(bundledDistPath);
|
|
10321
11002
|
return bundledDistPath;
|
|
10322
11003
|
} catch {
|
|
10323
|
-
const workspaceDistPath =
|
|
11004
|
+
const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
10324
11005
|
accessSync(workspaceDistPath);
|
|
10325
11006
|
return workspaceDistPath;
|
|
10326
11007
|
}
|
|
@@ -10499,7 +11180,7 @@ var DaemonCore = class {
|
|
|
10499
11180
|
}
|
|
10500
11181
|
resolveMachineStateRoot() {
|
|
10501
11182
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
10502
|
-
if (this.options.dataDir) return
|
|
11183
|
+
if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
|
|
10503
11184
|
return resolveDefaultMachineStateRoot();
|
|
10504
11185
|
}
|
|
10505
11186
|
shouldEnableLocalTrace() {
|
|
@@ -10525,6 +11206,7 @@ var DaemonCore = class {
|
|
|
10525
11206
|
sink: this.localTraceSink
|
|
10526
11207
|
});
|
|
10527
11208
|
this.agentManager.setTracer(this.tracer);
|
|
11209
|
+
this.agentManager.setCliTransportTraceDir(path17.join(machineDir, "traces"));
|
|
10528
11210
|
}
|
|
10529
11211
|
installTraceBundleUploader(machineDir) {
|
|
10530
11212
|
if (!this.shouldEnableLocalTrace()) return;
|
|
@@ -10947,8 +11629,8 @@ var DaemonCore = class {
|
|
|
10947
11629
|
capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
|
|
10948
11630
|
runtimes,
|
|
10949
11631
|
runningAgents: runningAgentIds,
|
|
10950
|
-
hostname: this.options.hostname ??
|
|
10951
|
-
os: this.options.osDescription ?? `${
|
|
11632
|
+
hostname: this.options.hostname ?? os7.hostname(),
|
|
11633
|
+
os: this.options.osDescription ?? `${os7.platform()} ${os7.arch()}`,
|
|
10952
11634
|
daemonVersion: this.daemonVersion
|
|
10953
11635
|
});
|
|
10954
11636
|
this.recordDaemonTrace("daemon.ready.sent", {
|
|
@@ -11010,6 +11692,8 @@ var DaemonCore = class {
|
|
|
11010
11692
|
};
|
|
11011
11693
|
|
|
11012
11694
|
export {
|
|
11695
|
+
DAEMON_API_KEY_ENV,
|
|
11696
|
+
scrubDaemonAuthEnv,
|
|
11013
11697
|
resolveWorkspaceDirectoryPath,
|
|
11014
11698
|
scanWorkspaceDirectories,
|
|
11015
11699
|
deleteWorkspaceDirectory,
|