@slock-ai/daemon 0.54.2 → 0.55.1
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-QQRU3GA6.js} +627 -345
- package/dist/cli/index.js +2128 -920
- package/dist/core.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -9,11 +9,35 @@ import {
|
|
|
9
9
|
|
|
10
10
|
// src/core.ts
|
|
11
11
|
import path16 from "path";
|
|
12
|
-
import
|
|
12
|
+
import os7 from "os";
|
|
13
13
|
import { createRequire as createRequire2 } from "module";
|
|
14
14
|
import { accessSync } from "fs";
|
|
15
15
|
import { fileURLToPath } from "url";
|
|
16
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
|
+
);
|
|
40
|
+
|
|
17
41
|
// ../shared/src/tracing/index.ts
|
|
18
42
|
var DEFAULT_TRACE_FLAGS = "00";
|
|
19
43
|
var TRACEPARENT_VERSION = "00";
|
|
@@ -724,6 +748,7 @@ 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 },
|
|
@@ -734,6 +759,164 @@ var RUNTIMES = [
|
|
|
734
759
|
{ id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true },
|
|
735
760
|
{ id: "opencode", displayName: "OpenCode", binary: "opencode", supported: true }
|
|
736
761
|
];
|
|
762
|
+
var RUNTIME_MODELS = {
|
|
763
|
+
claude: [
|
|
764
|
+
{ id: "opus", label: "Opus" },
|
|
765
|
+
{ id: "sonnet", label: "Sonnet" },
|
|
766
|
+
{ id: "haiku", label: "Haiku" }
|
|
767
|
+
],
|
|
768
|
+
codex: [
|
|
769
|
+
{ id: "gpt-5.5", label: "GPT-5.5" },
|
|
770
|
+
{ id: "gpt-5.4", label: "GPT-5.4" },
|
|
771
|
+
{ id: "gpt-5.3-codex", label: "GPT-5.3 Codex" },
|
|
772
|
+
{ id: "gpt-5.3-codex-spark", label: "GPT-5.3 Codex Spark" },
|
|
773
|
+
{ id: "gpt-5.2-codex", label: "GPT-5.2 Codex" },
|
|
774
|
+
{ id: "gpt-5.2", label: "GPT-5.2" },
|
|
775
|
+
{ id: "gpt-5.1-codex-max", label: "GPT-5.1 Codex Max" },
|
|
776
|
+
{ id: "gpt-5.1-codex", label: "GPT-5.1 Codex" },
|
|
777
|
+
{ id: "gpt-5-codex", label: "GPT-5 Codex" },
|
|
778
|
+
{ id: "gpt-5", label: "GPT-5" }
|
|
779
|
+
],
|
|
780
|
+
antigravity: [
|
|
781
|
+
{ id: "default", label: "AGY configured default", verified: "suggestion_only" }
|
|
782
|
+
],
|
|
783
|
+
copilot: [
|
|
784
|
+
{ id: "gpt-5.4", label: "GPT-5.4" },
|
|
785
|
+
{ id: "gpt-5.2", label: "GPT-5.2" },
|
|
786
|
+
{ id: "claude-4-sonnet", label: "Claude 4 Sonnet" },
|
|
787
|
+
{ id: "claude-4.5-sonnet", label: "Claude 4.5 Sonnet" }
|
|
788
|
+
],
|
|
789
|
+
cursor: [
|
|
790
|
+
{ id: "composer-2-fast", label: "Composer 2 Fast" },
|
|
791
|
+
{ id: "composer-2", label: "Composer 2" },
|
|
792
|
+
{ id: "auto", label: "Auto" }
|
|
793
|
+
],
|
|
794
|
+
gemini: [
|
|
795
|
+
{ id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
|
|
796
|
+
{ id: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro (Preview)" },
|
|
797
|
+
{ id: "gemini-3-flash-preview", label: "Gemini 3 Flash (Preview)" },
|
|
798
|
+
{ id: "gemini-2.5-pro", label: "Gemini 2.5 Pro" },
|
|
799
|
+
{ id: "gemini-2.5-flash", label: "Gemini 2.5 Flash" }
|
|
800
|
+
],
|
|
801
|
+
opencode: [
|
|
802
|
+
{ id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
|
|
803
|
+
{ id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro (OpenCode)", verified: "suggestion_only" },
|
|
804
|
+
{ id: "openrouter/anthropic/claude-opus-4.5", label: "Claude Opus 4.5 via OpenRouter", verified: "suggestion_only" },
|
|
805
|
+
{ id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
|
|
806
|
+
],
|
|
807
|
+
// Kimi CLI resolves model keys from each user's local config, so the safest
|
|
808
|
+
// built-in option is to defer to whatever default model the CLI already uses.
|
|
809
|
+
kimi: [
|
|
810
|
+
{ id: "default", label: "Configured Default" }
|
|
811
|
+
]
|
|
812
|
+
};
|
|
813
|
+
function getDefaultModel(runtimeId) {
|
|
814
|
+
const models = RUNTIME_MODELS[runtimeId];
|
|
815
|
+
return models?.[0]?.id ?? "sonnet";
|
|
816
|
+
}
|
|
817
|
+
var CONTROLLED_RUNTIME_ENV_KEYS = {
|
|
818
|
+
claude: ["ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY", "ANTHROPIC_CUSTOM_MODEL_OPTION"]
|
|
819
|
+
};
|
|
820
|
+
function isPlainRecord(value) {
|
|
821
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
822
|
+
}
|
|
823
|
+
function normalizeEnvVars(envVars) {
|
|
824
|
+
if (!isPlainRecord(envVars)) return null;
|
|
825
|
+
const normalized = {};
|
|
826
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
827
|
+
if (typeof key === "string" && typeof value === "string") {
|
|
828
|
+
normalized[key] = value;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
832
|
+
}
|
|
833
|
+
function getControlledRuntimeEnvKeys(runtime) {
|
|
834
|
+
return CONTROLLED_RUNTIME_ENV_KEYS[runtime] ?? [];
|
|
835
|
+
}
|
|
836
|
+
function stripControlledRuntimeEnvVars(runtime, envVars) {
|
|
837
|
+
const normalized = normalizeEnvVars(envVars);
|
|
838
|
+
if (!normalized) return null;
|
|
839
|
+
const controlled = new Set(getControlledRuntimeEnvKeys(runtime));
|
|
840
|
+
for (const key of controlled) {
|
|
841
|
+
delete normalized[key];
|
|
842
|
+
}
|
|
843
|
+
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
844
|
+
}
|
|
845
|
+
function isPresetRuntimeModel(runtime, model) {
|
|
846
|
+
return (RUNTIME_MODELS[runtime] ?? []).some((candidate) => candidate.id === model);
|
|
847
|
+
}
|
|
848
|
+
function modelConfigFromLegacy(runtime, model) {
|
|
849
|
+
return isPresetRuntimeModel(runtime, model) ? { kind: "preset", id: model } : { kind: "custom", name: model };
|
|
850
|
+
}
|
|
851
|
+
function parseProviderConfig(runtime, value) {
|
|
852
|
+
if (runtime !== "claude") return void 0;
|
|
853
|
+
if (!isPlainRecord(value)) return { kind: "default" };
|
|
854
|
+
if (value.kind === "custom" && typeof value.apiUrl === "string" && value.apiUrl.trim() && typeof value.apiKey === "string" && value.apiKey.trim()) {
|
|
855
|
+
return { kind: "custom", apiUrl: value.apiUrl.trim(), apiKey: value.apiKey.trim() };
|
|
856
|
+
}
|
|
857
|
+
return { kind: "default" };
|
|
858
|
+
}
|
|
859
|
+
function parseModelConfig(runtime, value, fallback) {
|
|
860
|
+
if (!isPlainRecord(value)) return modelConfigFromLegacy(runtime, fallback);
|
|
861
|
+
if (value.kind === "custom" && typeof value.name === "string" && value.name.trim()) {
|
|
862
|
+
return { kind: "custom", name: value.name.trim() };
|
|
863
|
+
}
|
|
864
|
+
if (value.kind === "preset" && typeof value.id === "string" && value.id.trim()) {
|
|
865
|
+
return { kind: "preset", id: value.id.trim() };
|
|
866
|
+
}
|
|
867
|
+
return modelConfigFromLegacy(runtime, fallback);
|
|
868
|
+
}
|
|
869
|
+
function parseModeConfig(value) {
|
|
870
|
+
if (!isPlainRecord(value)) return { kind: "default" };
|
|
871
|
+
if (value.kind === "fast") return { kind: "fast" };
|
|
872
|
+
return { kind: "default" };
|
|
873
|
+
}
|
|
874
|
+
function hydrateRuntimeConfig(input) {
|
|
875
|
+
const stored = isPlainRecord(input.runtimeConfig) && input.runtimeConfig.version === RUNTIME_CONFIG_VERSION ? input.runtimeConfig : null;
|
|
876
|
+
const runtime = typeof stored?.runtime === "string" && stored.runtime.trim() || typeof input.runtime === "string" && input.runtime.trim() || "claude";
|
|
877
|
+
const fallbackModel = typeof input.model === "string" && input.model.trim() || getDefaultModel(runtime);
|
|
878
|
+
const legacyEnvVars = normalizeEnvVars(input.envVars);
|
|
879
|
+
const storedEnvVars = normalizeEnvVars(stored?.envVars);
|
|
880
|
+
const legacyClaudeApiUrl = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_BASE_URL : void 0;
|
|
881
|
+
const legacyClaudeApiKey = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_API_KEY : void 0;
|
|
882
|
+
const provider = stored ? parseProviderConfig(runtime, stored.provider) : runtime === "claude" && legacyClaudeApiUrl && legacyClaudeApiKey ? { kind: "custom", apiUrl: legacyClaudeApiUrl, apiKey: legacyClaudeApiKey } : runtime === "claude" ? { kind: "default" } : void 0;
|
|
883
|
+
return {
|
|
884
|
+
version: RUNTIME_CONFIG_VERSION,
|
|
885
|
+
runtime,
|
|
886
|
+
...provider ? { provider } : {},
|
|
887
|
+
model: stored ? parseModelConfig(runtime, stored.model, fallbackModel) : modelConfigFromLegacy(runtime, fallbackModel),
|
|
888
|
+
mode: stored ? parseModeConfig(stored.mode) : { kind: "default" },
|
|
889
|
+
reasoningEffort: stored?.reasoningEffort ?? input.reasoningEffort ?? null,
|
|
890
|
+
envVars: stripControlledRuntimeEnvVars(runtime, stored ? storedEnvVars : legacyEnvVars)
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
function runtimeConfigModelValue(config) {
|
|
894
|
+
return config.model.kind === "custom" ? config.model.name : config.model.id;
|
|
895
|
+
}
|
|
896
|
+
function runtimeConfigToLaunchFields(config) {
|
|
897
|
+
const normalized = isPlainRecord(config) && config.version === RUNTIME_CONFIG_VERSION ? hydrateRuntimeConfig({ runtimeConfig: config }) : hydrateRuntimeConfig(config);
|
|
898
|
+
const generatedEnvVars = {};
|
|
899
|
+
if (normalized.runtime === "claude") {
|
|
900
|
+
if (normalized.provider?.kind === "custom") {
|
|
901
|
+
generatedEnvVars.ANTHROPIC_BASE_URL = normalized.provider.apiUrl;
|
|
902
|
+
generatedEnvVars.ANTHROPIC_API_KEY = normalized.provider.apiKey;
|
|
903
|
+
}
|
|
904
|
+
if (normalized.model.kind === "custom") {
|
|
905
|
+
generatedEnvVars.ANTHROPIC_CUSTOM_MODEL_OPTION = normalized.model.name;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
const envVars = {
|
|
909
|
+
...normalized.envVars ?? {},
|
|
910
|
+
...generatedEnvVars
|
|
911
|
+
};
|
|
912
|
+
return {
|
|
913
|
+
runtime: normalized.runtime,
|
|
914
|
+
model: runtimeConfigModelValue(normalized),
|
|
915
|
+
mode: normalized.mode,
|
|
916
|
+
reasoningEffort: normalized.reasoningEffort ?? null,
|
|
917
|
+
envVars: Object.keys(envVars).length > 0 ? envVars : null
|
|
918
|
+
};
|
|
919
|
+
}
|
|
737
920
|
var PLAN_CONFIG = {
|
|
738
921
|
free: {
|
|
739
922
|
displayName: "Hobby",
|
|
@@ -769,17 +952,14 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
769
952
|
};
|
|
770
953
|
|
|
771
954
|
// src/agentProcessManager.ts
|
|
772
|
-
import { mkdirSync as mkdirSync4, readdirSync
|
|
955
|
+
import { mkdirSync as mkdirSync4, readdirSync, statSync, writeFileSync as writeFileSync7 } from "fs";
|
|
773
956
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
774
957
|
import { createHash as createHash2 } from "crypto";
|
|
775
958
|
import path12 from "path";
|
|
776
|
-
import
|
|
959
|
+
import os5 from "os";
|
|
777
960
|
|
|
778
961
|
// src/drivers/claude.ts
|
|
779
962
|
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
963
|
|
|
784
964
|
// src/drivers/cliTransport.ts
|
|
785
965
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
@@ -881,13 +1061,14 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
|
|
|
881
1061
|
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
1062
|
20. **\`slock integration list\`** \u2014 List registered third-party services and this agent's active Slock Agent Logins.
|
|
883
1063
|
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
|
|
1064
|
+
22. **\`slock integration env\`** \u2014 Print per-agent local CLI environment for a manifest-backed service that requires isolated HOME/XDG state.
|
|
1065
|
+
23. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
1066
|
+
24. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
|
|
1067
|
+
25. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
|
|
1068
|
+
26. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
|
|
1069
|
+
27. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
|
|
1070
|
+
28. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
|
|
1071
|
+
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
1072
|
|
|
892
1073
|
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
1074
|
- failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
|
|
@@ -993,7 +1174,7 @@ Each channel has a **name** and optionally a **description** that define its pur
|
|
|
993
1174
|
- If unsure where something belongs, call ${serverInfoCmd} to review channel descriptions.`;
|
|
994
1175
|
const thirdPartyIntegrationsSection = isCli ? `### Third-party integrations
|
|
995
1176
|
|
|
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
|
|
1177
|
+
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
1178
|
|
|
998
1179
|
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
1180
|
const readingHistorySection = isCli ? `### Reading history
|
|
@@ -1368,6 +1549,17 @@ var proxyServerState = null;
|
|
|
1368
1549
|
var proxyServerStartPromise = null;
|
|
1369
1550
|
var proxyServerFactory = createProxyServer;
|
|
1370
1551
|
var DECODED_RESPONSE_HEADERS = /* @__PURE__ */ new Set(["content-encoding", "content-length", "transfer-encoding"]);
|
|
1552
|
+
var HOP_BY_HOP_REQUEST_HEADERS = /* @__PURE__ */ new Set([
|
|
1553
|
+
"connection",
|
|
1554
|
+
"keep-alive",
|
|
1555
|
+
"proxy-authenticate",
|
|
1556
|
+
"proxy-authorization",
|
|
1557
|
+
"proxy-connection",
|
|
1558
|
+
"te",
|
|
1559
|
+
"trailer",
|
|
1560
|
+
"transfer-encoding",
|
|
1561
|
+
"upgrade"
|
|
1562
|
+
]);
|
|
1371
1563
|
var LOCAL_HELD_CONTEXT_LIMIT = 3;
|
|
1372
1564
|
var AGENT_CREDENTIAL_PROXY_HOST = "127.0.0.1";
|
|
1373
1565
|
var AGENT_CREDENTIAL_PROXY_BIND_MAX_ATTEMPTS = 3;
|
|
@@ -1467,9 +1659,12 @@ async function handleProxyRequest(req, res) {
|
|
|
1467
1659
|
target = new URL2(req.url ?? "/", registration.serverUrl);
|
|
1468
1660
|
const headers = new Headers();
|
|
1469
1661
|
for (const [name, value] of Object.entries(req.headers)) {
|
|
1662
|
+
const normalizedName = name.toLowerCase();
|
|
1470
1663
|
if (value === void 0) continue;
|
|
1471
|
-
if (
|
|
1472
|
-
if (
|
|
1664
|
+
if (normalizedName === "host") continue;
|
|
1665
|
+
if (normalizedName === "authorization") continue;
|
|
1666
|
+
if (normalizedName === "content-length") continue;
|
|
1667
|
+
if (HOP_BY_HOP_REQUEST_HEADERS.has(normalizedName)) continue;
|
|
1473
1668
|
if (Array.isArray(value)) {
|
|
1474
1669
|
for (const item of value) headers.append(name, item);
|
|
1475
1670
|
} else {
|
|
@@ -1480,7 +1675,15 @@ async function handleProxyRequest(req, res) {
|
|
|
1480
1675
|
headers.set("X-Agent-Id", registration.agentId);
|
|
1481
1676
|
headers.set("X-Slock-Client", "cli");
|
|
1482
1677
|
headers.set("X-Slock-Agent-Active-Capabilities", registration.activeCapabilities);
|
|
1483
|
-
let body
|
|
1678
|
+
let body;
|
|
1679
|
+
let rawBodyBuffer;
|
|
1680
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
1681
|
+
rawBodyBuffer = await readRequestBody(req);
|
|
1682
|
+
const bodyBuffer = new ArrayBuffer(rawBodyBuffer.byteLength);
|
|
1683
|
+
new Uint8Array(bodyBuffer).set(rawBodyBuffer);
|
|
1684
|
+
body = bodyBuffer;
|
|
1685
|
+
headers.set("content-length", String(rawBodyBuffer.byteLength));
|
|
1686
|
+
}
|
|
1484
1687
|
let sendTarget;
|
|
1485
1688
|
const sideEffectAction = agentApiSideEffectAction(target.pathname);
|
|
1486
1689
|
if (method === "GET" && target.pathname === "/internal/agent-api/events") {
|
|
@@ -1492,7 +1695,7 @@ async function handleProxyRequest(req, res) {
|
|
|
1492
1695
|
}
|
|
1493
1696
|
}
|
|
1494
1697
|
if (method === "POST" && sideEffectAction) {
|
|
1495
|
-
const rawBody =
|
|
1698
|
+
const rawBody = rawBodyBuffer?.toString("utf8") ?? "";
|
|
1496
1699
|
const prepared = await prepareAgentApiSideEffectForward(registration, headers, rawBody, sideEffectAction);
|
|
1497
1700
|
if (prepared.localResponse) {
|
|
1498
1701
|
const responseText = JSON.stringify(prepared.localResponse);
|
|
@@ -1508,10 +1711,13 @@ async function handleProxyRequest(req, res) {
|
|
|
1508
1711
|
const upstream = await daemonFetch(target, {
|
|
1509
1712
|
method,
|
|
1510
1713
|
headers,
|
|
1511
|
-
body
|
|
1512
|
-
// Required by undici when forwarding a Node stream.
|
|
1513
|
-
duplex: body ? "half" : void 0
|
|
1714
|
+
body
|
|
1514
1715
|
});
|
|
1716
|
+
if (upstream.status >= 500) {
|
|
1717
|
+
registration.inboxCoordinator?.recordTransportNormalizedError?.(
|
|
1718
|
+
transportNormalizedErrorForHttpStatus(target, upstream.status, registration.launchId)
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1515
1721
|
if (shouldBufferJsonResponse(upstream, target.pathname, registration)) {
|
|
1516
1722
|
const responseText = await upstream.text();
|
|
1517
1723
|
consumeVisibleResponse(registration, target, sendTarget, responseText);
|
|
@@ -1534,9 +1740,12 @@ async function handleProxyRequest(req, res) {
|
|
|
1534
1740
|
} catch (err) {
|
|
1535
1741
|
const failure = proxyFailureForError(method, target, err);
|
|
1536
1742
|
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}`
|
|
1743
|
+
`[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
1744
|
);
|
|
1539
1745
|
registration.inboxCoordinator?.recordProxyFailure?.(failure);
|
|
1746
|
+
registration.inboxCoordinator?.recordTransportNormalizedError?.(
|
|
1747
|
+
transportNormalizedErrorForError(target, err, registration.launchId)
|
|
1748
|
+
);
|
|
1540
1749
|
writeProxyFailureResponse(res, failure);
|
|
1541
1750
|
}
|
|
1542
1751
|
}
|
|
@@ -1555,25 +1764,125 @@ function writeProxyFailureResponse(res, failure) {
|
|
|
1555
1764
|
}
|
|
1556
1765
|
function proxyFailureForError(method, target, err) {
|
|
1557
1766
|
const queryKeys = target ? [.../* @__PURE__ */ new Set([...target.searchParams.keys()])].sort() : [];
|
|
1558
|
-
|
|
1767
|
+
const cause = err instanceof Error ? err.cause : void 0;
|
|
1768
|
+
const failure = {
|
|
1559
1769
|
method,
|
|
1560
1770
|
pathname: target?.pathname ?? "unknown",
|
|
1561
1771
|
queryKeys,
|
|
1562
1772
|
errorName: err instanceof Error ? err.name : typeof err,
|
|
1563
1773
|
errorMessage: truncateProxyErrorMessage(err instanceof Error ? err.message : String(err))
|
|
1564
1774
|
};
|
|
1775
|
+
const errorCause = describeProxyErrorCause(cause);
|
|
1776
|
+
if (errorCause) failure.errorCause = errorCause;
|
|
1777
|
+
return failure;
|
|
1778
|
+
}
|
|
1779
|
+
function describeProxyErrorCause(cause) {
|
|
1780
|
+
if (!cause) return void 0;
|
|
1781
|
+
if (cause instanceof Error) {
|
|
1782
|
+
const errorWithCode = cause;
|
|
1783
|
+
const code = typeof errorWithCode.code === "string" ? errorWithCode.code : void 0;
|
|
1784
|
+
return truncateProxyErrorMessage([code, cause.message].filter(Boolean).join(" "));
|
|
1785
|
+
}
|
|
1786
|
+
if (typeof cause === "object") {
|
|
1787
|
+
const causeObject = cause;
|
|
1788
|
+
const code = typeof causeObject.code === "string" ? causeObject.code : void 0;
|
|
1789
|
+
const message = typeof causeObject.message === "string" ? causeObject.message : void 0;
|
|
1790
|
+
const detail = [code, message].filter(Boolean).join(" ");
|
|
1791
|
+
if (detail) return truncateProxyErrorMessage(detail);
|
|
1792
|
+
}
|
|
1793
|
+
return truncateProxyErrorMessage(String(cause));
|
|
1565
1794
|
}
|
|
1566
1795
|
function truncateProxyErrorMessage(message) {
|
|
1567
1796
|
const normalized = message.replace(/\s+/g, " ").trim();
|
|
1568
1797
|
return normalized.length > 500 ? `${normalized.slice(0, 497)}...` : normalized;
|
|
1569
1798
|
}
|
|
1799
|
+
function transportNormalizedErrorForHttpStatus(target, status, launchId) {
|
|
1800
|
+
return {
|
|
1801
|
+
normalizedCode: "server_5xx",
|
|
1802
|
+
routeFamily: routeFamilyForPath(target.pathname),
|
|
1803
|
+
responseStarted: true,
|
|
1804
|
+
upstreamLayer: "http_status",
|
|
1805
|
+
upstreamStatus: status,
|
|
1806
|
+
launchId,
|
|
1807
|
+
targetHostClass: daemonUpstreamTargetHostClass(target),
|
|
1808
|
+
// Today the local credential proxy is only used by CLI wrappers. If runtime
|
|
1809
|
+
// or daemon-internal callers use it later, plumb the caller identity here.
|
|
1810
|
+
downstreamCaller: "cli",
|
|
1811
|
+
upstream: "server"
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
function transportNormalizedErrorForError(target, err, launchId) {
|
|
1815
|
+
return {
|
|
1816
|
+
normalizedCode: "transport_failure",
|
|
1817
|
+
routeFamily: routeFamilyForPath(target?.pathname ?? "unknown"),
|
|
1818
|
+
responseStarted: false,
|
|
1819
|
+
upstreamLayer: target ? upstreamLayerForProxyError(err) : "unknown",
|
|
1820
|
+
originalMessage: sanitizeTransportOriginalMessage(err instanceof Error ? err.message : String(err)),
|
|
1821
|
+
launchId,
|
|
1822
|
+
targetHostClass: target ? daemonUpstreamTargetHostClass(target) : "custom_server",
|
|
1823
|
+
// Today the local credential proxy is only used by CLI wrappers. If runtime
|
|
1824
|
+
// or daemon-internal callers use it later, plumb the caller identity here.
|
|
1825
|
+
downstreamCaller: "cli",
|
|
1826
|
+
upstream: "server"
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
function routeFamilyForPath(pathname) {
|
|
1830
|
+
if (pathname === "/internal/agent-api/send") return "agent-api/send";
|
|
1831
|
+
if (pathname === "/internal/agent-api/events") return "agent-api/events";
|
|
1832
|
+
if (pathname === "/internal/agent-api/receive-ack") return "agent-api/events";
|
|
1833
|
+
if (pathname === "/internal/agent-api/tasks/claim") return "tasks/claim";
|
|
1834
|
+
if (pathname === "/internal/agent-api/tasks/update-status") return "tasks/update";
|
|
1835
|
+
if (pathname === "/internal/agent-api/tasks" || pathname.startsWith("/internal/agent-api/tasks/")) return "tasks";
|
|
1836
|
+
if (pathname.startsWith("/internal/agent-api/attachments/")) return "agent-api/attachments";
|
|
1837
|
+
if (/^\/internal\/agent-api\/messages\/[^/]+\/reactions$/.test(pathname)) return "agent-api/messages/reactions";
|
|
1838
|
+
if (pathname === "/internal/agent-api/server") return "server";
|
|
1839
|
+
if (pathname.startsWith("/internal/agent-api/history")) return "agent-api/events";
|
|
1840
|
+
if (pathname.startsWith("/internal/agent-api/search")) return "agent-api/events";
|
|
1841
|
+
if (pathname.startsWith("/internal/agent-api/channel-members")) return "channel-members";
|
|
1842
|
+
if (pathname.startsWith("/internal/agent-api/knowledge")) return "knowledge";
|
|
1843
|
+
if (pathname === "/internal/agent-api/profile" || pathname.startsWith("/internal/agent-api/profile/")) return "profile";
|
|
1844
|
+
if (pathname === "/internal/agent-api/integrations" || pathname.startsWith("/internal/agent-api/integrations/")) return "integrations";
|
|
1845
|
+
if (pathname === "/internal/agent-api/upload") return "attachments/upload";
|
|
1846
|
+
if (pathname === "/internal/agent-api/resolve-channel") return "resolve-channel";
|
|
1847
|
+
if (pathname === "/internal/agent-api/threads/unfollow") return "threads/unfollow";
|
|
1848
|
+
if (pathname === "/internal/agent-api/prepare-action") return "action/prepare";
|
|
1849
|
+
if (pathname === "/internal/agent-api/reminders" || pathname.startsWith("/internal/agent-api/reminders/")) return "reminders";
|
|
1850
|
+
if (/^\/internal\/agent-api\/channels\/[^/]+\/join$/.test(pathname)) return "channels/join";
|
|
1851
|
+
if (/^\/internal\/agent-api\/channels\/[^/]+\/leave$/.test(pathname)) return "channels/leave";
|
|
1852
|
+
return "unknown";
|
|
1853
|
+
}
|
|
1854
|
+
function daemonUpstreamTargetHostClass(url) {
|
|
1855
|
+
const hostname = url.hostname.toLowerCase();
|
|
1856
|
+
if (hostname === "api.slock.ai") return "api.slock.ai";
|
|
1857
|
+
return "custom_server";
|
|
1858
|
+
}
|
|
1859
|
+
function upstreamLayerForProxyError(err) {
|
|
1860
|
+
const code = errorCode(err).toUpperCase();
|
|
1861
|
+
const message = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
1862
|
+
if (code === "ENOTFOUND" || code === "EAI_AGAIN" || message.includes("dns")) return "dns";
|
|
1863
|
+
if (code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "UND_ERR_SOCKET" || message.includes("socket")) return "tcp";
|
|
1864
|
+
if (code.includes("TLS") || message.includes("certificate") || message.includes("tls")) return "tls";
|
|
1865
|
+
if (code === "UND_ERR_HEADERS_TIMEOUT" || code === "UND_ERR_BODY_TIMEOUT" || message.includes("timeout")) return "read_timeout";
|
|
1866
|
+
if (message.includes("proxy")) return "proxy_connect";
|
|
1867
|
+
if (message.includes("fly")) return "fly_edge";
|
|
1868
|
+
return "unknown";
|
|
1869
|
+
}
|
|
1870
|
+
function errorCode(err) {
|
|
1871
|
+
if (typeof err === "object" && err && "code" in err && typeof err.code === "string") {
|
|
1872
|
+
return err.code;
|
|
1873
|
+
}
|
|
1874
|
+
if (typeof err === "object" && err && "cause" in err) return errorCode(err.cause);
|
|
1875
|
+
return "";
|
|
1876
|
+
}
|
|
1877
|
+
function sanitizeTransportOriginalMessage(message) {
|
|
1878
|
+
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]");
|
|
1879
|
+
}
|
|
1570
1880
|
async function readRequestBody(req) {
|
|
1571
|
-
|
|
1572
|
-
req.setEncoding("utf8");
|
|
1881
|
+
const chunks = [];
|
|
1573
1882
|
for await (const chunk of req) {
|
|
1574
|
-
|
|
1883
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
1575
1884
|
}
|
|
1576
|
-
return
|
|
1885
|
+
return Buffer.concat(chunks);
|
|
1577
1886
|
}
|
|
1578
1887
|
function messageSeq(message) {
|
|
1579
1888
|
return Number(message.seq ?? 0);
|
|
@@ -1748,7 +2057,7 @@ async function loadRecentTargetMessages(registration, headers, target) {
|
|
|
1748
2057
|
const historyHeaders = new Headers(headers);
|
|
1749
2058
|
historyHeaders.delete("content-length");
|
|
1750
2059
|
historyHeaders.delete("content-type");
|
|
1751
|
-
const res = await
|
|
2060
|
+
const res = await fetch(historyUrl, { method: "GET", headers: historyHeaders });
|
|
1752
2061
|
if (!res.ok) return [];
|
|
1753
2062
|
const parsed = await res.json().catch(() => null);
|
|
1754
2063
|
return Array.isArray(parsed?.messages) ? normalizeVisibleMessages(parsed.messages, target) : [];
|
|
@@ -1949,8 +2258,9 @@ function unregisterAgentCredentialProxyForLaunch(input) {
|
|
|
1949
2258
|
// src/drivers/cliTransport.ts
|
|
1950
2259
|
var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1951
2260
|
var powershellSingleQuote = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
1952
|
-
var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels";
|
|
2261
|
+
var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels,knowledge";
|
|
1953
2262
|
var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
|
|
2263
|
+
var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
|
|
1954
2264
|
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
1955
2265
|
var RAW_CREDENTIAL_ENV_DENYLIST = [
|
|
1956
2266
|
"SLOCK_AGENT_CREDENTIAL_KEY"
|
|
@@ -2152,16 +2462,18 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2152
2462
|
}
|
|
2153
2463
|
}
|
|
2154
2464
|
const wrapperPath = platform === "win32" ? path2.join(slockDir, "slock.cmd") : posixWrapper;
|
|
2465
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
2155
2466
|
const spawnEnv = {
|
|
2156
2467
|
...process.env,
|
|
2157
2468
|
FORCE_COLOR: "0",
|
|
2158
|
-
...
|
|
2469
|
+
...launchRuntimeFields.envVars || {},
|
|
2159
2470
|
...extraEnv,
|
|
2160
2471
|
...platform === "win32" ? windowsUtf8Env() : {},
|
|
2161
2472
|
...runtimeContextEnv(ctx.config),
|
|
2162
2473
|
[SLOCK_HOME_ENV]: slockHome,
|
|
2163
2474
|
SLOCK_AGENT_ID: ctx.agentId,
|
|
2164
2475
|
...ctx.launchId ? { SLOCK_AGENT_LAUNCH_ID: ctx.launchId } : {},
|
|
2476
|
+
...ctx.cliTransportTraceDir ? { [CLI_TRANSPORT_TRACE_DIR_ENV]: ctx.cliTransportTraceDir } : {},
|
|
2165
2477
|
SLOCK_SERVER_URL: ctx.config.serverUrl,
|
|
2166
2478
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
2167
2479
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
@@ -2186,6 +2498,129 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2186
2498
|
};
|
|
2187
2499
|
}
|
|
2188
2500
|
|
|
2501
|
+
// src/drivers/claudeEventNormalizer.ts
|
|
2502
|
+
function collectResultErrorDetail(message, fallback) {
|
|
2503
|
+
const parts = [];
|
|
2504
|
+
if (Array.isArray(message.errors)) {
|
|
2505
|
+
for (const err of message.errors) {
|
|
2506
|
+
if (typeof err === "string" && err.trim()) parts.push(err.trim());
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
if (typeof message.result === "string" && message.result.trim()) {
|
|
2510
|
+
parts.push(message.result.trim());
|
|
2511
|
+
}
|
|
2512
|
+
return parts.join(" | ") || fallback;
|
|
2513
|
+
}
|
|
2514
|
+
function isProviderApiFailureText(value, hasToolUse) {
|
|
2515
|
+
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));
|
|
2516
|
+
}
|
|
2517
|
+
var ClaudeEventNormalizer = class {
|
|
2518
|
+
normalizeLine(line) {
|
|
2519
|
+
let event;
|
|
2520
|
+
try {
|
|
2521
|
+
event = JSON.parse(line);
|
|
2522
|
+
} catch {
|
|
2523
|
+
return [];
|
|
2524
|
+
}
|
|
2525
|
+
const events = [];
|
|
2526
|
+
const pushResultError = (message, fallback) => {
|
|
2527
|
+
events.push({ kind: "error", message: collectResultErrorDetail(message, fallback) });
|
|
2528
|
+
};
|
|
2529
|
+
switch (event.type) {
|
|
2530
|
+
case "system":
|
|
2531
|
+
if (event.subtype === "init" && event.session_id) {
|
|
2532
|
+
events.push({ kind: "session_init", sessionId: event.session_id });
|
|
2533
|
+
}
|
|
2534
|
+
if (event.subtype === "status" && event.status === "compacting") {
|
|
2535
|
+
events.push({ kind: "compaction_started" });
|
|
2536
|
+
}
|
|
2537
|
+
if (event.subtype === "status" && event.status === "requesting") {
|
|
2538
|
+
events.push({
|
|
2539
|
+
kind: "internal_progress",
|
|
2540
|
+
source: "claude_system_status",
|
|
2541
|
+
itemType: "requesting",
|
|
2542
|
+
payloadBytes: Buffer.byteLength(line, "utf8")
|
|
2543
|
+
});
|
|
2544
|
+
}
|
|
2545
|
+
if (event.subtype === "compact_boundary") {
|
|
2546
|
+
events.push({ kind: "compaction_finished" });
|
|
2547
|
+
}
|
|
2548
|
+
break;
|
|
2549
|
+
case "stream_event":
|
|
2550
|
+
events.push({
|
|
2551
|
+
kind: "internal_progress",
|
|
2552
|
+
source: "claude_stream_event",
|
|
2553
|
+
itemType: typeof event.event?.type === "string" && event.event.type.length > 0 ? event.event.type : "unknown",
|
|
2554
|
+
payloadBytes: Buffer.byteLength(line, "utf8")
|
|
2555
|
+
});
|
|
2556
|
+
break;
|
|
2557
|
+
case "assistant": {
|
|
2558
|
+
const content = event.message?.content;
|
|
2559
|
+
if (Array.isArray(content)) {
|
|
2560
|
+
const hasToolUse = content.some((block) => block?.type === "tool_use");
|
|
2561
|
+
for (const block of content) {
|
|
2562
|
+
if (block.type === "thinking" && block.thinking) {
|
|
2563
|
+
events.push({ kind: "thinking", text: block.thinking });
|
|
2564
|
+
} else if (block.type === "text" && block.text) {
|
|
2565
|
+
if (isProviderApiFailureText(block.text, hasToolUse)) {
|
|
2566
|
+
events.push({ kind: "error", message: block.text });
|
|
2567
|
+
} else {
|
|
2568
|
+
events.push({ kind: "text", text: block.text });
|
|
2569
|
+
}
|
|
2570
|
+
} else if (block.type === "tool_use") {
|
|
2571
|
+
events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
break;
|
|
2576
|
+
}
|
|
2577
|
+
case "user": {
|
|
2578
|
+
const content = event.message?.content;
|
|
2579
|
+
if (Array.isArray(content)) {
|
|
2580
|
+
for (const block of content) {
|
|
2581
|
+
if (block.type === "tool_result") {
|
|
2582
|
+
events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result" });
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
break;
|
|
2587
|
+
}
|
|
2588
|
+
case "result": {
|
|
2589
|
+
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
2590
|
+
const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
|
|
2591
|
+
switch (subtype) {
|
|
2592
|
+
case "success":
|
|
2593
|
+
if (event.is_error && stopReason !== "max_tokens") {
|
|
2594
|
+
pushResultError(event, "Execution failed");
|
|
2595
|
+
}
|
|
2596
|
+
break;
|
|
2597
|
+
case "error_during_execution":
|
|
2598
|
+
if (stopReason !== "max_tokens") {
|
|
2599
|
+
pushResultError(event, "Execution failed");
|
|
2600
|
+
}
|
|
2601
|
+
break;
|
|
2602
|
+
case "error_max_budget_usd":
|
|
2603
|
+
pushResultError(event, "Budget limit exceeded");
|
|
2604
|
+
break;
|
|
2605
|
+
case "error_max_turns":
|
|
2606
|
+
pushResultError(event, "Max turns exceeded");
|
|
2607
|
+
break;
|
|
2608
|
+
case "error_max_structured_output_retries":
|
|
2609
|
+
pushResultError(event, "Structured output retries exceeded");
|
|
2610
|
+
break;
|
|
2611
|
+
}
|
|
2612
|
+
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
2613
|
+
break;
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
return events;
|
|
2617
|
+
}
|
|
2618
|
+
};
|
|
2619
|
+
|
|
2620
|
+
// src/drivers/claudeLaunch.ts
|
|
2621
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
2622
|
+
import path4 from "path";
|
|
2623
|
+
|
|
2189
2624
|
// src/drivers/probe.ts
|
|
2190
2625
|
import { execFileSync } from "child_process";
|
|
2191
2626
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -2386,12 +2821,10 @@ function resolveHomePath(relativePath, deps = {}) {
|
|
|
2386
2821
|
return path3.join(homeDir, relativePath);
|
|
2387
2822
|
}
|
|
2388
2823
|
|
|
2389
|
-
// src/drivers/
|
|
2824
|
+
// src/drivers/claudeLaunch.ts
|
|
2390
2825
|
var CLAUDE_DESKTOP_CLI_RELATIVE_PATH = path4.join("Applications", "Claude Code URL Handler.app", "Contents", "MacOS", "claude");
|
|
2391
2826
|
var CLAUDE_DESKTOP_CLI_SYSTEM_PATH = "/Applications/Claude Code URL Handler.app/Contents/MacOS/claude";
|
|
2392
2827
|
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
2828
|
var CLAUDE_DISALLOWED_TOOLS = [
|
|
2396
2829
|
"EnterPlanMode",
|
|
2397
2830
|
"ExitPlanMode",
|
|
@@ -2417,81 +2850,54 @@ function probeClaude(deps = {}) {
|
|
|
2417
2850
|
version: readCommandVersion(command, [], deps) ?? void 0
|
|
2418
2851
|
};
|
|
2419
2852
|
}
|
|
2420
|
-
function
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2853
|
+
function buildClaudeArgs(config, opts) {
|
|
2854
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(config);
|
|
2855
|
+
const args = [
|
|
2856
|
+
"--allow-dangerously-skip-permissions",
|
|
2857
|
+
"--dangerously-skip-permissions",
|
|
2858
|
+
"--verbose",
|
|
2859
|
+
"--permission-mode",
|
|
2860
|
+
"bypassPermissions",
|
|
2861
|
+
"--output-format",
|
|
2862
|
+
"stream-json",
|
|
2863
|
+
"--input-format",
|
|
2864
|
+
"stream-json",
|
|
2865
|
+
"--include-partial-messages",
|
|
2866
|
+
"--model",
|
|
2867
|
+
launchRuntimeFields.model || "sonnet",
|
|
2868
|
+
"--disallowed-tools",
|
|
2869
|
+
CLAUDE_DISALLOWED_TOOLS,
|
|
2870
|
+
"--append-system-prompt-file",
|
|
2871
|
+
opts.standingPromptFilePath
|
|
2872
|
+
];
|
|
2873
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
2874
|
+
args.push("--effort", launchRuntimeFields.reasoningEffort);
|
|
2428
2875
|
}
|
|
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;
|
|
2876
|
+
if (launchRuntimeFields.mode.kind === "fast") {
|
|
2877
|
+
args.push("--bare");
|
|
2443
2878
|
}
|
|
2879
|
+
if (config.sessionId) {
|
|
2880
|
+
args.push("--resume", config.sessionId);
|
|
2881
|
+
}
|
|
2882
|
+
return args;
|
|
2444
2883
|
}
|
|
2445
|
-
function
|
|
2446
|
-
const
|
|
2447
|
-
|
|
2884
|
+
function writeClaudeSystemPromptFile(standingPrompt, slockDir) {
|
|
2885
|
+
const systemPromptPath = path4.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
|
|
2886
|
+
writeFileSync2(systemPromptPath, standingPrompt, { mode: 384 });
|
|
2887
|
+
return systemPromptPath;
|
|
2448
2888
|
}
|
|
2449
|
-
function
|
|
2450
|
-
const
|
|
2451
|
-
const
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
}
|
|
2889
|
+
function buildClaudeSpawnSpec(claudeCommand, platform = process.platform) {
|
|
2890
|
+
const lowerClaudeCommand = claudeCommand?.toLowerCase();
|
|
2891
|
+
const isBatchFile = Boolean(
|
|
2892
|
+
platform === "win32" && lowerClaudeCommand && (lowerClaudeCommand.endsWith(".cmd") || lowerClaudeCommand.endsWith(".bat"))
|
|
2893
|
+
);
|
|
2894
|
+
return {
|
|
2895
|
+
command: claudeCommand ?? "claude",
|
|
2896
|
+
shell: platform === "win32" && (!claudeCommand || isBatchFile)
|
|
2458
2897
|
};
|
|
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
2898
|
}
|
|
2899
|
+
|
|
2900
|
+
// src/drivers/claude.ts
|
|
2495
2901
|
var ClaudeDriver = class {
|
|
2496
2902
|
id = "claude";
|
|
2497
2903
|
lifecycle = {
|
|
@@ -2501,7 +2907,7 @@ var ClaudeDriver = class {
|
|
|
2501
2907
|
};
|
|
2502
2908
|
communication = {
|
|
2503
2909
|
chat: "slock_cli",
|
|
2504
|
-
runtimeControl: "
|
|
2910
|
+
runtimeControl: "none"
|
|
2505
2911
|
};
|
|
2506
2912
|
session = {
|
|
2507
2913
|
recovery: "resume_or_fresh"
|
|
@@ -2511,107 +2917,37 @@ var ClaudeDriver = class {
|
|
|
2511
2917
|
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
2512
2918
|
};
|
|
2513
2919
|
supportsStdinNotification = true;
|
|
2514
|
-
mcpToolPrefix = "
|
|
2920
|
+
mcpToolPrefix = "";
|
|
2515
2921
|
usesSlockCliForCommunication = true;
|
|
2516
2922
|
// Claude Code supports same-turn steering, but raw stdin injection at an
|
|
2517
2923
|
// arbitrary busy instant can collide with active signed thinking blocks. The
|
|
2518
2924
|
// daemon therefore gates busy delivery on Claude stream-json boundaries.
|
|
2519
2925
|
busyDeliveryMode = "gated";
|
|
2520
2926
|
supportsNativeStandingPrompt = true;
|
|
2927
|
+
eventNormalizer = new ClaudeEventNormalizer();
|
|
2521
2928
|
probe() {
|
|
2522
2929
|
return probeClaude();
|
|
2523
2930
|
}
|
|
2524
|
-
buildClaudeArgs(config,
|
|
2525
|
-
|
|
2526
|
-
"--allow-dangerously-skip-permissions",
|
|
2527
|
-
"--dangerously-skip-permissions",
|
|
2528
|
-
"--verbose",
|
|
2529
|
-
"--permission-mode",
|
|
2530
|
-
"bypassPermissions",
|
|
2531
|
-
"--output-format",
|
|
2532
|
-
"stream-json",
|
|
2533
|
-
"--input-format",
|
|
2534
|
-
"stream-json",
|
|
2535
|
-
"--model",
|
|
2536
|
-
config.model || "sonnet",
|
|
2537
|
-
"--disallowed-tools",
|
|
2538
|
-
CLAUDE_DISALLOWED_TOOLS
|
|
2539
|
-
];
|
|
2540
|
-
if (opts.standingPromptFilePath) {
|
|
2541
|
-
args.push("--append-system-prompt-file", opts.standingPromptFilePath);
|
|
2542
|
-
} else {
|
|
2543
|
-
args.push("--append-system-prompt", standingPrompt);
|
|
2544
|
-
}
|
|
2545
|
-
if (config.sessionId) {
|
|
2546
|
-
args.push("--resume", config.sessionId);
|
|
2547
|
-
}
|
|
2548
|
-
return args;
|
|
2549
|
-
}
|
|
2550
|
-
buildRuntimeActionsMcpServer(ctx) {
|
|
2551
|
-
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
2552
|
-
const command = isTsSource ? "npx" : "node";
|
|
2553
|
-
const bridgeArgs = isTsSource ? ["tsx", ctx.chatBridgePath] : [ctx.chatBridgePath];
|
|
2554
|
-
return {
|
|
2555
|
-
command,
|
|
2556
|
-
args: [
|
|
2557
|
-
...bridgeArgs,
|
|
2558
|
-
"--agent-id",
|
|
2559
|
-
ctx.agentId,
|
|
2560
|
-
"--server-url",
|
|
2561
|
-
ctx.config.serverUrl,
|
|
2562
|
-
"--auth-token",
|
|
2563
|
-
ctx.config.authToken || ctx.daemonApiKey,
|
|
2564
|
-
"--runtime",
|
|
2565
|
-
this.id,
|
|
2566
|
-
...ctx.launchId ? ["--launch-id", ctx.launchId] : [],
|
|
2567
|
-
"--runtime-actions-only"
|
|
2568
|
-
]
|
|
2569
|
-
};
|
|
2570
|
-
}
|
|
2571
|
-
buildRuntimeActionsMcpConfig(ctx, home = os2.homedir()) {
|
|
2572
|
-
const userMcpServers = buildClaudeUserMcpServers(ctx, home);
|
|
2573
|
-
if (Object.prototype.hasOwnProperty.call(userMcpServers, SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME)) {
|
|
2574
|
-
logger.warn(
|
|
2575
|
-
`[Agent ${ctx.agentId}] Claude user MCP server "${SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME}" is reserved by Slock runtime actions and will be ignored`
|
|
2576
|
-
);
|
|
2577
|
-
}
|
|
2578
|
-
return JSON.stringify({
|
|
2579
|
-
mcpServers: {
|
|
2580
|
-
...userMcpServers,
|
|
2581
|
-
[SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME]: this.buildRuntimeActionsMcpServer(ctx)
|
|
2582
|
-
}
|
|
2583
|
-
});
|
|
2584
|
-
}
|
|
2585
|
-
writeClaudeLaunchFiles(ctx, slockDir, home = os2.homedir()) {
|
|
2586
|
-
const systemPromptPath = path4.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
|
|
2587
|
-
const mcpConfigPath = path4.join(slockDir, CLAUDE_MCP_CONFIG_FILE);
|
|
2588
|
-
writeFileSync2(systemPromptPath, ctx.standingPrompt, { mode: 384 });
|
|
2589
|
-
writeFileSync2(mcpConfigPath, this.buildRuntimeActionsMcpConfig(ctx, home), { mode: 384 });
|
|
2590
|
-
return { systemPromptPath, mcpConfigPath };
|
|
2931
|
+
buildClaudeArgs(config, opts) {
|
|
2932
|
+
return buildClaudeArgs(config, opts);
|
|
2591
2933
|
}
|
|
2592
2934
|
async spawn(ctx) {
|
|
2593
2935
|
const { slockDir, tokenFile, spawnEnv } = await prepareCliTransport(ctx);
|
|
2594
|
-
const
|
|
2595
|
-
const args = this.buildClaudeArgs(ctx.config,
|
|
2936
|
+
const systemPromptPath = writeClaudeSystemPromptFile(ctx.standingPrompt, slockDir);
|
|
2937
|
+
const args = this.buildClaudeArgs(ctx.config, {
|
|
2596
2938
|
standingPromptFilePath: systemPromptPath
|
|
2597
2939
|
});
|
|
2598
|
-
args.push("--mcp-config", mcpConfigPath, "--strict-mcp-config");
|
|
2599
2940
|
delete spawnEnv.CLAUDECODE;
|
|
2600
2941
|
logger.info(
|
|
2601
2942
|
`[Agent ${ctx.agentId}] transport=cli cli=${ctx.slockCliPath} token_file=${tokenFile}`
|
|
2602
2943
|
);
|
|
2603
2944
|
const claudeCommand = resolveClaudeCommand();
|
|
2604
|
-
const
|
|
2605
|
-
const
|
|
2606
|
-
const isBatchFile = Boolean(
|
|
2607
|
-
isWindows && lowerClaudeCommand && (lowerClaudeCommand.endsWith(".cmd") || lowerClaudeCommand.endsWith(".bat"))
|
|
2608
|
-
);
|
|
2609
|
-
const useShell = isWindows && (!claudeCommand || isBatchFile);
|
|
2610
|
-
const proc = spawn(claudeCommand ?? "claude", args, {
|
|
2945
|
+
const spawnSpec = buildClaudeSpawnSpec(claudeCommand);
|
|
2946
|
+
const proc = spawn(spawnSpec.command, args, {
|
|
2611
2947
|
cwd: ctx.workingDirectory,
|
|
2612
2948
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2613
2949
|
env: spawnEnv,
|
|
2614
|
-
shell:
|
|
2950
|
+
shell: spawnSpec.shell
|
|
2615
2951
|
});
|
|
2616
2952
|
const stdinMsg = JSON.stringify({
|
|
2617
2953
|
type: "user",
|
|
@@ -2625,99 +2961,7 @@ var ClaudeDriver = class {
|
|
|
2625
2961
|
return { process: proc };
|
|
2626
2962
|
}
|
|
2627
2963
|
parseLine(line) {
|
|
2628
|
-
|
|
2629
|
-
try {
|
|
2630
|
-
event = JSON.parse(line);
|
|
2631
|
-
} catch {
|
|
2632
|
-
return [];
|
|
2633
|
-
}
|
|
2634
|
-
const events = [];
|
|
2635
|
-
const pushResultError = (message, fallback) => {
|
|
2636
|
-
const parts = [];
|
|
2637
|
-
if (Array.isArray(message.errors)) {
|
|
2638
|
-
for (const err of message.errors) {
|
|
2639
|
-
if (typeof err === "string" && err.trim()) parts.push(err.trim());
|
|
2640
|
-
}
|
|
2641
|
-
}
|
|
2642
|
-
if (typeof message.result === "string" && message.result.trim()) {
|
|
2643
|
-
parts.push(message.result.trim());
|
|
2644
|
-
}
|
|
2645
|
-
const detail = parts.join(" | ") || fallback;
|
|
2646
|
-
events.push({ kind: "error", message: detail });
|
|
2647
|
-
};
|
|
2648
|
-
const isProviderApiFailureText = (value, hasToolUse) => !hasToolUse && /^\s*API Error:/i.test(value) && (/\b(?:ECONNRESET|EPIPE|ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/i.test(value) || /\bUnable to connect to API\b/i.test(value) || /\b(?:timed out|timeout)\b/i.test(value) || /\b5\d{2}\b/.test(value));
|
|
2649
|
-
switch (event.type) {
|
|
2650
|
-
case "system":
|
|
2651
|
-
if (event.subtype === "init" && event.session_id) {
|
|
2652
|
-
events.push({ kind: "session_init", sessionId: event.session_id });
|
|
2653
|
-
}
|
|
2654
|
-
if (event.subtype === "status" && event.status === "compacting") {
|
|
2655
|
-
events.push({ kind: "compaction_started" });
|
|
2656
|
-
}
|
|
2657
|
-
if (event.subtype === "compact_boundary") {
|
|
2658
|
-
events.push({ kind: "compaction_finished" });
|
|
2659
|
-
}
|
|
2660
|
-
break;
|
|
2661
|
-
case "assistant": {
|
|
2662
|
-
const content = event.message?.content;
|
|
2663
|
-
if (Array.isArray(content)) {
|
|
2664
|
-
const hasToolUse = content.some((block) => block?.type === "tool_use");
|
|
2665
|
-
for (const block of content) {
|
|
2666
|
-
if (block.type === "thinking" && block.thinking) {
|
|
2667
|
-
events.push({ kind: "thinking", text: block.thinking });
|
|
2668
|
-
} else if (block.type === "text" && block.text) {
|
|
2669
|
-
if (isProviderApiFailureText(block.text, hasToolUse)) {
|
|
2670
|
-
events.push({ kind: "error", message: block.text });
|
|
2671
|
-
} else {
|
|
2672
|
-
events.push({ kind: "text", text: block.text });
|
|
2673
|
-
}
|
|
2674
|
-
} else if (block.type === "tool_use") {
|
|
2675
|
-
events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
|
|
2676
|
-
}
|
|
2677
|
-
}
|
|
2678
|
-
}
|
|
2679
|
-
break;
|
|
2680
|
-
}
|
|
2681
|
-
case "user": {
|
|
2682
|
-
const content = event.message?.content;
|
|
2683
|
-
if (Array.isArray(content)) {
|
|
2684
|
-
for (const block of content) {
|
|
2685
|
-
if (block.type === "tool_result") {
|
|
2686
|
-
events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result" });
|
|
2687
|
-
}
|
|
2688
|
-
}
|
|
2689
|
-
}
|
|
2690
|
-
break;
|
|
2691
|
-
}
|
|
2692
|
-
case "result": {
|
|
2693
|
-
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
2694
|
-
const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
|
|
2695
|
-
switch (subtype) {
|
|
2696
|
-
case "success":
|
|
2697
|
-
if (event.is_error && stopReason !== "max_tokens") {
|
|
2698
|
-
pushResultError(event, "Execution failed");
|
|
2699
|
-
}
|
|
2700
|
-
break;
|
|
2701
|
-
case "error_during_execution":
|
|
2702
|
-
if (stopReason !== "max_tokens") {
|
|
2703
|
-
pushResultError(event, "Execution failed");
|
|
2704
|
-
}
|
|
2705
|
-
break;
|
|
2706
|
-
case "error_max_budget_usd":
|
|
2707
|
-
pushResultError(event, "Budget limit exceeded");
|
|
2708
|
-
break;
|
|
2709
|
-
case "error_max_turns":
|
|
2710
|
-
pushResultError(event, "Max turns exceeded");
|
|
2711
|
-
break;
|
|
2712
|
-
case "error_max_structured_output_retries":
|
|
2713
|
-
pushResultError(event, "Structured output retries exceeded");
|
|
2714
|
-
break;
|
|
2715
|
-
}
|
|
2716
|
-
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
2717
|
-
break;
|
|
2718
|
-
}
|
|
2719
|
-
}
|
|
2720
|
-
return events;
|
|
2964
|
+
return this.eventNormalizer.normalizeLine(line);
|
|
2721
2965
|
}
|
|
2722
2966
|
encodeStdinMessage(text, sessionId, _opts) {
|
|
2723
2967
|
return JSON.stringify({
|
|
@@ -2731,7 +2975,7 @@ var ClaudeDriver = class {
|
|
|
2731
2975
|
}
|
|
2732
2976
|
buildSystemPrompt(config, _agentId) {
|
|
2733
2977
|
return buildCliTransportSystemPrompt(config, {
|
|
2734
|
-
toolPrefix: "
|
|
2978
|
+
toolPrefix: "",
|
|
2735
2979
|
extraCriticalRules: [],
|
|
2736
2980
|
postStartupNotes: [
|
|
2737
2981
|
"**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 +2988,8 @@ var ClaudeDriver = class {
|
|
|
2744
2988
|
|
|
2745
2989
|
// src/drivers/codex.ts
|
|
2746
2990
|
import { spawn as spawn2, execFileSync as execFileSync2, execSync } from "child_process";
|
|
2747
|
-
import { existsSync as
|
|
2748
|
-
import
|
|
2991
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
2992
|
+
import os2 from "os";
|
|
2749
2993
|
import path5 from "path";
|
|
2750
2994
|
|
|
2751
2995
|
// src/runtimeTurnState.ts
|
|
@@ -3107,7 +3351,7 @@ var CodexEventNormalizer = class {
|
|
|
3107
3351
|
|
|
3108
3352
|
// src/drivers/codex.ts
|
|
3109
3353
|
function ensureGitRepoForCodex(workingDirectory, deps = {}) {
|
|
3110
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3354
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
3111
3355
|
const execSyncFn = deps.execSyncFn ?? execSync;
|
|
3112
3356
|
const gitDir = path5.join(workingDirectory, ".git");
|
|
3113
3357
|
if (existsSyncFn(gitDir)) return;
|
|
@@ -3125,7 +3369,7 @@ function isWindowsSandboxRunner(commandPath) {
|
|
|
3125
3369
|
return path5.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
|
|
3126
3370
|
}
|
|
3127
3371
|
function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
3128
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3372
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
3129
3373
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
|
|
3130
3374
|
const env = deps.env ?? process.env;
|
|
3131
3375
|
const winPath = path5.win32;
|
|
@@ -3147,7 +3391,7 @@ function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
|
3147
3391
|
return null;
|
|
3148
3392
|
}
|
|
3149
3393
|
function resolveWindowsCodexDesktopEntry(deps = {}) {
|
|
3150
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3394
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
3151
3395
|
const env = deps.env ?? process.env;
|
|
3152
3396
|
const homeDir = deps.homeDir;
|
|
3153
3397
|
const winPath = path5.win32;
|
|
@@ -3299,11 +3543,15 @@ var CodexDriver = class {
|
|
|
3299
3543
|
// the daemon. They replace the previous transcript-mtime heuristic.
|
|
3300
3544
|
experimentalRawEvents: true
|
|
3301
3545
|
};
|
|
3302
|
-
|
|
3303
|
-
|
|
3546
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
3547
|
+
if (launchRuntimeFields.model) {
|
|
3548
|
+
threadParams.model = launchRuntimeFields.model;
|
|
3549
|
+
}
|
|
3550
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
3551
|
+
threadParams.config = { model_reasoning_effort: launchRuntimeFields.reasoningEffort };
|
|
3304
3552
|
}
|
|
3305
|
-
if (
|
|
3306
|
-
threadParams.
|
|
3553
|
+
if (launchRuntimeFields.mode.kind === "fast") {
|
|
3554
|
+
threadParams.serviceTier = "priority";
|
|
3307
3555
|
}
|
|
3308
3556
|
if (ctx.config.sessionId) {
|
|
3309
3557
|
return {
|
|
@@ -3459,12 +3707,12 @@ var CodexDriver = class {
|
|
|
3459
3707
|
return detectCodexModels();
|
|
3460
3708
|
}
|
|
3461
3709
|
};
|
|
3462
|
-
function detectCodexModels(home =
|
|
3710
|
+
function detectCodexModels(home = os2.homedir()) {
|
|
3463
3711
|
const cachePath = path5.join(home, ".codex", "models_cache.json");
|
|
3464
3712
|
const configPath = path5.join(home, ".codex", "config.toml");
|
|
3465
3713
|
let models = [];
|
|
3466
3714
|
try {
|
|
3467
|
-
const raw =
|
|
3715
|
+
const raw = readFileSync2(cachePath, "utf8");
|
|
3468
3716
|
const parsed = JSON.parse(raw);
|
|
3469
3717
|
const entries = Array.isArray(parsed?.models) ? parsed.models : [];
|
|
3470
3718
|
for (const entry of entries) {
|
|
@@ -3481,7 +3729,7 @@ function detectCodexModels(home = os3.homedir()) {
|
|
|
3481
3729
|
if (models.length === 0) return null;
|
|
3482
3730
|
let defaultModel;
|
|
3483
3731
|
try {
|
|
3484
|
-
const raw =
|
|
3732
|
+
const raw = readFileSync2(configPath, "utf8");
|
|
3485
3733
|
const match = raw.match(/^\s*model\s*=\s*"([^"]+)"/m);
|
|
3486
3734
|
if (match) defaultModel = match[1];
|
|
3487
3735
|
} catch {
|
|
@@ -3512,7 +3760,7 @@ function buildAntigravityArgs(ctx) {
|
|
|
3512
3760
|
const args = [
|
|
3513
3761
|
"--print",
|
|
3514
3762
|
"--print-timeout",
|
|
3515
|
-
ctx.config.envVars?.ANTIGRAVITY_PRINT_TIMEOUT || DEFAULT_PRINT_TIMEOUT,
|
|
3763
|
+
runtimeConfigToLaunchFields(ctx.config).envVars?.ANTIGRAVITY_PRINT_TIMEOUT || DEFAULT_PRINT_TIMEOUT,
|
|
3516
3764
|
"--dangerously-skip-permissions"
|
|
3517
3765
|
];
|
|
3518
3766
|
if (ctx.config.sessionId) {
|
|
@@ -3691,11 +3939,12 @@ var CopilotDriver = class {
|
|
|
3691
3939
|
"-p",
|
|
3692
3940
|
ctx.prompt
|
|
3693
3941
|
];
|
|
3694
|
-
|
|
3695
|
-
|
|
3942
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
3943
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
3944
|
+
args.push("--model", launchRuntimeFields.model);
|
|
3696
3945
|
}
|
|
3697
|
-
if (
|
|
3698
|
-
args.push("--effort",
|
|
3946
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
3947
|
+
args.push("--effort", launchRuntimeFields.reasoningEffort);
|
|
3699
3948
|
}
|
|
3700
3949
|
if (ctx.config.sessionId) {
|
|
3701
3950
|
args.push(`--resume=${ctx.config.sessionId}`);
|
|
@@ -3795,7 +4044,7 @@ var CopilotDriver = class {
|
|
|
3795
4044
|
|
|
3796
4045
|
// src/drivers/cursor.ts
|
|
3797
4046
|
import { spawn as spawn5, spawnSync } from "child_process";
|
|
3798
|
-
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as
|
|
4047
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
3799
4048
|
import path7 from "path";
|
|
3800
4049
|
async function buildCursorSpawnEnv(ctx, deps = {}) {
|
|
3801
4050
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
@@ -3851,7 +4100,7 @@ var CursorDriver = class {
|
|
|
3851
4100
|
}
|
|
3852
4101
|
async spawn(ctx) {
|
|
3853
4102
|
const cursorDir = path7.join(ctx.workingDirectory, ".cursor");
|
|
3854
|
-
if (!
|
|
4103
|
+
if (!existsSync4(cursorDir)) {
|
|
3855
4104
|
mkdirSync2(cursorDir, { recursive: true });
|
|
3856
4105
|
}
|
|
3857
4106
|
const mcpConfigPath = path7.join(cursorDir, "mcp.json");
|
|
@@ -3864,8 +4113,9 @@ var CursorDriver = class {
|
|
|
3864
4113
|
"--approve-mcps",
|
|
3865
4114
|
"--trust"
|
|
3866
4115
|
];
|
|
3867
|
-
|
|
3868
|
-
|
|
4116
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4117
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4118
|
+
args.push("--model", launchRuntimeFields.model);
|
|
3869
4119
|
}
|
|
3870
4120
|
if (ctx.config.sessionId) {
|
|
3871
4121
|
args.push("--resume", ctx.config.sessionId);
|
|
@@ -4000,14 +4250,15 @@ function runCursorModelsCommand() {
|
|
|
4000
4250
|
|
|
4001
4251
|
// src/drivers/gemini.ts
|
|
4002
4252
|
import { execFileSync as execFileSync3, spawn as spawn6 } from "child_process";
|
|
4003
|
-
import { existsSync as
|
|
4253
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
4004
4254
|
import path8 from "path";
|
|
4005
4255
|
async function buildGeminiSpawnEnv(ctx, platform = process.platform) {
|
|
4006
4256
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
|
|
4007
|
-
|
|
4257
|
+
const launchEnvVars = runtimeConfigToLaunchFields(ctx.config).envVars;
|
|
4258
|
+
if (!Object.prototype.hasOwnProperty.call(launchEnvVars ?? {}, "GEMINI_CLI_TRUST_WORKSPACE")) {
|
|
4008
4259
|
spawnEnv.GEMINI_CLI_TRUST_WORKSPACE = "true";
|
|
4009
4260
|
}
|
|
4010
|
-
if (platform === "win32" && !Object.prototype.hasOwnProperty.call(
|
|
4261
|
+
if (platform === "win32" && !Object.prototype.hasOwnProperty.call(launchEnvVars ?? {}, "GEMINI_PTY_INFO")) {
|
|
4011
4262
|
spawnEnv.GEMINI_PTY_INFO = "child_process";
|
|
4012
4263
|
}
|
|
4013
4264
|
return spawnEnv;
|
|
@@ -4027,8 +4278,9 @@ function buildGeminiArgs(config) {
|
|
|
4027
4278
|
"-p",
|
|
4028
4279
|
""
|
|
4029
4280
|
];
|
|
4030
|
-
|
|
4031
|
-
|
|
4281
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(config);
|
|
4282
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4283
|
+
args.push("--model", launchRuntimeFields.model);
|
|
4032
4284
|
}
|
|
4033
4285
|
if (config.sessionId) {
|
|
4034
4286
|
args.push("--resume", config.sessionId);
|
|
@@ -4041,7 +4293,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
4041
4293
|
return { command: resolveCommandOnPath("gemini", deps) ?? "gemini", args: commandArgs };
|
|
4042
4294
|
}
|
|
4043
4295
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
4044
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
4296
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
4045
4297
|
const env = deps.env ?? process.env;
|
|
4046
4298
|
const winPath = path8.win32;
|
|
4047
4299
|
let geminiEntry = null;
|
|
@@ -4214,8 +4466,8 @@ var GeminiDriver = class {
|
|
|
4214
4466
|
// src/drivers/kimi.ts
|
|
4215
4467
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4216
4468
|
import { spawn as spawn7 } from "child_process";
|
|
4217
|
-
import { existsSync as
|
|
4218
|
-
import
|
|
4469
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
4470
|
+
import os3 from "os";
|
|
4219
4471
|
import path9 from "path";
|
|
4220
4472
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
4221
4473
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
@@ -4288,7 +4540,7 @@ var KimiDriver = class {
|
|
|
4288
4540
|
const systemPromptPath = path9.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
|
|
4289
4541
|
const agentFilePath = path9.join(ctx.workingDirectory, KIMI_AGENT_FILE);
|
|
4290
4542
|
const mcpConfigPath = path9.join(ctx.workingDirectory, KIMI_MCP_FILE);
|
|
4291
|
-
if (!isResume || !
|
|
4543
|
+
if (!isResume || !existsSync6(systemPromptPath)) {
|
|
4292
4544
|
writeFileSync6(systemPromptPath, ctx.prompt, "utf8");
|
|
4293
4545
|
}
|
|
4294
4546
|
writeFileSync6(agentFilePath, [
|
|
@@ -4316,8 +4568,9 @@ var KimiDriver = class {
|
|
|
4316
4568
|
"--session",
|
|
4317
4569
|
this.sessionId
|
|
4318
4570
|
];
|
|
4319
|
-
|
|
4320
|
-
|
|
4571
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4572
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4573
|
+
args.push("--model", launchRuntimeFields.model);
|
|
4321
4574
|
}
|
|
4322
4575
|
const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
4323
4576
|
const launch = resolveKimiSpawn(args);
|
|
@@ -4443,11 +4696,11 @@ var KimiDriver = class {
|
|
|
4443
4696
|
return detectKimiModels();
|
|
4444
4697
|
}
|
|
4445
4698
|
};
|
|
4446
|
-
function detectKimiModels(home =
|
|
4699
|
+
function detectKimiModels(home = os3.homedir()) {
|
|
4447
4700
|
const configPath = path9.join(home, ".kimi", "config.toml");
|
|
4448
4701
|
let raw;
|
|
4449
4702
|
try {
|
|
4450
|
-
raw =
|
|
4703
|
+
raw = readFileSync3(configPath, "utf8");
|
|
4451
4704
|
} catch {
|
|
4452
4705
|
return null;
|
|
4453
4706
|
}
|
|
@@ -4471,8 +4724,8 @@ function detectKimiModels(home = os4.homedir()) {
|
|
|
4471
4724
|
|
|
4472
4725
|
// src/drivers/opencode.ts
|
|
4473
4726
|
import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
|
|
4474
|
-
import { existsSync as
|
|
4475
|
-
import
|
|
4727
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
|
|
4728
|
+
import os4 from "os";
|
|
4476
4729
|
import path10 from "path";
|
|
4477
4730
|
var CHAT_MCP_SERVER_NAME = "chat";
|
|
4478
4731
|
var CHAT_MCP_TOOL_PREFIX = `${CHAT_MCP_SERVER_NAME}_`;
|
|
@@ -4517,13 +4770,13 @@ function parseOpenCodeConfigContent(raw) {
|
|
|
4517
4770
|
return {};
|
|
4518
4771
|
}
|
|
4519
4772
|
function parseUserOpenCodeConfig(ctx) {
|
|
4520
|
-
const raw = ctx.config.envVars?.OPENCODE_CONFIG_CONTENT;
|
|
4773
|
+
const raw = runtimeConfigToLaunchFields(ctx.config).envVars?.OPENCODE_CONFIG_CONTENT;
|
|
4521
4774
|
return parseOpenCodeConfigContent(raw);
|
|
4522
4775
|
}
|
|
4523
|
-
function readLocalOpenCodeConfig(home =
|
|
4776
|
+
function readLocalOpenCodeConfig(home = os4.homedir()) {
|
|
4524
4777
|
const configPath = path10.join(home, ".config", "opencode", "opencode.json");
|
|
4525
4778
|
try {
|
|
4526
|
-
return parseOpenCodeConfigContent(
|
|
4779
|
+
return parseOpenCodeConfigContent(readFileSync4(configPath, "utf8"));
|
|
4527
4780
|
} catch {
|
|
4528
4781
|
}
|
|
4529
4782
|
return {};
|
|
@@ -4581,7 +4834,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
|
|
|
4581
4834
|
}
|
|
4582
4835
|
};
|
|
4583
4836
|
}
|
|
4584
|
-
function buildOpenCodeConfig(ctx, home =
|
|
4837
|
+
function buildOpenCodeConfig(ctx, home = os4.homedir()) {
|
|
4585
4838
|
const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
|
|
4586
4839
|
const userAgents = recordField(userConfig.agent);
|
|
4587
4840
|
const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
|
|
@@ -4606,7 +4859,7 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
|
|
|
4606
4859
|
}
|
|
4607
4860
|
};
|
|
4608
4861
|
}
|
|
4609
|
-
async function buildOpenCodeLaunchOptions(ctx, home =
|
|
4862
|
+
async function buildOpenCodeLaunchOptions(ctx, home = os4.homedir(), version = null) {
|
|
4610
4863
|
const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
4611
4864
|
const config = buildOpenCodeConfig(ctx, home);
|
|
4612
4865
|
const env = {
|
|
@@ -4622,8 +4875,9 @@ async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = n
|
|
|
4622
4875
|
"--dir",
|
|
4623
4876
|
ctx.workingDirectory
|
|
4624
4877
|
];
|
|
4625
|
-
|
|
4626
|
-
|
|
4878
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4879
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4880
|
+
args.push("--model", launchRuntimeFields.model);
|
|
4627
4881
|
}
|
|
4628
4882
|
if (requiresAgentCliFlag(version)) {
|
|
4629
4883
|
args.push("--agent", SLOCK_AGENT_NAME);
|
|
@@ -4704,7 +4958,7 @@ function formatOpenCodeLabelToken(token) {
|
|
|
4704
4958
|
if (/^\d/.test(token)) return token;
|
|
4705
4959
|
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
4706
4960
|
}
|
|
4707
|
-
function detectOpenCodeModels(home =
|
|
4961
|
+
function detectOpenCodeModels(home = os4.homedir(), runCommand = runOpenCodeModelsCommand) {
|
|
4708
4962
|
const commandResult = runCommand(home);
|
|
4709
4963
|
if (commandResult.error || commandResult.status !== 0) return null;
|
|
4710
4964
|
return parseOpenCodeModelsOutput(commandResult.stdout);
|
|
@@ -4744,7 +4998,7 @@ function openCodeSpecForEntry(entry, commandArgs) {
|
|
|
4744
4998
|
return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
|
|
4745
4999
|
}
|
|
4746
5000
|
function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
4747
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
5001
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync7;
|
|
4748
5002
|
const execFileSyncFn = deps.execFileSyncFn;
|
|
4749
5003
|
const env = deps.env ?? process.env;
|
|
4750
5004
|
const winPath = path10.win32;
|
|
@@ -4774,7 +5028,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
|
4774
5028
|
}
|
|
4775
5029
|
function extractWindowsShimTargets(commandPath, deps = {}) {
|
|
4776
5030
|
if (!isWindowsCommandShim(commandPath)) return [];
|
|
4777
|
-
const readFileSyncFn = deps.readFileSyncFn ??
|
|
5031
|
+
const readFileSyncFn = deps.readFileSyncFn ?? readFileSync4;
|
|
4778
5032
|
const commandDir = path10.win32.dirname(commandPath);
|
|
4779
5033
|
let raw;
|
|
4780
5034
|
try {
|
|
@@ -4908,7 +5162,7 @@ var OpenCodeDriver = class {
|
|
|
4908
5162
|
if (unsupportedMessage) {
|
|
4909
5163
|
throw new Error(unsupportedMessage);
|
|
4910
5164
|
}
|
|
4911
|
-
const launch = await buildOpenCodeLaunchOptions(ctx,
|
|
5165
|
+
const launch = await buildOpenCodeLaunchOptions(ctx, os4.homedir(), version);
|
|
4912
5166
|
const spawnSpec = resolveOpenCodeSpawn(launch.args);
|
|
4913
5167
|
const proc = spawn8(spawnSpec.command, spawnSpec.args, {
|
|
4914
5168
|
cwd: ctx.workingDirectory,
|
|
@@ -5500,7 +5754,7 @@ function findSessionJsonl(root, predicate) {
|
|
|
5500
5754
|
if (depth < 0 || visited >= maxEntries) return null;
|
|
5501
5755
|
let entries;
|
|
5502
5756
|
try {
|
|
5503
|
-
entries =
|
|
5757
|
+
entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
|
|
5504
5758
|
} catch {
|
|
5505
5759
|
return null;
|
|
5506
5760
|
}
|
|
@@ -5546,11 +5800,11 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
5546
5800
|
return null;
|
|
5547
5801
|
}
|
|
5548
5802
|
}
|
|
5549
|
-
function resolveRuntimeSessionRef(runtime, sessionId, homeDir =
|
|
5803
|
+
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
|
|
5550
5804
|
const directPath = path12.isAbsolute(sessionId) ? sessionId : null;
|
|
5551
5805
|
if (directPath) {
|
|
5552
5806
|
try {
|
|
5553
|
-
if (
|
|
5807
|
+
if (statSync(directPath).isFile()) {
|
|
5554
5808
|
return { label: sessionId, path: directPath, runtime, reachable: true };
|
|
5555
5809
|
}
|
|
5556
5810
|
} catch {
|
|
@@ -6494,6 +6748,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6494
6748
|
driverResolver;
|
|
6495
6749
|
defaultAgentEnvVarsProvider;
|
|
6496
6750
|
tracer;
|
|
6751
|
+
cliTransportTraceDir = null;
|
|
6497
6752
|
deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
|
|
6498
6753
|
processExitTraceAttrs = /* @__PURE__ */ new WeakMap();
|
|
6499
6754
|
agentVisibleBoundaries = /* @__PURE__ */ new Map();
|
|
@@ -6504,7 +6759,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6504
6759
|
this.daemonApiKey = daemonApiKey;
|
|
6505
6760
|
this.serverUrl = opts.serverUrl;
|
|
6506
6761
|
this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
|
|
6507
|
-
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir ||
|
|
6762
|
+
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os5.homedir();
|
|
6508
6763
|
this.driverResolver = opts.driverResolver || getDriver;
|
|
6509
6764
|
this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
|
|
6510
6765
|
this.tracer = opts.tracer ?? noopTracer;
|
|
@@ -6524,6 +6779,9 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6524
6779
|
setTracer(tracer) {
|
|
6525
6780
|
this.tracer = tracer;
|
|
6526
6781
|
}
|
|
6782
|
+
setCliTransportTraceDir(traceDir) {
|
|
6783
|
+
this.cliTransportTraceDir = traceDir;
|
|
6784
|
+
}
|
|
6527
6785
|
visibleBoundaryMap(agentId) {
|
|
6528
6786
|
let map = this.agentVisibleBoundaries.get(agentId);
|
|
6529
6787
|
if (!map) {
|
|
@@ -6628,6 +6886,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6628
6886
|
});
|
|
6629
6887
|
},
|
|
6630
6888
|
recordProxyFailure: (input) => this.recordAgentProxyFailure(agentId, input),
|
|
6889
|
+
recordTransportNormalizedError: (input) => this.recordAgentProxyTransportNormalizedError(agentId, input),
|
|
6631
6890
|
recordFreshnessDecision: (input) => {
|
|
6632
6891
|
this.recordDaemonTrace("daemon.agent.inbox.freshness_decision", {
|
|
6633
6892
|
agentId,
|
|
@@ -6656,6 +6915,22 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6656
6915
|
error_message: input.errorMessage
|
|
6657
6916
|
}, "error");
|
|
6658
6917
|
}
|
|
6918
|
+
recordAgentProxyTransportNormalizedError(agentId, input) {
|
|
6919
|
+
this.recordDaemonTrace("daemon.transport.normalized_error", {
|
|
6920
|
+
producer: "daemon",
|
|
6921
|
+
agentId,
|
|
6922
|
+
normalized_code: input.normalizedCode,
|
|
6923
|
+
route_family: input.routeFamily,
|
|
6924
|
+
response_started: input.responseStarted,
|
|
6925
|
+
upstream_layer: input.upstreamLayer,
|
|
6926
|
+
...typeof input.upstreamStatus === "number" ? { upstream_status: input.upstreamStatus } : {},
|
|
6927
|
+
...input.originalMessage ? { original_message: input.originalMessage } : {},
|
|
6928
|
+
launchId: input.launchId,
|
|
6929
|
+
target_host_class: input.targetHostClass,
|
|
6930
|
+
downstream_caller: input.downstreamCaller,
|
|
6931
|
+
upstream: input.upstream
|
|
6932
|
+
}, "error");
|
|
6933
|
+
}
|
|
6659
6934
|
recordFreshnessDecisionActivity(agentId, input) {
|
|
6660
6935
|
if (input.decision !== "local_hold" && input.decision !== "syncing_hold") return;
|
|
6661
6936
|
const ap = this.agents.get(agentId);
|
|
@@ -7055,7 +7330,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7055
7330
|
slockCliPath: this.slockCliPath,
|
|
7056
7331
|
daemonApiKey: this.daemonApiKey,
|
|
7057
7332
|
launchId: launchId || null,
|
|
7058
|
-
agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId)
|
|
7333
|
+
agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId),
|
|
7334
|
+
cliTransportTraceDir: this.cliTransportTraceDir
|
|
7059
7335
|
});
|
|
7060
7336
|
this.recordDaemonTrace("daemon.agent.spawn.created", {
|
|
7061
7337
|
...this.startQueueTraceAttrs(agentId, effectiveConfig, wakeMessage, unreadSummary, resumePrompt, launchId),
|
|
@@ -7388,7 +7664,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7388
7664
|
"X-Slock-Client": "daemon-server-session-worker"
|
|
7389
7665
|
},
|
|
7390
7666
|
body: JSON.stringify({
|
|
7391
|
-
scopes: ["send", "read", "mentions", "tasks", "reactions", "server", "channels"],
|
|
7667
|
+
scopes: ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"],
|
|
7392
7668
|
name: `runner:${config.runtime}:${agentId.slice(0, 8)}`
|
|
7393
7669
|
})
|
|
7394
7670
|
});
|
|
@@ -8199,7 +8475,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8199
8475
|
async listSkills(agentId, runtimeHint) {
|
|
8200
8476
|
const agent = this.agents.get(agentId);
|
|
8201
8477
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
8202
|
-
const home =
|
|
8478
|
+
const home = os5.homedir();
|
|
8203
8479
|
const workspaceDir = path12.join(this.dataDir, agentId);
|
|
8204
8480
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
8205
8481
|
const globalResults = await Promise.all(
|
|
@@ -9626,8 +9902,8 @@ var ReminderCache = class {
|
|
|
9626
9902
|
|
|
9627
9903
|
// src/machineLock.ts
|
|
9628
9904
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
9629
|
-
import { mkdirSync as mkdirSync5, readFileSync as
|
|
9630
|
-
import
|
|
9905
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
9906
|
+
import os6 from "os";
|
|
9631
9907
|
import path13 from "path";
|
|
9632
9908
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
9633
9909
|
var DaemonMachineLockConflictError = class extends Error {
|
|
@@ -9654,14 +9930,14 @@ function ownerPath(lockDir) {
|
|
|
9654
9930
|
}
|
|
9655
9931
|
function readOwner(lockDir) {
|
|
9656
9932
|
try {
|
|
9657
|
-
return JSON.parse(
|
|
9933
|
+
return JSON.parse(readFileSync5(ownerPath(lockDir), "utf8"));
|
|
9658
9934
|
} catch {
|
|
9659
9935
|
return null;
|
|
9660
9936
|
}
|
|
9661
9937
|
}
|
|
9662
9938
|
function lockAgeMs(lockDir) {
|
|
9663
9939
|
try {
|
|
9664
|
-
return Date.now() -
|
|
9940
|
+
return Date.now() - statSync2(lockDir).mtimeMs;
|
|
9665
9941
|
} catch {
|
|
9666
9942
|
return null;
|
|
9667
9943
|
}
|
|
@@ -9690,7 +9966,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
9690
9966
|
const owner = {
|
|
9691
9967
|
pid: process.pid,
|
|
9692
9968
|
token,
|
|
9693
|
-
hostname:
|
|
9969
|
+
hostname: os6.hostname(),
|
|
9694
9970
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9695
9971
|
serverUrl: options.serverUrl,
|
|
9696
9972
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
@@ -9733,7 +10009,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
9733
10009
|
}
|
|
9734
10010
|
|
|
9735
10011
|
// src/localTraceSink.ts
|
|
9736
|
-
import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as
|
|
10012
|
+
import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
9737
10013
|
import path14 from "path";
|
|
9738
10014
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
9739
10015
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
@@ -9757,7 +10033,8 @@ var DIAGNOSTIC_ERROR_ATTRS = /* @__PURE__ */ new Set([
|
|
|
9757
10033
|
"runtime_error_message_present",
|
|
9758
10034
|
"runtime_error_message_length_bucket",
|
|
9759
10035
|
"runtime_error_message_truncated",
|
|
9760
|
-
"runtime_error_message_excerpt"
|
|
10036
|
+
"runtime_error_message_excerpt",
|
|
10037
|
+
"original_message"
|
|
9761
10038
|
]);
|
|
9762
10039
|
var LocalRotatingTraceSink = class {
|
|
9763
10040
|
traceDir;
|
|
@@ -9805,13 +10082,13 @@ var LocalRotatingTraceSink = class {
|
|
|
9805
10082
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
9806
10083
|
);
|
|
9807
10084
|
writeFileSync9(this.currentFile, "", { flag: "a", mode: 384 });
|
|
9808
|
-
this.currentSize =
|
|
10085
|
+
this.currentSize = statSync3(this.currentFile).size;
|
|
9809
10086
|
this.currentFileOpenedAtMs = nowMs;
|
|
9810
10087
|
this.pruneOldFiles();
|
|
9811
10088
|
}
|
|
9812
10089
|
}
|
|
9813
10090
|
pruneOldFiles() {
|
|
9814
|
-
const files =
|
|
10091
|
+
const files = readdirSync2(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
|
|
9815
10092
|
const excess = files.length - this.maxFiles;
|
|
9816
10093
|
if (excess <= 0) return;
|
|
9817
10094
|
for (const file of files.slice(0, excess)) {
|
|
@@ -9851,14 +10128,13 @@ function sanitizeAttrs(attrs) {
|
|
|
9851
10128
|
if (!attrs) return void 0;
|
|
9852
10129
|
const sanitized = {};
|
|
9853
10130
|
for (const [key, value] of Object.entries(attrs)) {
|
|
10131
|
+
if (value === null || value === void 0 || value === "") continue;
|
|
9854
10132
|
if (isDiagnosticIdAttr(key)) {
|
|
9855
|
-
if (value === null || value === void 0 || value === "") continue;
|
|
9856
10133
|
sanitized[key] = sanitizeValue(value);
|
|
9857
10134
|
continue;
|
|
9858
10135
|
}
|
|
9859
10136
|
if (isDiagnosticErrorAttr(key)) {
|
|
9860
|
-
|
|
9861
|
-
sanitized[key] = sanitizeValue(value);
|
|
10137
|
+
sanitized[key] = sanitizeDiagnosticErrorValue(key, value);
|
|
9862
10138
|
continue;
|
|
9863
10139
|
}
|
|
9864
10140
|
if (shouldDropAttr(key)) continue;
|
|
@@ -9878,6 +10154,11 @@ function sanitizeValue(value) {
|
|
|
9878
10154
|
}
|
|
9879
10155
|
return String(value);
|
|
9880
10156
|
}
|
|
10157
|
+
function sanitizeDiagnosticErrorValue(key, value) {
|
|
10158
|
+
if (key !== "original_message" || typeof value !== "string") return sanitizeValue(value);
|
|
10159
|
+
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();
|
|
10160
|
+
return normalized.length > 240 ? `${normalized.slice(0, 237)}...` : normalized;
|
|
10161
|
+
}
|
|
9881
10162
|
function shouldDropAttr(key) {
|
|
9882
10163
|
const normalized = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
9883
10164
|
if (/(^|_)(api_key|auth_token|token|secret|password|cookie|credential)(_|$)/i.test(normalized)) {
|
|
@@ -10246,7 +10527,7 @@ function readPositiveIntegerEnv2(name, fallback) {
|
|
|
10246
10527
|
|
|
10247
10528
|
// src/core.ts
|
|
10248
10529
|
var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
10249
|
-
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels"];
|
|
10530
|
+
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
|
|
10250
10531
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
10251
10532
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
10252
10533
|
var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
|
|
@@ -10525,6 +10806,7 @@ var DaemonCore = class {
|
|
|
10525
10806
|
sink: this.localTraceSink
|
|
10526
10807
|
});
|
|
10527
10808
|
this.agentManager.setTracer(this.tracer);
|
|
10809
|
+
this.agentManager.setCliTransportTraceDir(path16.join(machineDir, "traces"));
|
|
10528
10810
|
}
|
|
10529
10811
|
installTraceBundleUploader(machineDir) {
|
|
10530
10812
|
if (!this.shouldEnableLocalTrace()) return;
|
|
@@ -10947,8 +11229,8 @@ var DaemonCore = class {
|
|
|
10947
11229
|
capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
|
|
10948
11230
|
runtimes,
|
|
10949
11231
|
runningAgents: runningAgentIds,
|
|
10950
|
-
hostname: this.options.hostname ??
|
|
10951
|
-
os: this.options.osDescription ?? `${
|
|
11232
|
+
hostname: this.options.hostname ?? os7.hostname(),
|
|
11233
|
+
os: this.options.osDescription ?? `${os7.platform()} ${os7.arch()}`,
|
|
10952
11234
|
daemonVersion: this.daemonVersion
|
|
10953
11235
|
});
|
|
10954
11236
|
this.recordDaemonTrace("daemon.ready.sent", {
|