@slock-ai/daemon 0.54.1 → 0.54.2-play.20260528162546
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-X366KJGT.js → chunk-MWGRDZGK.js} +1563 -747
- package/dist/cli/index.js +69 -0
- package/dist/core.js +6 -2
- package/dist/drivers/piSdkRunner.js +96 -0
- package/dist/index.js +5 -3
- package/package.json +2 -1
|
@@ -8,11 +8,35 @@ import {
|
|
|
8
8
|
} from "./chunk-VOZJ2ELH.js";
|
|
9
9
|
|
|
10
10
|
// src/core.ts
|
|
11
|
-
import
|
|
12
|
-
import
|
|
11
|
+
import path17 from "path";
|
|
12
|
+
import os7 from "os";
|
|
13
13
|
import { createRequire as createRequire2 } from "module";
|
|
14
14
|
import { accessSync } from "fs";
|
|
15
|
-
import { fileURLToPath } from "url";
|
|
15
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16
|
+
|
|
17
|
+
// ../shared/src/slockRefs.ts
|
|
18
|
+
var SLOCK_REF_CHANNEL_NAME_PATTERN = String.raw`[\p{L}\p{N}_-]+`;
|
|
19
|
+
var SLOCK_REF_USER_NAME_PATTERN = SLOCK_REF_CHANNEL_NAME_PATTERN;
|
|
20
|
+
var SLOCK_REF_DM_PEER_PATTERN = String.raw`[\w-]+`;
|
|
21
|
+
var SLOCK_REF_THREAD_SHORT_ID_PATTERN = String.raw`[\da-f]{6,8}`;
|
|
22
|
+
var SLOCK_REF_MESSAGE_ID_PATTERN = String.raw`[A-Za-z0-9][A-Za-z0-9-]{1,63}`;
|
|
23
|
+
var SLOCK_REF_TASK_NUMBER_PATTERN = String.raw`[1-9]\d*`;
|
|
24
|
+
var USER_RE = new RegExp(String.raw`^@(${SLOCK_REF_USER_NAME_PATTERN})$`, "u");
|
|
25
|
+
var CHANNEL_RE = new RegExp(String.raw`^#(${SLOCK_REF_CHANNEL_NAME_PATTERN})$`, "u");
|
|
26
|
+
var CHANNEL_THREAD_RE = new RegExp(
|
|
27
|
+
String.raw`^#(${SLOCK_REF_CHANNEL_NAME_PATTERN}):(${SLOCK_REF_THREAD_SHORT_ID_PATTERN})$`,
|
|
28
|
+
"iu"
|
|
29
|
+
);
|
|
30
|
+
var DM_RE = new RegExp(String.raw`^dm:@(${SLOCK_REF_DM_PEER_PATTERN})$`, "iu");
|
|
31
|
+
var DM_THREAD_RE = new RegExp(
|
|
32
|
+
String.raw`^dm:@(${SLOCK_REF_DM_PEER_PATTERN}):(${SLOCK_REF_THREAD_SHORT_ID_PATTERN})$`,
|
|
33
|
+
"iu"
|
|
34
|
+
);
|
|
35
|
+
var TASK_RE = new RegExp(String.raw`^task\s+#(${SLOCK_REF_TASK_NUMBER_PATTERN})$`, "iu");
|
|
36
|
+
var CHANNEL_MESSAGE_RE = new RegExp(
|
|
37
|
+
String.raw`^#(${SLOCK_REF_CHANNEL_NAME_PATTERN})(?::(${SLOCK_REF_THREAD_SHORT_ID_PATTERN}))?\s+msg=(${SLOCK_REF_MESSAGE_ID_PATTERN})$`,
|
|
38
|
+
"iu"
|
|
39
|
+
);
|
|
16
40
|
|
|
17
41
|
// ../shared/src/tracing/index.ts
|
|
18
42
|
var DEFAULT_TRACE_FLAGS = "00";
|
|
@@ -724,9 +748,11 @@ var SERVER_CAPABILITY_MATRIX = {
|
|
|
724
748
|
};
|
|
725
749
|
|
|
726
750
|
// ../shared/src/index.ts
|
|
751
|
+
var RUNTIME_CONFIG_VERSION = 1;
|
|
727
752
|
var RUNTIMES = [
|
|
728
753
|
{ id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
|
|
729
754
|
{ id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
|
|
755
|
+
{ id: "pi", displayName: "Pi", binary: "pi", supported: true },
|
|
730
756
|
{ id: "antigravity", displayName: "Antigravity CLI", binary: "agy", supported: true },
|
|
731
757
|
{ id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
|
|
732
758
|
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
@@ -734,6 +760,171 @@ var RUNTIMES = [
|
|
|
734
760
|
{ id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true },
|
|
735
761
|
{ id: "opencode", displayName: "OpenCode", binary: "opencode", supported: true }
|
|
736
762
|
];
|
|
763
|
+
var RUNTIME_MODELS = {
|
|
764
|
+
claude: [
|
|
765
|
+
{ id: "opus", label: "Opus" },
|
|
766
|
+
{ id: "sonnet", label: "Sonnet" },
|
|
767
|
+
{ id: "haiku", label: "Haiku" }
|
|
768
|
+
],
|
|
769
|
+
codex: [
|
|
770
|
+
{ id: "gpt-5.5", label: "GPT-5.5" },
|
|
771
|
+
{ id: "gpt-5.4", label: "GPT-5.4" },
|
|
772
|
+
{ id: "gpt-5.3-codex", label: "GPT-5.3 Codex" },
|
|
773
|
+
{ id: "gpt-5.3-codex-spark", label: "GPT-5.3 Codex Spark" },
|
|
774
|
+
{ id: "gpt-5.2-codex", label: "GPT-5.2 Codex" },
|
|
775
|
+
{ id: "gpt-5.2", label: "GPT-5.2" },
|
|
776
|
+
{ id: "gpt-5.1-codex-max", label: "GPT-5.1 Codex Max" },
|
|
777
|
+
{ id: "gpt-5.1-codex", label: "GPT-5.1 Codex" },
|
|
778
|
+
{ id: "gpt-5-codex", label: "GPT-5 Codex" },
|
|
779
|
+
{ id: "gpt-5", label: "GPT-5" }
|
|
780
|
+
],
|
|
781
|
+
antigravity: [
|
|
782
|
+
{ id: "default", label: "AGY configured default", verified: "suggestion_only" }
|
|
783
|
+
],
|
|
784
|
+
copilot: [
|
|
785
|
+
{ id: "gpt-5.4", label: "GPT-5.4" },
|
|
786
|
+
{ id: "gpt-5.2", label: "GPT-5.2" },
|
|
787
|
+
{ id: "claude-4-sonnet", label: "Claude 4 Sonnet" },
|
|
788
|
+
{ id: "claude-4.5-sonnet", label: "Claude 4.5 Sonnet" }
|
|
789
|
+
],
|
|
790
|
+
cursor: [
|
|
791
|
+
{ id: "composer-2-fast", label: "Composer 2 Fast" },
|
|
792
|
+
{ id: "composer-2", label: "Composer 2" },
|
|
793
|
+
{ id: "auto", label: "Auto" }
|
|
794
|
+
],
|
|
795
|
+
gemini: [
|
|
796
|
+
{ id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
|
|
797
|
+
{ id: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro (Preview)" },
|
|
798
|
+
{ id: "gemini-3-flash-preview", label: "Gemini 3 Flash (Preview)" },
|
|
799
|
+
{ id: "gemini-2.5-pro", label: "Gemini 2.5 Pro" },
|
|
800
|
+
{ id: "gemini-2.5-flash", label: "Gemini 2.5 Flash" }
|
|
801
|
+
],
|
|
802
|
+
opencode: [
|
|
803
|
+
{ id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
|
|
804
|
+
{ id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro (OpenCode)", verified: "suggestion_only" },
|
|
805
|
+
{ id: "openrouter/anthropic/claude-opus-4.5", label: "Claude Opus 4.5 via OpenRouter", verified: "suggestion_only" },
|
|
806
|
+
{ id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
|
|
807
|
+
],
|
|
808
|
+
pi: [
|
|
809
|
+
{ id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro" },
|
|
810
|
+
{ id: "deepseek/deepseek-v4-flash", label: "DeepSeek V4 Flash" },
|
|
811
|
+
{ id: "kimi-coding/kimi-for-coding", label: "Kimi for Coding" },
|
|
812
|
+
{ id: "zai/glm-5.1", label: "GLM-5.1" },
|
|
813
|
+
{ id: "zai/glm-4.7", label: "GLM-4.7" }
|
|
814
|
+
],
|
|
815
|
+
// Kimi CLI resolves model keys from each user's local config, so the safest
|
|
816
|
+
// built-in option is to defer to whatever default model the CLI already uses.
|
|
817
|
+
kimi: [
|
|
818
|
+
{ id: "default", label: "Configured Default" }
|
|
819
|
+
]
|
|
820
|
+
};
|
|
821
|
+
function getDefaultModel(runtimeId) {
|
|
822
|
+
const models = RUNTIME_MODELS[runtimeId];
|
|
823
|
+
return models?.[0]?.id ?? "sonnet";
|
|
824
|
+
}
|
|
825
|
+
var CONTROLLED_RUNTIME_ENV_KEYS = {
|
|
826
|
+
claude: ["ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY", "ANTHROPIC_CUSTOM_MODEL_OPTION"]
|
|
827
|
+
};
|
|
828
|
+
function isPlainRecord(value) {
|
|
829
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
830
|
+
}
|
|
831
|
+
function normalizeEnvVars(envVars) {
|
|
832
|
+
if (!isPlainRecord(envVars)) return null;
|
|
833
|
+
const normalized = {};
|
|
834
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
835
|
+
if (typeof key === "string" && typeof value === "string") {
|
|
836
|
+
normalized[key] = value;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
840
|
+
}
|
|
841
|
+
function getControlledRuntimeEnvKeys(runtime) {
|
|
842
|
+
return CONTROLLED_RUNTIME_ENV_KEYS[runtime] ?? [];
|
|
843
|
+
}
|
|
844
|
+
function stripControlledRuntimeEnvVars(runtime, envVars) {
|
|
845
|
+
const normalized = normalizeEnvVars(envVars);
|
|
846
|
+
if (!normalized) return null;
|
|
847
|
+
const controlled = new Set(getControlledRuntimeEnvKeys(runtime));
|
|
848
|
+
for (const key of controlled) {
|
|
849
|
+
delete normalized[key];
|
|
850
|
+
}
|
|
851
|
+
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
852
|
+
}
|
|
853
|
+
function isPresetRuntimeModel(runtime, model) {
|
|
854
|
+
return (RUNTIME_MODELS[runtime] ?? []).some((candidate) => candidate.id === model);
|
|
855
|
+
}
|
|
856
|
+
function modelConfigFromLegacy(runtime, model) {
|
|
857
|
+
return isPresetRuntimeModel(runtime, model) ? { kind: "preset", id: model } : { kind: "custom", name: model };
|
|
858
|
+
}
|
|
859
|
+
function parseProviderConfig(runtime, value) {
|
|
860
|
+
if (runtime !== "claude") return void 0;
|
|
861
|
+
if (!isPlainRecord(value)) return { kind: "default" };
|
|
862
|
+
if (value.kind === "custom" && typeof value.apiUrl === "string" && value.apiUrl.trim() && typeof value.apiKey === "string" && value.apiKey.trim()) {
|
|
863
|
+
return { kind: "custom", apiUrl: value.apiUrl.trim(), apiKey: value.apiKey.trim() };
|
|
864
|
+
}
|
|
865
|
+
return { kind: "default" };
|
|
866
|
+
}
|
|
867
|
+
function parseModelConfig(runtime, value, fallback) {
|
|
868
|
+
if (!isPlainRecord(value)) return modelConfigFromLegacy(runtime, fallback);
|
|
869
|
+
if (value.kind === "custom" && typeof value.name === "string" && value.name.trim()) {
|
|
870
|
+
return { kind: "custom", name: value.name.trim() };
|
|
871
|
+
}
|
|
872
|
+
if (value.kind === "preset" && typeof value.id === "string" && value.id.trim()) {
|
|
873
|
+
return { kind: "preset", id: value.id.trim() };
|
|
874
|
+
}
|
|
875
|
+
return modelConfigFromLegacy(runtime, fallback);
|
|
876
|
+
}
|
|
877
|
+
function parseModeConfig(value) {
|
|
878
|
+
if (!isPlainRecord(value)) return { kind: "default" };
|
|
879
|
+
if (value.kind === "fast") return { kind: "fast" };
|
|
880
|
+
return { kind: "default" };
|
|
881
|
+
}
|
|
882
|
+
function hydrateRuntimeConfig(input) {
|
|
883
|
+
const stored = isPlainRecord(input.runtimeConfig) && input.runtimeConfig.version === RUNTIME_CONFIG_VERSION ? input.runtimeConfig : null;
|
|
884
|
+
const runtime = typeof stored?.runtime === "string" && stored.runtime.trim() || typeof input.runtime === "string" && input.runtime.trim() || "claude";
|
|
885
|
+
const fallbackModel = typeof input.model === "string" && input.model.trim() || getDefaultModel(runtime);
|
|
886
|
+
const legacyEnvVars = normalizeEnvVars(input.envVars);
|
|
887
|
+
const storedEnvVars = normalizeEnvVars(stored?.envVars);
|
|
888
|
+
const legacyClaudeApiUrl = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_BASE_URL : void 0;
|
|
889
|
+
const legacyClaudeApiKey = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_API_KEY : void 0;
|
|
890
|
+
const provider = stored ? parseProviderConfig(runtime, stored.provider) : runtime === "claude" && legacyClaudeApiUrl && legacyClaudeApiKey ? { kind: "custom", apiUrl: legacyClaudeApiUrl, apiKey: legacyClaudeApiKey } : runtime === "claude" ? { kind: "default" } : void 0;
|
|
891
|
+
return {
|
|
892
|
+
version: RUNTIME_CONFIG_VERSION,
|
|
893
|
+
runtime,
|
|
894
|
+
...provider ? { provider } : {},
|
|
895
|
+
model: stored ? parseModelConfig(runtime, stored.model, fallbackModel) : modelConfigFromLegacy(runtime, fallbackModel),
|
|
896
|
+
mode: stored ? parseModeConfig(stored.mode) : { kind: "default" },
|
|
897
|
+
reasoningEffort: stored?.reasoningEffort ?? input.reasoningEffort ?? null,
|
|
898
|
+
envVars: stripControlledRuntimeEnvVars(runtime, stored ? storedEnvVars : legacyEnvVars)
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
function runtimeConfigModelValue(config) {
|
|
902
|
+
return config.model.kind === "custom" ? config.model.name : config.model.id;
|
|
903
|
+
}
|
|
904
|
+
function runtimeConfigToLaunchFields(config) {
|
|
905
|
+
const normalized = isPlainRecord(config) && config.version === RUNTIME_CONFIG_VERSION ? hydrateRuntimeConfig({ runtimeConfig: config }) : hydrateRuntimeConfig(config);
|
|
906
|
+
const generatedEnvVars = {};
|
|
907
|
+
if (normalized.runtime === "claude") {
|
|
908
|
+
if (normalized.provider?.kind === "custom") {
|
|
909
|
+
generatedEnvVars.ANTHROPIC_BASE_URL = normalized.provider.apiUrl;
|
|
910
|
+
generatedEnvVars.ANTHROPIC_API_KEY = normalized.provider.apiKey;
|
|
911
|
+
}
|
|
912
|
+
if (normalized.model.kind === "custom") {
|
|
913
|
+
generatedEnvVars.ANTHROPIC_CUSTOM_MODEL_OPTION = normalized.model.name;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
const envVars = {
|
|
917
|
+
...normalized.envVars ?? {},
|
|
918
|
+
...generatedEnvVars
|
|
919
|
+
};
|
|
920
|
+
return {
|
|
921
|
+
runtime: normalized.runtime,
|
|
922
|
+
model: runtimeConfigModelValue(normalized),
|
|
923
|
+
mode: normalized.mode,
|
|
924
|
+
reasoningEffort: normalized.reasoningEffort ?? null,
|
|
925
|
+
envVars: Object.keys(envVars).length > 0 ? envVars : null
|
|
926
|
+
};
|
|
927
|
+
}
|
|
737
928
|
var PLAN_CONFIG = {
|
|
738
929
|
free: {
|
|
739
930
|
displayName: "Hobby",
|
|
@@ -769,17 +960,14 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
769
960
|
};
|
|
770
961
|
|
|
771
962
|
// src/agentProcessManager.ts
|
|
772
|
-
import { mkdirSync as
|
|
963
|
+
import { mkdirSync as mkdirSync5, readdirSync, statSync, writeFileSync as writeFileSync8 } from "fs";
|
|
773
964
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
774
965
|
import { createHash as createHash2 } from "crypto";
|
|
775
|
-
import
|
|
776
|
-
import
|
|
966
|
+
import path13 from "path";
|
|
967
|
+
import os5 from "os";
|
|
777
968
|
|
|
778
969
|
// src/drivers/claude.ts
|
|
779
970
|
import { spawn } from "child_process";
|
|
780
|
-
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
781
|
-
import os2 from "os";
|
|
782
|
-
import path4 from "path";
|
|
783
971
|
|
|
784
972
|
// src/drivers/cliTransport.ts
|
|
785
973
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
@@ -1359,6 +1547,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1359
1547
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1360
1548
|
}
|
|
1361
1549
|
|
|
1550
|
+
// src/authEnv.ts
|
|
1551
|
+
var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
|
|
1552
|
+
var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
|
|
1553
|
+
function scrubDaemonAuthEnv(env) {
|
|
1554
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1555
|
+
return env;
|
|
1556
|
+
}
|
|
1557
|
+
function scrubDaemonChildEnv(env) {
|
|
1558
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1559
|
+
delete env[SLOCK_AGENT_TOKEN_ENV];
|
|
1560
|
+
return env;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1362
1563
|
// src/agentCredentialProxy.ts
|
|
1363
1564
|
import { randomBytes } from "crypto";
|
|
1364
1565
|
import http from "http";
|
|
@@ -1949,10 +2150,13 @@ function unregisterAgentCredentialProxyForLaunch(input) {
|
|
|
1949
2150
|
// src/drivers/cliTransport.ts
|
|
1950
2151
|
var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1951
2152
|
var powershellSingleQuote = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
1952
|
-
var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels";
|
|
2153
|
+
var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels,knowledge";
|
|
2154
|
+
var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
|
|
1953
2155
|
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
1954
2156
|
var RAW_CREDENTIAL_ENV_DENYLIST = [
|
|
1955
|
-
"
|
|
2157
|
+
"SLOCK_AGENT_TOKEN",
|
|
2158
|
+
"SLOCK_AGENT_CREDENTIAL_KEY",
|
|
2159
|
+
"SLOCK_AGENT_CREDENTIAL_KEY_FILE"
|
|
1956
2160
|
];
|
|
1957
2161
|
var cachedOpencliBinPath;
|
|
1958
2162
|
function resolveOpencliBinPath() {
|
|
@@ -1996,6 +2200,34 @@ function windowsUtf8Env() {
|
|
|
1996
2200
|
LC_ALL: "C.UTF-8"
|
|
1997
2201
|
};
|
|
1998
2202
|
}
|
|
2203
|
+
function posixLoopbackNoProxyPrelude() {
|
|
2204
|
+
return [
|
|
2205
|
+
`SLOCK_LOOPBACK_NO_PROXY=${shellSingleQuote(LOOPBACK_NO_PROXY)}`,
|
|
2206
|
+
`SLOCK_EXISTING_NO_PROXY="\${NO_PROXY:-}"`,
|
|
2207
|
+
`if [ -n "\${no_proxy:-}" ]; then SLOCK_EXISTING_NO_PROXY="\${SLOCK_EXISTING_NO_PROXY:+$SLOCK_EXISTING_NO_PROXY,}$no_proxy"; fi`,
|
|
2208
|
+
`NO_PROXY="\${SLOCK_LOOPBACK_NO_PROXY}\${SLOCK_EXISTING_NO_PROXY:+,$SLOCK_EXISTING_NO_PROXY}"`,
|
|
2209
|
+
`no_proxy="$NO_PROXY"`,
|
|
2210
|
+
"export NO_PROXY no_proxy"
|
|
2211
|
+
].join("\n");
|
|
2212
|
+
}
|
|
2213
|
+
function cmdLoopbackNoProxyLines() {
|
|
2214
|
+
return [
|
|
2215
|
+
`set "SLOCK_LOOPBACK_NO_PROXY=${LOOPBACK_NO_PROXY}"`,
|
|
2216
|
+
`set "SLOCK_EXISTING_NO_PROXY=%NO_PROXY%"`,
|
|
2217
|
+
`if defined no_proxy (if defined SLOCK_EXISTING_NO_PROXY (set "SLOCK_EXISTING_NO_PROXY=%SLOCK_EXISTING_NO_PROXY%,%no_proxy%") else set "SLOCK_EXISTING_NO_PROXY=%no_proxy%")`,
|
|
2218
|
+
`if defined SLOCK_EXISTING_NO_PROXY (set "NO_PROXY=%SLOCK_LOOPBACK_NO_PROXY%,%SLOCK_EXISTING_NO_PROXY%") else set "NO_PROXY=%SLOCK_LOOPBACK_NO_PROXY%"`,
|
|
2219
|
+
`set "no_proxy=%NO_PROXY%"`
|
|
2220
|
+
];
|
|
2221
|
+
}
|
|
2222
|
+
function powershellLoopbackNoProxyLines() {
|
|
2223
|
+
return [
|
|
2224
|
+
`$loopbackNoProxy = ${powershellSingleQuote(LOOPBACK_NO_PROXY)}`,
|
|
2225
|
+
"$existingNoProxy = @($env:NO_PROXY, $env:no_proxy) | Where-Object { $_ }",
|
|
2226
|
+
`if ($existingNoProxy.Count -gt 0) { $mergedNoProxy = "$loopbackNoProxy,$($existingNoProxy -join ',')" } else { $mergedNoProxy = $loopbackNoProxy }`,
|
|
2227
|
+
"$env:NO_PROXY = $mergedNoProxy",
|
|
2228
|
+
"$env:no_proxy = $mergedNoProxy"
|
|
2229
|
+
];
|
|
2230
|
+
}
|
|
1999
2231
|
function runtimeContextEnv(config) {
|
|
2000
2232
|
const ctx = config.runtimeContext;
|
|
2001
2233
|
if (!ctx) return {};
|
|
@@ -2045,6 +2277,7 @@ async function prepareCliTransport(ctx, extraEnv = {}, platform = process.platfo
|
|
|
2045
2277
|
const posixWrapper = path2.join(slockDir, "slock");
|
|
2046
2278
|
const posixCredentialPrefix = agentCredentialProxy ? `SLOCK_AGENT_PROXY_URL=${shellSingleQuote(agentCredentialProxy.proxyUrl)} SLOCK_AGENT_PROXY_TOKEN_FILE=${shellSingleQuote(agentCredentialProxyTokenFile)} SLOCK_AGENT_ACTIVE_CAPABILITIES=${shellSingleQuote(DEFAULT_ACTIVE_CAPABILITIES)} ` : "";
|
|
2047
2279
|
const posixBody = `#!/usr/bin/env bash
|
|
2280
|
+
${posixLoopbackNoProxyPrelude()}
|
|
2048
2281
|
${posixCredentialPrefix}exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)} "$@"
|
|
2049
2282
|
`;
|
|
2050
2283
|
writeFileSync(posixWrapper, posixBody, { mode: 493 });
|
|
@@ -2061,6 +2294,7 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
|
|
|
2061
2294
|
"set LANG=C.UTF-8",
|
|
2062
2295
|
"set LC_ALL=C.UTF-8",
|
|
2063
2296
|
"chcp 65001 >NUL 2>NUL",
|
|
2297
|
+
...cmdLoopbackNoProxyLines(),
|
|
2064
2298
|
cmdCredentialLine.trimEnd(),
|
|
2065
2299
|
`"${process.execPath}" "${ctx.slockCliPath}" %*`,
|
|
2066
2300
|
""
|
|
@@ -2081,6 +2315,7 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
|
|
|
2081
2315
|
"$env:PYTHONUTF8 = '1'",
|
|
2082
2316
|
"$env:LANG = 'C.UTF-8'",
|
|
2083
2317
|
"$env:LC_ALL = 'C.UTF-8'",
|
|
2318
|
+
...powershellLoopbackNoProxyLines(),
|
|
2084
2319
|
...psCredentialLines,
|
|
2085
2320
|
`$node = ${powershellSingleQuote(process.execPath)}`,
|
|
2086
2321
|
`$cli = ${powershellSingleQuote(ctx.slockCliPath)}`,
|
|
@@ -2120,10 +2355,11 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2120
2355
|
}
|
|
2121
2356
|
}
|
|
2122
2357
|
const wrapperPath = platform === "win32" ? path2.join(slockDir, "slock.cmd") : posixWrapper;
|
|
2358
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
2123
2359
|
const spawnEnv = {
|
|
2124
2360
|
...process.env,
|
|
2125
2361
|
FORCE_COLOR: "0",
|
|
2126
|
-
...
|
|
2362
|
+
...launchRuntimeFields.envVars || {},
|
|
2127
2363
|
...extraEnv,
|
|
2128
2364
|
...platform === "win32" ? windowsUtf8Env() : {},
|
|
2129
2365
|
...runtimeContextEnv(ctx.config),
|
|
@@ -2134,7 +2370,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2134
2370
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
2135
2371
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
2136
2372
|
};
|
|
2137
|
-
|
|
2373
|
+
scrubDaemonChildEnv(spawnEnv);
|
|
2138
2374
|
for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
|
|
2139
2375
|
delete spawnEnv[key];
|
|
2140
2376
|
}
|
|
@@ -2154,6 +2390,129 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2154
2390
|
};
|
|
2155
2391
|
}
|
|
2156
2392
|
|
|
2393
|
+
// src/drivers/claudeEventNormalizer.ts
|
|
2394
|
+
function collectResultErrorDetail(message, fallback) {
|
|
2395
|
+
const parts = [];
|
|
2396
|
+
if (Array.isArray(message.errors)) {
|
|
2397
|
+
for (const err of message.errors) {
|
|
2398
|
+
if (typeof err === "string" && err.trim()) parts.push(err.trim());
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
if (typeof message.result === "string" && message.result.trim()) {
|
|
2402
|
+
parts.push(message.result.trim());
|
|
2403
|
+
}
|
|
2404
|
+
return parts.join(" | ") || fallback;
|
|
2405
|
+
}
|
|
2406
|
+
function isProviderApiFailureText(value, hasToolUse) {
|
|
2407
|
+
return !hasToolUse && /^\s*API Error:/i.test(value) && (/\b(?:ECONNRESET|EPIPE|ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/i.test(value) || /\bUnable to connect to API\b/i.test(value) || /\b(?:timed out|timeout)\b/i.test(value) || /\b5\d{2}\b/.test(value));
|
|
2408
|
+
}
|
|
2409
|
+
var ClaudeEventNormalizer = class {
|
|
2410
|
+
normalizeLine(line) {
|
|
2411
|
+
let event;
|
|
2412
|
+
try {
|
|
2413
|
+
event = JSON.parse(line);
|
|
2414
|
+
} catch {
|
|
2415
|
+
return [];
|
|
2416
|
+
}
|
|
2417
|
+
const events = [];
|
|
2418
|
+
const pushResultError = (message, fallback) => {
|
|
2419
|
+
events.push({ kind: "error", message: collectResultErrorDetail(message, fallback) });
|
|
2420
|
+
};
|
|
2421
|
+
switch (event.type) {
|
|
2422
|
+
case "system":
|
|
2423
|
+
if (event.subtype === "init" && event.session_id) {
|
|
2424
|
+
events.push({ kind: "session_init", sessionId: event.session_id });
|
|
2425
|
+
}
|
|
2426
|
+
if (event.subtype === "status" && event.status === "compacting") {
|
|
2427
|
+
events.push({ kind: "compaction_started" });
|
|
2428
|
+
}
|
|
2429
|
+
if (event.subtype === "status" && event.status === "requesting") {
|
|
2430
|
+
events.push({
|
|
2431
|
+
kind: "internal_progress",
|
|
2432
|
+
source: "claude_system_status",
|
|
2433
|
+
itemType: "requesting",
|
|
2434
|
+
payloadBytes: Buffer.byteLength(line, "utf8")
|
|
2435
|
+
});
|
|
2436
|
+
}
|
|
2437
|
+
if (event.subtype === "compact_boundary") {
|
|
2438
|
+
events.push({ kind: "compaction_finished" });
|
|
2439
|
+
}
|
|
2440
|
+
break;
|
|
2441
|
+
case "stream_event":
|
|
2442
|
+
events.push({
|
|
2443
|
+
kind: "internal_progress",
|
|
2444
|
+
source: "claude_stream_event",
|
|
2445
|
+
itemType: typeof event.event?.type === "string" && event.event.type.length > 0 ? event.event.type : "unknown",
|
|
2446
|
+
payloadBytes: Buffer.byteLength(line, "utf8")
|
|
2447
|
+
});
|
|
2448
|
+
break;
|
|
2449
|
+
case "assistant": {
|
|
2450
|
+
const content = event.message?.content;
|
|
2451
|
+
if (Array.isArray(content)) {
|
|
2452
|
+
const hasToolUse = content.some((block) => block?.type === "tool_use");
|
|
2453
|
+
for (const block of content) {
|
|
2454
|
+
if (block.type === "thinking" && block.thinking) {
|
|
2455
|
+
events.push({ kind: "thinking", text: block.thinking });
|
|
2456
|
+
} else if (block.type === "text" && block.text) {
|
|
2457
|
+
if (isProviderApiFailureText(block.text, hasToolUse)) {
|
|
2458
|
+
events.push({ kind: "error", message: block.text });
|
|
2459
|
+
} else {
|
|
2460
|
+
events.push({ kind: "text", text: block.text });
|
|
2461
|
+
}
|
|
2462
|
+
} else if (block.type === "tool_use") {
|
|
2463
|
+
events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
break;
|
|
2468
|
+
}
|
|
2469
|
+
case "user": {
|
|
2470
|
+
const content = event.message?.content;
|
|
2471
|
+
if (Array.isArray(content)) {
|
|
2472
|
+
for (const block of content) {
|
|
2473
|
+
if (block.type === "tool_result") {
|
|
2474
|
+
events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result" });
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
break;
|
|
2479
|
+
}
|
|
2480
|
+
case "result": {
|
|
2481
|
+
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
2482
|
+
const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
|
|
2483
|
+
switch (subtype) {
|
|
2484
|
+
case "success":
|
|
2485
|
+
if (event.is_error && stopReason !== "max_tokens") {
|
|
2486
|
+
pushResultError(event, "Execution failed");
|
|
2487
|
+
}
|
|
2488
|
+
break;
|
|
2489
|
+
case "error_during_execution":
|
|
2490
|
+
if (stopReason !== "max_tokens") {
|
|
2491
|
+
pushResultError(event, "Execution failed");
|
|
2492
|
+
}
|
|
2493
|
+
break;
|
|
2494
|
+
case "error_max_budget_usd":
|
|
2495
|
+
pushResultError(event, "Budget limit exceeded");
|
|
2496
|
+
break;
|
|
2497
|
+
case "error_max_turns":
|
|
2498
|
+
pushResultError(event, "Max turns exceeded");
|
|
2499
|
+
break;
|
|
2500
|
+
case "error_max_structured_output_retries":
|
|
2501
|
+
pushResultError(event, "Structured output retries exceeded");
|
|
2502
|
+
break;
|
|
2503
|
+
}
|
|
2504
|
+
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
2505
|
+
break;
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
return events;
|
|
2509
|
+
}
|
|
2510
|
+
};
|
|
2511
|
+
|
|
2512
|
+
// src/drivers/claudeLaunch.ts
|
|
2513
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
2514
|
+
import path4 from "path";
|
|
2515
|
+
|
|
2157
2516
|
// src/drivers/probe.ts
|
|
2158
2517
|
import { execFileSync } from "child_process";
|
|
2159
2518
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -2310,7 +2669,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
|
|
|
2310
2669
|
}
|
|
2311
2670
|
function resolveCommandOnPath(command, deps = {}) {
|
|
2312
2671
|
const platform = deps.platform ?? process.platform;
|
|
2313
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
2672
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
2314
2673
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
2315
2674
|
const existsSyncFn = deps.existsSyncFn ?? existsSync2;
|
|
2316
2675
|
if (platform === "win32") {
|
|
@@ -2336,7 +2695,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
2336
2695
|
return null;
|
|
2337
2696
|
}
|
|
2338
2697
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
2339
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
2698
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
2340
2699
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
2341
2700
|
try {
|
|
2342
2701
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -2354,12 +2713,10 @@ function resolveHomePath(relativePath, deps = {}) {
|
|
|
2354
2713
|
return path3.join(homeDir, relativePath);
|
|
2355
2714
|
}
|
|
2356
2715
|
|
|
2357
|
-
// src/drivers/
|
|
2716
|
+
// src/drivers/claudeLaunch.ts
|
|
2358
2717
|
var CLAUDE_DESKTOP_CLI_RELATIVE_PATH = path4.join("Applications", "Claude Code URL Handler.app", "Contents", "MacOS", "claude");
|
|
2359
2718
|
var CLAUDE_DESKTOP_CLI_SYSTEM_PATH = "/Applications/Claude Code URL Handler.app/Contents/MacOS/claude";
|
|
2360
2719
|
var CLAUDE_SYSTEM_PROMPT_FILE = "claude-system-prompt.md";
|
|
2361
|
-
var CLAUDE_MCP_CONFIG_FILE = "claude-mcp-config.json";
|
|
2362
|
-
var SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME = "chat";
|
|
2363
2720
|
var CLAUDE_DISALLOWED_TOOLS = [
|
|
2364
2721
|
"EnterPlanMode",
|
|
2365
2722
|
"ExitPlanMode",
|
|
@@ -2385,81 +2742,54 @@ function probeClaude(deps = {}) {
|
|
|
2385
2742
|
version: readCommandVersion(command, [], deps) ?? void 0
|
|
2386
2743
|
};
|
|
2387
2744
|
}
|
|
2388
|
-
function
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2745
|
+
function buildClaudeArgs(config, opts) {
|
|
2746
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(config);
|
|
2747
|
+
const args = [
|
|
2748
|
+
"--allow-dangerously-skip-permissions",
|
|
2749
|
+
"--dangerously-skip-permissions",
|
|
2750
|
+
"--verbose",
|
|
2751
|
+
"--permission-mode",
|
|
2752
|
+
"bypassPermissions",
|
|
2753
|
+
"--output-format",
|
|
2754
|
+
"stream-json",
|
|
2755
|
+
"--input-format",
|
|
2756
|
+
"stream-json",
|
|
2757
|
+
"--include-partial-messages",
|
|
2758
|
+
"--model",
|
|
2759
|
+
launchRuntimeFields.model || "sonnet",
|
|
2760
|
+
"--disallowed-tools",
|
|
2761
|
+
CLAUDE_DISALLOWED_TOOLS,
|
|
2762
|
+
"--append-system-prompt-file",
|
|
2763
|
+
opts.standingPromptFilePath
|
|
2764
|
+
];
|
|
2765
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
2766
|
+
args.push("--effort", launchRuntimeFields.reasoningEffort);
|
|
2396
2767
|
}
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
function readClaudeMcpServers(configPath, vars = {}) {
|
|
2400
|
-
try {
|
|
2401
|
-
const parsed = JSON.parse(
|
|
2402
|
-
expandClaudeMcpConfigVariables(readFileSync2(configPath, "utf8"), vars)
|
|
2403
|
-
);
|
|
2404
|
-
if (!isRecord(parsed) || !isRecord(parsed.mcpServers)) return null;
|
|
2405
|
-
return parsed.mcpServers;
|
|
2406
|
-
} catch (err) {
|
|
2407
|
-
logger.warn(
|
|
2408
|
-
`[Claude] failed to read MCP config ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
2409
|
-
);
|
|
2410
|
-
return null;
|
|
2768
|
+
if (launchRuntimeFields.mode.kind === "fast") {
|
|
2769
|
+
args.push("--bare");
|
|
2411
2770
|
}
|
|
2771
|
+
if (config.sessionId) {
|
|
2772
|
+
args.push("--resume", config.sessionId);
|
|
2773
|
+
}
|
|
2774
|
+
return args;
|
|
2412
2775
|
}
|
|
2413
|
-
function
|
|
2414
|
-
const
|
|
2415
|
-
|
|
2776
|
+
function writeClaudeSystemPromptFile(standingPrompt, slockDir) {
|
|
2777
|
+
const systemPromptPath = path4.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
|
|
2778
|
+
writeFileSync2(systemPromptPath, standingPrompt, { mode: 384 });
|
|
2779
|
+
return systemPromptPath;
|
|
2416
2780
|
}
|
|
2417
|
-
function
|
|
2418
|
-
const
|
|
2419
|
-
const
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
}
|
|
2781
|
+
function buildClaudeSpawnSpec(claudeCommand, platform = process.platform) {
|
|
2782
|
+
const lowerClaudeCommand = claudeCommand?.toLowerCase();
|
|
2783
|
+
const isBatchFile = Boolean(
|
|
2784
|
+
platform === "win32" && lowerClaudeCommand && (lowerClaudeCommand.endsWith(".cmd") || lowerClaudeCommand.endsWith(".bat"))
|
|
2785
|
+
);
|
|
2786
|
+
return {
|
|
2787
|
+
command: claudeCommand ?? "claude",
|
|
2788
|
+
shell: platform === "win32" && (!claudeCommand || isBatchFile)
|
|
2426
2789
|
};
|
|
2427
|
-
pushIfFile(path4.join(home, ".claude.json"));
|
|
2428
|
-
pushIfFile(path4.join(ctx.workingDirectory, ".mcp.json"));
|
|
2429
|
-
const pluginRoot = path4.join(resolveClaudeConfigDir(ctx, home), "plugins");
|
|
2430
|
-
try {
|
|
2431
|
-
for (const entry of readdirSync(pluginRoot)) {
|
|
2432
|
-
const pluginPath = path4.join(pluginRoot, entry);
|
|
2433
|
-
const configPath = path4.join(pluginPath, ".mcp.json");
|
|
2434
|
-
try {
|
|
2435
|
-
if (existsSync3(configPath) && statSync(configPath).isFile()) {
|
|
2436
|
-
files.push({
|
|
2437
|
-
path: configPath,
|
|
2438
|
-
vars: { CLAUDE_PLUGIN_ROOT: pluginPath }
|
|
2439
|
-
});
|
|
2440
|
-
}
|
|
2441
|
-
} catch {
|
|
2442
|
-
}
|
|
2443
|
-
}
|
|
2444
|
-
} catch {
|
|
2445
|
-
}
|
|
2446
|
-
return files;
|
|
2447
|
-
}
|
|
2448
|
-
function buildClaudeUserMcpServers(ctx, home) {
|
|
2449
|
-
const servers = /* @__PURE__ */ Object.create(null);
|
|
2450
|
-
for (const configFile of collectClaudeMcpConfigFiles(ctx, home)) {
|
|
2451
|
-
const mcpServers = readClaudeMcpServers(configFile.path, configFile.vars);
|
|
2452
|
-
if (!mcpServers) continue;
|
|
2453
|
-
for (const [name, server] of Object.entries(mcpServers)) {
|
|
2454
|
-
if (!isRecord(server)) {
|
|
2455
|
-
logger.warn(`[Claude] ignoring invalid MCP server "${name}" in ${configFile.path}`);
|
|
2456
|
-
continue;
|
|
2457
|
-
}
|
|
2458
|
-
servers[name] = server;
|
|
2459
|
-
}
|
|
2460
|
-
}
|
|
2461
|
-
return servers;
|
|
2462
2790
|
}
|
|
2791
|
+
|
|
2792
|
+
// src/drivers/claude.ts
|
|
2463
2793
|
var ClaudeDriver = class {
|
|
2464
2794
|
id = "claude";
|
|
2465
2795
|
lifecycle = {
|
|
@@ -2469,7 +2799,7 @@ var ClaudeDriver = class {
|
|
|
2469
2799
|
};
|
|
2470
2800
|
communication = {
|
|
2471
2801
|
chat: "slock_cli",
|
|
2472
|
-
runtimeControl: "
|
|
2802
|
+
runtimeControl: "none"
|
|
2473
2803
|
};
|
|
2474
2804
|
session = {
|
|
2475
2805
|
recovery: "resume_or_fresh"
|
|
@@ -2479,107 +2809,37 @@ var ClaudeDriver = class {
|
|
|
2479
2809
|
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
2480
2810
|
};
|
|
2481
2811
|
supportsStdinNotification = true;
|
|
2482
|
-
mcpToolPrefix = "
|
|
2812
|
+
mcpToolPrefix = "";
|
|
2483
2813
|
usesSlockCliForCommunication = true;
|
|
2484
2814
|
// Claude Code supports same-turn steering, but raw stdin injection at an
|
|
2485
2815
|
// arbitrary busy instant can collide with active signed thinking blocks. The
|
|
2486
2816
|
// daemon therefore gates busy delivery on Claude stream-json boundaries.
|
|
2487
2817
|
busyDeliveryMode = "gated";
|
|
2488
2818
|
supportsNativeStandingPrompt = true;
|
|
2819
|
+
eventNormalizer = new ClaudeEventNormalizer();
|
|
2489
2820
|
probe() {
|
|
2490
2821
|
return probeClaude();
|
|
2491
2822
|
}
|
|
2492
|
-
buildClaudeArgs(config,
|
|
2493
|
-
|
|
2494
|
-
"--allow-dangerously-skip-permissions",
|
|
2495
|
-
"--dangerously-skip-permissions",
|
|
2496
|
-
"--verbose",
|
|
2497
|
-
"--permission-mode",
|
|
2498
|
-
"bypassPermissions",
|
|
2499
|
-
"--output-format",
|
|
2500
|
-
"stream-json",
|
|
2501
|
-
"--input-format",
|
|
2502
|
-
"stream-json",
|
|
2503
|
-
"--model",
|
|
2504
|
-
config.model || "sonnet",
|
|
2505
|
-
"--disallowed-tools",
|
|
2506
|
-
CLAUDE_DISALLOWED_TOOLS
|
|
2507
|
-
];
|
|
2508
|
-
if (opts.standingPromptFilePath) {
|
|
2509
|
-
args.push("--append-system-prompt-file", opts.standingPromptFilePath);
|
|
2510
|
-
} else {
|
|
2511
|
-
args.push("--append-system-prompt", standingPrompt);
|
|
2512
|
-
}
|
|
2513
|
-
if (config.sessionId) {
|
|
2514
|
-
args.push("--resume", config.sessionId);
|
|
2515
|
-
}
|
|
2516
|
-
return args;
|
|
2517
|
-
}
|
|
2518
|
-
buildRuntimeActionsMcpServer(ctx) {
|
|
2519
|
-
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
2520
|
-
const command = isTsSource ? "npx" : "node";
|
|
2521
|
-
const bridgeArgs = isTsSource ? ["tsx", ctx.chatBridgePath] : [ctx.chatBridgePath];
|
|
2522
|
-
return {
|
|
2523
|
-
command,
|
|
2524
|
-
args: [
|
|
2525
|
-
...bridgeArgs,
|
|
2526
|
-
"--agent-id",
|
|
2527
|
-
ctx.agentId,
|
|
2528
|
-
"--server-url",
|
|
2529
|
-
ctx.config.serverUrl,
|
|
2530
|
-
"--auth-token",
|
|
2531
|
-
ctx.config.authToken || ctx.daemonApiKey,
|
|
2532
|
-
"--runtime",
|
|
2533
|
-
this.id,
|
|
2534
|
-
...ctx.launchId ? ["--launch-id", ctx.launchId] : [],
|
|
2535
|
-
"--runtime-actions-only"
|
|
2536
|
-
]
|
|
2537
|
-
};
|
|
2538
|
-
}
|
|
2539
|
-
buildRuntimeActionsMcpConfig(ctx, home = os2.homedir()) {
|
|
2540
|
-
const userMcpServers = buildClaudeUserMcpServers(ctx, home);
|
|
2541
|
-
if (Object.prototype.hasOwnProperty.call(userMcpServers, SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME)) {
|
|
2542
|
-
logger.warn(
|
|
2543
|
-
`[Agent ${ctx.agentId}] Claude user MCP server "${SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME}" is reserved by Slock runtime actions and will be ignored`
|
|
2544
|
-
);
|
|
2545
|
-
}
|
|
2546
|
-
return JSON.stringify({
|
|
2547
|
-
mcpServers: {
|
|
2548
|
-
...userMcpServers,
|
|
2549
|
-
[SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME]: this.buildRuntimeActionsMcpServer(ctx)
|
|
2550
|
-
}
|
|
2551
|
-
});
|
|
2552
|
-
}
|
|
2553
|
-
writeClaudeLaunchFiles(ctx, slockDir, home = os2.homedir()) {
|
|
2554
|
-
const systemPromptPath = path4.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
|
|
2555
|
-
const mcpConfigPath = path4.join(slockDir, CLAUDE_MCP_CONFIG_FILE);
|
|
2556
|
-
writeFileSync2(systemPromptPath, ctx.standingPrompt, { mode: 384 });
|
|
2557
|
-
writeFileSync2(mcpConfigPath, this.buildRuntimeActionsMcpConfig(ctx, home), { mode: 384 });
|
|
2558
|
-
return { systemPromptPath, mcpConfigPath };
|
|
2823
|
+
buildClaudeArgs(config, opts) {
|
|
2824
|
+
return buildClaudeArgs(config, opts);
|
|
2559
2825
|
}
|
|
2560
2826
|
async spawn(ctx) {
|
|
2561
2827
|
const { slockDir, tokenFile, spawnEnv } = await prepareCliTransport(ctx);
|
|
2562
|
-
const
|
|
2563
|
-
const args = this.buildClaudeArgs(ctx.config,
|
|
2828
|
+
const systemPromptPath = writeClaudeSystemPromptFile(ctx.standingPrompt, slockDir);
|
|
2829
|
+
const args = this.buildClaudeArgs(ctx.config, {
|
|
2564
2830
|
standingPromptFilePath: systemPromptPath
|
|
2565
2831
|
});
|
|
2566
|
-
args.push("--mcp-config", mcpConfigPath, "--strict-mcp-config");
|
|
2567
2832
|
delete spawnEnv.CLAUDECODE;
|
|
2568
2833
|
logger.info(
|
|
2569
2834
|
`[Agent ${ctx.agentId}] transport=cli cli=${ctx.slockCliPath} token_file=${tokenFile}`
|
|
2570
2835
|
);
|
|
2571
2836
|
const claudeCommand = resolveClaudeCommand();
|
|
2572
|
-
const
|
|
2573
|
-
const
|
|
2574
|
-
const isBatchFile = Boolean(
|
|
2575
|
-
isWindows && lowerClaudeCommand && (lowerClaudeCommand.endsWith(".cmd") || lowerClaudeCommand.endsWith(".bat"))
|
|
2576
|
-
);
|
|
2577
|
-
const useShell = isWindows && (!claudeCommand || isBatchFile);
|
|
2578
|
-
const proc = spawn(claudeCommand ?? "claude", args, {
|
|
2837
|
+
const spawnSpec = buildClaudeSpawnSpec(claudeCommand);
|
|
2838
|
+
const proc = spawn(spawnSpec.command, args, {
|
|
2579
2839
|
cwd: ctx.workingDirectory,
|
|
2580
2840
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2581
2841
|
env: spawnEnv,
|
|
2582
|
-
shell:
|
|
2842
|
+
shell: spawnSpec.shell
|
|
2583
2843
|
});
|
|
2584
2844
|
const stdinMsg = JSON.stringify({
|
|
2585
2845
|
type: "user",
|
|
@@ -2593,99 +2853,7 @@ var ClaudeDriver = class {
|
|
|
2593
2853
|
return { process: proc };
|
|
2594
2854
|
}
|
|
2595
2855
|
parseLine(line) {
|
|
2596
|
-
|
|
2597
|
-
try {
|
|
2598
|
-
event = JSON.parse(line);
|
|
2599
|
-
} catch {
|
|
2600
|
-
return [];
|
|
2601
|
-
}
|
|
2602
|
-
const events = [];
|
|
2603
|
-
const pushResultError = (message, fallback) => {
|
|
2604
|
-
const parts = [];
|
|
2605
|
-
if (Array.isArray(message.errors)) {
|
|
2606
|
-
for (const err of message.errors) {
|
|
2607
|
-
if (typeof err === "string" && err.trim()) parts.push(err.trim());
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
if (typeof message.result === "string" && message.result.trim()) {
|
|
2611
|
-
parts.push(message.result.trim());
|
|
2612
|
-
}
|
|
2613
|
-
const detail = parts.join(" | ") || fallback;
|
|
2614
|
-
events.push({ kind: "error", message: detail });
|
|
2615
|
-
};
|
|
2616
|
-
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));
|
|
2617
|
-
switch (event.type) {
|
|
2618
|
-
case "system":
|
|
2619
|
-
if (event.subtype === "init" && event.session_id) {
|
|
2620
|
-
events.push({ kind: "session_init", sessionId: event.session_id });
|
|
2621
|
-
}
|
|
2622
|
-
if (event.subtype === "status" && event.status === "compacting") {
|
|
2623
|
-
events.push({ kind: "compaction_started" });
|
|
2624
|
-
}
|
|
2625
|
-
if (event.subtype === "compact_boundary") {
|
|
2626
|
-
events.push({ kind: "compaction_finished" });
|
|
2627
|
-
}
|
|
2628
|
-
break;
|
|
2629
|
-
case "assistant": {
|
|
2630
|
-
const content = event.message?.content;
|
|
2631
|
-
if (Array.isArray(content)) {
|
|
2632
|
-
const hasToolUse = content.some((block) => block?.type === "tool_use");
|
|
2633
|
-
for (const block of content) {
|
|
2634
|
-
if (block.type === "thinking" && block.thinking) {
|
|
2635
|
-
events.push({ kind: "thinking", text: block.thinking });
|
|
2636
|
-
} else if (block.type === "text" && block.text) {
|
|
2637
|
-
if (isProviderApiFailureText(block.text, hasToolUse)) {
|
|
2638
|
-
events.push({ kind: "error", message: block.text });
|
|
2639
|
-
} else {
|
|
2640
|
-
events.push({ kind: "text", text: block.text });
|
|
2641
|
-
}
|
|
2642
|
-
} else if (block.type === "tool_use") {
|
|
2643
|
-
events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
|
|
2644
|
-
}
|
|
2645
|
-
}
|
|
2646
|
-
}
|
|
2647
|
-
break;
|
|
2648
|
-
}
|
|
2649
|
-
case "user": {
|
|
2650
|
-
const content = event.message?.content;
|
|
2651
|
-
if (Array.isArray(content)) {
|
|
2652
|
-
for (const block of content) {
|
|
2653
|
-
if (block.type === "tool_result") {
|
|
2654
|
-
events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result" });
|
|
2655
|
-
}
|
|
2656
|
-
}
|
|
2657
|
-
}
|
|
2658
|
-
break;
|
|
2659
|
-
}
|
|
2660
|
-
case "result": {
|
|
2661
|
-
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
2662
|
-
const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
|
|
2663
|
-
switch (subtype) {
|
|
2664
|
-
case "success":
|
|
2665
|
-
if (event.is_error && stopReason !== "max_tokens") {
|
|
2666
|
-
pushResultError(event, "Execution failed");
|
|
2667
|
-
}
|
|
2668
|
-
break;
|
|
2669
|
-
case "error_during_execution":
|
|
2670
|
-
if (stopReason !== "max_tokens") {
|
|
2671
|
-
pushResultError(event, "Execution failed");
|
|
2672
|
-
}
|
|
2673
|
-
break;
|
|
2674
|
-
case "error_max_budget_usd":
|
|
2675
|
-
pushResultError(event, "Budget limit exceeded");
|
|
2676
|
-
break;
|
|
2677
|
-
case "error_max_turns":
|
|
2678
|
-
pushResultError(event, "Max turns exceeded");
|
|
2679
|
-
break;
|
|
2680
|
-
case "error_max_structured_output_retries":
|
|
2681
|
-
pushResultError(event, "Structured output retries exceeded");
|
|
2682
|
-
break;
|
|
2683
|
-
}
|
|
2684
|
-
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
2685
|
-
break;
|
|
2686
|
-
}
|
|
2687
|
-
}
|
|
2688
|
-
return events;
|
|
2856
|
+
return this.eventNormalizer.normalizeLine(line);
|
|
2689
2857
|
}
|
|
2690
2858
|
encodeStdinMessage(text, sessionId, _opts) {
|
|
2691
2859
|
return JSON.stringify({
|
|
@@ -2699,7 +2867,7 @@ var ClaudeDriver = class {
|
|
|
2699
2867
|
}
|
|
2700
2868
|
buildSystemPrompt(config, _agentId) {
|
|
2701
2869
|
return buildCliTransportSystemPrompt(config, {
|
|
2702
|
-
toolPrefix: "
|
|
2870
|
+
toolPrefix: "",
|
|
2703
2871
|
extraCriticalRules: [],
|
|
2704
2872
|
postStartupNotes: [
|
|
2705
2873
|
"**Claude runtime note:** While you are busy, Slock batches inbox-count notifications instead of injecting message content. Use `slock message check` at natural breakpoints to pull the pending messages before side-effect actions that depend on current context."
|
|
@@ -2712,9 +2880,117 @@ var ClaudeDriver = class {
|
|
|
2712
2880
|
|
|
2713
2881
|
// src/drivers/codex.ts
|
|
2714
2882
|
import { spawn as spawn2, execFileSync as execFileSync2, execSync } from "child_process";
|
|
2715
|
-
import { existsSync as
|
|
2716
|
-
import
|
|
2883
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
2884
|
+
import os2 from "os";
|
|
2717
2885
|
import path5 from "path";
|
|
2886
|
+
|
|
2887
|
+
// src/runtimeTurnState.ts
|
|
2888
|
+
var RuntimeTurnState = class {
|
|
2889
|
+
currentTurnId = null;
|
|
2890
|
+
/**
|
|
2891
|
+
* Post-tool window where the app-server may not yet accept stdin steering.
|
|
2892
|
+
* Gate busy-mode delivery until turn/completed or next progress.
|
|
2893
|
+
*/
|
|
2894
|
+
steeringGateActive = false;
|
|
2895
|
+
reset() {
|
|
2896
|
+
this.currentTurnId = null;
|
|
2897
|
+
this.steeringGateActive = false;
|
|
2898
|
+
}
|
|
2899
|
+
get activeTurnId() {
|
|
2900
|
+
return this.currentTurnId;
|
|
2901
|
+
}
|
|
2902
|
+
get canSteerBusy() {
|
|
2903
|
+
return Boolean(this.currentTurnId && !this.steeringGateActive);
|
|
2904
|
+
}
|
|
2905
|
+
markTurnStarted(turnId) {
|
|
2906
|
+
if (turnId !== void 0 && turnId !== null) {
|
|
2907
|
+
this.currentTurnId = turnId;
|
|
2908
|
+
}
|
|
2909
|
+
this.steeringGateActive = false;
|
|
2910
|
+
}
|
|
2911
|
+
adoptTurnId(turnId) {
|
|
2912
|
+
this.currentTurnId = turnId;
|
|
2913
|
+
}
|
|
2914
|
+
markProgress() {
|
|
2915
|
+
this.steeringGateActive = false;
|
|
2916
|
+
}
|
|
2917
|
+
markToolBoundary() {
|
|
2918
|
+
this.steeringGateActive = true;
|
|
2919
|
+
}
|
|
2920
|
+
markTurnCompleted() {
|
|
2921
|
+
this.currentTurnId = null;
|
|
2922
|
+
this.steeringGateActive = false;
|
|
2923
|
+
}
|
|
2924
|
+
};
|
|
2925
|
+
|
|
2926
|
+
// src/drivers/codexTelemetrySidecar.ts
|
|
2927
|
+
function finiteNumber(value) {
|
|
2928
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2929
|
+
}
|
|
2930
|
+
function finiteString(value) {
|
|
2931
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
2932
|
+
}
|
|
2933
|
+
function ratio(numerator, denominator) {
|
|
2934
|
+
if (numerator === void 0 || denominator === void 0 || denominator <= 0) return void 0;
|
|
2935
|
+
return Number((numerator / denominator).toFixed(6));
|
|
2936
|
+
}
|
|
2937
|
+
function withDefined(attrs) {
|
|
2938
|
+
return Object.fromEntries(Object.entries(attrs).filter(([, value]) => value !== void 0));
|
|
2939
|
+
}
|
|
2940
|
+
function parseTokenUsageTelemetry(message) {
|
|
2941
|
+
const usage = message.params?.tokenUsage;
|
|
2942
|
+
const total = usage?.total;
|
|
2943
|
+
if (!total || typeof total !== "object") return null;
|
|
2944
|
+
const inputTokens = finiteNumber(total.inputTokens);
|
|
2945
|
+
const cachedInputTokens = finiteNumber(total.cachedInputTokens);
|
|
2946
|
+
const totalTokens = finiteNumber(total.totalTokens);
|
|
2947
|
+
const modelContextWindow = finiteNumber(usage.modelContextWindow);
|
|
2948
|
+
const attrs = withDefined({
|
|
2949
|
+
totalTokens,
|
|
2950
|
+
inputTokens,
|
|
2951
|
+
cachedInputTokens,
|
|
2952
|
+
outputTokens: finiteNumber(total.outputTokens),
|
|
2953
|
+
reasoningOutputTokens: finiteNumber(total.reasoningOutputTokens),
|
|
2954
|
+
modelContextWindow,
|
|
2955
|
+
cachedInputRatio: ratio(cachedInputTokens, inputTokens),
|
|
2956
|
+
contextUtilization: ratio(totalTokens, modelContextWindow)
|
|
2957
|
+
});
|
|
2958
|
+
if (Object.keys(attrs).length === 0) return null;
|
|
2959
|
+
return { kind: "telemetry", name: "token_usage", attrs };
|
|
2960
|
+
}
|
|
2961
|
+
function parseRateLimitTelemetry(message) {
|
|
2962
|
+
const rateLimits = message.params?.rateLimits;
|
|
2963
|
+
const primary = rateLimits?.primary;
|
|
2964
|
+
if (!rateLimits || typeof rateLimits !== "object" || !primary || typeof primary !== "object") return null;
|
|
2965
|
+
const attrs = withDefined({
|
|
2966
|
+
limitId: finiteString(rateLimits.limitId),
|
|
2967
|
+
planType: finiteString(rateLimits.planType),
|
|
2968
|
+
usedPercent: finiteNumber(primary.usedPercent),
|
|
2969
|
+
windowDurationMins: finiteNumber(primary.windowDurationMins),
|
|
2970
|
+
resetsAt: finiteNumber(primary.resetsAt)
|
|
2971
|
+
});
|
|
2972
|
+
if (Object.keys(attrs).length === 0) return null;
|
|
2973
|
+
return { kind: "telemetry", name: "rate_limits", attrs };
|
|
2974
|
+
}
|
|
2975
|
+
function parseCodexTelemetryEvent(message) {
|
|
2976
|
+
switch (message.method) {
|
|
2977
|
+
case "thread/tokenUsage/updated":
|
|
2978
|
+
return parseTokenUsageTelemetry(message);
|
|
2979
|
+
case "account/rateLimits/updated":
|
|
2980
|
+
return parseRateLimitTelemetry(message);
|
|
2981
|
+
default:
|
|
2982
|
+
return null;
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
|
|
2986
|
+
// src/drivers/codexEventNormalizer.ts
|
|
2987
|
+
function parseCodexJsonRpcLine(line) {
|
|
2988
|
+
try {
|
|
2989
|
+
return JSON.parse(line);
|
|
2990
|
+
} catch {
|
|
2991
|
+
return null;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2718
2994
|
function getCodexNotificationErrorMessage(params) {
|
|
2719
2995
|
const topLevelMessage = params?.message;
|
|
2720
2996
|
if (typeof topLevelMessage === "string" && topLevelMessage.trim()) {
|
|
@@ -2726,8 +3002,248 @@ function getCodexNotificationErrorMessage(params) {
|
|
|
2726
3002
|
}
|
|
2727
3003
|
return null;
|
|
2728
3004
|
}
|
|
3005
|
+
function joinReasoningText(item) {
|
|
3006
|
+
const summary = Array.isArray(item.summary) ? item.summary.filter((entry) => typeof entry === "string") : [];
|
|
3007
|
+
const content = Array.isArray(item.content) ? item.content.filter((entry) => typeof entry === "string") : [];
|
|
3008
|
+
return [...summary, ...content].join("\n").trim();
|
|
3009
|
+
}
|
|
3010
|
+
function rawResponseItemProgressEvent(message) {
|
|
3011
|
+
if (message.method !== "rawResponseItem/completed") return null;
|
|
3012
|
+
const item = message.params?.item ?? message.params?.responseItem ?? message.params?.rawItem ?? message.params;
|
|
3013
|
+
if (!item || typeof item !== "object") return null;
|
|
3014
|
+
const itemType = typeof item.type === "string" ? item.type : void 0;
|
|
3015
|
+
let payloadBytes;
|
|
3016
|
+
try {
|
|
3017
|
+
payloadBytes = Buffer.byteLength(JSON.stringify(item), "utf8");
|
|
3018
|
+
} catch {
|
|
3019
|
+
payloadBytes = void 0;
|
|
3020
|
+
}
|
|
3021
|
+
return {
|
|
3022
|
+
kind: "internal_progress",
|
|
3023
|
+
source: "codex_raw_response_item",
|
|
3024
|
+
itemType,
|
|
3025
|
+
payloadBytes
|
|
3026
|
+
};
|
|
3027
|
+
}
|
|
3028
|
+
var CodexEventNormalizer = class {
|
|
3029
|
+
mcpToolPrefix;
|
|
3030
|
+
currentThreadId = null;
|
|
3031
|
+
sessionAnnounced = false;
|
|
3032
|
+
streamedAgentMessageIds = /* @__PURE__ */ new Set();
|
|
3033
|
+
streamedReasoningIds = /* @__PURE__ */ new Set();
|
|
3034
|
+
turnState = new RuntimeTurnState();
|
|
3035
|
+
constructor(opts) {
|
|
3036
|
+
this.mcpToolPrefix = opts.mcpToolPrefix;
|
|
3037
|
+
}
|
|
3038
|
+
reset(opts = {}) {
|
|
3039
|
+
this.currentThreadId = opts.threadId ?? null;
|
|
3040
|
+
this.turnState.reset();
|
|
3041
|
+
this.sessionAnnounced = false;
|
|
3042
|
+
this.streamedAgentMessageIds.clear();
|
|
3043
|
+
this.streamedReasoningIds.clear();
|
|
3044
|
+
}
|
|
3045
|
+
get threadId() {
|
|
3046
|
+
return this.currentThreadId;
|
|
3047
|
+
}
|
|
3048
|
+
get activeTurnId() {
|
|
3049
|
+
return this.turnState.activeTurnId;
|
|
3050
|
+
}
|
|
3051
|
+
get canSteerBusy() {
|
|
3052
|
+
return this.turnState.canSteerBusy;
|
|
3053
|
+
}
|
|
3054
|
+
adoptThreadId(threadId) {
|
|
3055
|
+
this.currentThreadId = threadId;
|
|
3056
|
+
}
|
|
3057
|
+
normalizeMessage(message) {
|
|
3058
|
+
const events = [];
|
|
3059
|
+
if (message.result) {
|
|
3060
|
+
const thread = message.result.thread;
|
|
3061
|
+
if (thread && typeof thread.id === "string") {
|
|
3062
|
+
return this.handleThreadReady(thread.id, events);
|
|
3063
|
+
}
|
|
3064
|
+
const turn = message.result.turn;
|
|
3065
|
+
if (turn && typeof turn.id === "string") {
|
|
3066
|
+
this.turnState.adoptTurnId(turn.id);
|
|
3067
|
+
return { events };
|
|
3068
|
+
}
|
|
3069
|
+
if (typeof message.result.turnId === "string") {
|
|
3070
|
+
this.turnState.adoptTurnId(message.result.turnId);
|
|
3071
|
+
return { events };
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
if (message.error) {
|
|
3075
|
+
events.push({ kind: "error", message: message.error.message || "Codex app-server request failed" });
|
|
3076
|
+
return { events };
|
|
3077
|
+
}
|
|
3078
|
+
const telemetry = parseCodexTelemetryEvent(message);
|
|
3079
|
+
if (telemetry) {
|
|
3080
|
+
events.push(telemetry);
|
|
3081
|
+
return { events };
|
|
3082
|
+
}
|
|
3083
|
+
const rawProgress = rawResponseItemProgressEvent(message);
|
|
3084
|
+
if (rawProgress) {
|
|
3085
|
+
events.push(rawProgress);
|
|
3086
|
+
return { events };
|
|
3087
|
+
}
|
|
3088
|
+
switch (message.method) {
|
|
3089
|
+
case "thread/started": {
|
|
3090
|
+
const threadId = message.params?.thread?.id;
|
|
3091
|
+
if (typeof threadId === "string") {
|
|
3092
|
+
return this.handleThreadReady(threadId, events);
|
|
3093
|
+
}
|
|
3094
|
+
break;
|
|
3095
|
+
}
|
|
3096
|
+
case "turn/started": {
|
|
3097
|
+
const turnId = message.params?.turn?.id;
|
|
3098
|
+
this.turnState.markTurnStarted(typeof turnId === "string" ? turnId : null);
|
|
3099
|
+
events.push({ kind: "thinking", text: "" });
|
|
3100
|
+
break;
|
|
3101
|
+
}
|
|
3102
|
+
case "item/agentMessage/delta": {
|
|
3103
|
+
const delta = message.params?.delta;
|
|
3104
|
+
const itemId = message.params?.itemId;
|
|
3105
|
+
if (typeof itemId === "string") {
|
|
3106
|
+
this.streamedAgentMessageIds.add(itemId);
|
|
3107
|
+
}
|
|
3108
|
+
if (typeof delta === "string" && delta.length > 0) {
|
|
3109
|
+
this.turnState.markProgress();
|
|
3110
|
+
events.push({ kind: "text", text: delta });
|
|
3111
|
+
}
|
|
3112
|
+
break;
|
|
3113
|
+
}
|
|
3114
|
+
case "item/reasoning/summaryTextDelta":
|
|
3115
|
+
case "item/reasoning/textDelta": {
|
|
3116
|
+
const delta = message.params?.delta;
|
|
3117
|
+
const itemId = message.params?.itemId;
|
|
3118
|
+
if (typeof itemId === "string") {
|
|
3119
|
+
this.streamedReasoningIds.add(itemId);
|
|
3120
|
+
}
|
|
3121
|
+
if (typeof delta === "string" && delta.length > 0) {
|
|
3122
|
+
this.turnState.markProgress();
|
|
3123
|
+
events.push({ kind: "thinking", text: delta });
|
|
3124
|
+
}
|
|
3125
|
+
break;
|
|
3126
|
+
}
|
|
3127
|
+
case "item/started":
|
|
3128
|
+
case "item/completed": {
|
|
3129
|
+
const item = message.params?.item;
|
|
3130
|
+
if (!item || typeof item !== "object" || typeof item.type !== "string") break;
|
|
3131
|
+
const itemType = item.type;
|
|
3132
|
+
const isStarted = message.method === "item/started";
|
|
3133
|
+
const isCompleted = message.method === "item/completed";
|
|
3134
|
+
switch (itemType) {
|
|
3135
|
+
case "reasoning":
|
|
3136
|
+
if (isCompleted && typeof item.id === "string" && !this.streamedReasoningIds.has(item.id)) {
|
|
3137
|
+
const text = joinReasoningText(item);
|
|
3138
|
+
if (text) {
|
|
3139
|
+
this.turnState.markProgress();
|
|
3140
|
+
events.push({ kind: "thinking", text });
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
if (isCompleted && typeof item.id === "string") {
|
|
3144
|
+
this.streamedReasoningIds.delete(item.id);
|
|
3145
|
+
}
|
|
3146
|
+
break;
|
|
3147
|
+
case "agentMessage":
|
|
3148
|
+
if (isCompleted && typeof item.id === "string" && !this.streamedAgentMessageIds.has(item.id) && typeof item.text === "string" && item.text.length > 0) {
|
|
3149
|
+
this.turnState.markProgress();
|
|
3150
|
+
events.push({ kind: "text", text: item.text });
|
|
3151
|
+
}
|
|
3152
|
+
if (isCompleted && typeof item.id === "string") {
|
|
3153
|
+
this.streamedAgentMessageIds.delete(item.id);
|
|
3154
|
+
}
|
|
3155
|
+
break;
|
|
3156
|
+
case "commandExecution":
|
|
3157
|
+
if (isStarted && typeof item.command === "string") {
|
|
3158
|
+
events.push({ kind: "tool_call", name: "shell", input: { command: item.command } });
|
|
3159
|
+
}
|
|
3160
|
+
if (isCompleted) {
|
|
3161
|
+
events.push({ kind: "tool_output", name: "shell" });
|
|
3162
|
+
this.turnState.markToolBoundary();
|
|
3163
|
+
}
|
|
3164
|
+
break;
|
|
3165
|
+
case "contextCompaction":
|
|
3166
|
+
if (isStarted) {
|
|
3167
|
+
events.push({ kind: "compaction_started" });
|
|
3168
|
+
}
|
|
3169
|
+
if (isCompleted) {
|
|
3170
|
+
events.push({ kind: "compaction_finished" });
|
|
3171
|
+
}
|
|
3172
|
+
break;
|
|
3173
|
+
case "fileChange":
|
|
3174
|
+
if (isStarted && Array.isArray(item.changes)) {
|
|
3175
|
+
for (const change of item.changes) {
|
|
3176
|
+
events.push({
|
|
3177
|
+
kind: "tool_call",
|
|
3178
|
+
name: "file_change",
|
|
3179
|
+
input: { path: change?.path, kind: change?.kind }
|
|
3180
|
+
});
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
break;
|
|
3184
|
+
case "mcpToolCall":
|
|
3185
|
+
if (isStarted) {
|
|
3186
|
+
const toolName = item.server === "chat" ? `${this.mcpToolPrefix}${item.tool}` : `${this.mcpToolPrefix.replace(/_$/, "")}_${item.server}_${item.tool}`;
|
|
3187
|
+
events.push({ kind: "tool_call", name: toolName, input: item.arguments });
|
|
3188
|
+
}
|
|
3189
|
+
if (isCompleted) {
|
|
3190
|
+
const toolName = item.server === "chat" ? `${this.mcpToolPrefix}${item.tool}` : `${this.mcpToolPrefix.replace(/_$/, "")}_${item.server}_${item.tool}`;
|
|
3191
|
+
events.push({ kind: "tool_output", name: toolName });
|
|
3192
|
+
this.turnState.markToolBoundary();
|
|
3193
|
+
}
|
|
3194
|
+
break;
|
|
3195
|
+
case "collabAgentToolCall":
|
|
3196
|
+
if (isStarted) {
|
|
3197
|
+
events.push({ kind: "tool_call", name: "collab_tool_call", input: { tool: item.tool, prompt: item.prompt } });
|
|
3198
|
+
}
|
|
3199
|
+
if (isCompleted) {
|
|
3200
|
+
this.turnState.markToolBoundary();
|
|
3201
|
+
}
|
|
3202
|
+
break;
|
|
3203
|
+
case "webSearch":
|
|
3204
|
+
if (isStarted) {
|
|
3205
|
+
events.push({ kind: "tool_call", name: "web_search", input: { query: item.query } });
|
|
3206
|
+
}
|
|
3207
|
+
if (isCompleted) {
|
|
3208
|
+
this.turnState.markToolBoundary();
|
|
3209
|
+
}
|
|
3210
|
+
break;
|
|
3211
|
+
}
|
|
3212
|
+
break;
|
|
3213
|
+
}
|
|
3214
|
+
case "turn/completed": {
|
|
3215
|
+
const turn = message.params?.turn;
|
|
3216
|
+
if (turn?.status === "failed" && turn?.error?.message) {
|
|
3217
|
+
events.push({ kind: "error", message: turn.error.message });
|
|
3218
|
+
}
|
|
3219
|
+
this.turnState.markTurnCompleted();
|
|
3220
|
+
this.streamedAgentMessageIds.clear();
|
|
3221
|
+
this.streamedReasoningIds.clear();
|
|
3222
|
+
events.push({ kind: "turn_end", sessionId: this.currentThreadId || void 0 });
|
|
3223
|
+
break;
|
|
3224
|
+
}
|
|
3225
|
+
case "error":
|
|
3226
|
+
events.push({
|
|
3227
|
+
kind: "error",
|
|
3228
|
+
message: getCodexNotificationErrorMessage(message.params) || "Unknown Codex app-server error"
|
|
3229
|
+
});
|
|
3230
|
+
break;
|
|
3231
|
+
}
|
|
3232
|
+
return { events };
|
|
3233
|
+
}
|
|
3234
|
+
handleThreadReady(threadId, events) {
|
|
3235
|
+
this.currentThreadId = threadId;
|
|
3236
|
+
if (!this.sessionAnnounced) {
|
|
3237
|
+
events.push({ kind: "session_init", sessionId: threadId });
|
|
3238
|
+
this.sessionAnnounced = true;
|
|
3239
|
+
}
|
|
3240
|
+
return { events, threadReady: threadId };
|
|
3241
|
+
}
|
|
3242
|
+
};
|
|
3243
|
+
|
|
3244
|
+
// src/drivers/codex.ts
|
|
2729
3245
|
function ensureGitRepoForCodex(workingDirectory, deps = {}) {
|
|
2730
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3246
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
2731
3247
|
const execSyncFn = deps.execSyncFn ?? execSync;
|
|
2732
3248
|
const gitDir = path5.join(workingDirectory, ".git");
|
|
2733
3249
|
if (existsSyncFn(gitDir)) return;
|
|
@@ -2745,7 +3261,7 @@ function isWindowsSandboxRunner(commandPath) {
|
|
|
2745
3261
|
return path5.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
|
|
2746
3262
|
}
|
|
2747
3263
|
function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
2748
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3264
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
2749
3265
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
|
|
2750
3266
|
const env = deps.env ?? process.env;
|
|
2751
3267
|
const winPath = path5.win32;
|
|
@@ -2767,7 +3283,7 @@ function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
|
2767
3283
|
return null;
|
|
2768
3284
|
}
|
|
2769
3285
|
function resolveWindowsCodexDesktopEntry(deps = {}) {
|
|
2770
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3286
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync3;
|
|
2771
3287
|
const env = deps.env ?? process.env;
|
|
2772
3288
|
const homeDir = deps.homeDir;
|
|
2773
3289
|
const winPath = path5.win32;
|
|
@@ -2839,11 +3355,6 @@ function resolveCodexSpawn(commandArgs, deps = {}) {
|
|
|
2839
3355
|
"Cannot resolve Codex CLI entry point on Windows. Install Codex Desktop or install @openai/codex globally via npm (npm i -g @openai/codex). Ignoring .codex/.sandbox-bin/codex-command-runner because it is a sandbox helper, not the Codex CLI."
|
|
2840
3356
|
);
|
|
2841
3357
|
}
|
|
2842
|
-
function joinReasoningText(item) {
|
|
2843
|
-
const summary = Array.isArray(item.summary) ? item.summary.filter((entry) => typeof entry === "string") : [];
|
|
2844
|
-
const content = Array.isArray(item.content) ? item.content.filter((entry) => typeof entry === "string") : [];
|
|
2845
|
-
return [...summary, ...content].join("\n").trim();
|
|
2846
|
-
}
|
|
2847
3358
|
var CodexDriver = class {
|
|
2848
3359
|
id = "codex";
|
|
2849
3360
|
lifecycle = {
|
|
@@ -2919,13 +3430,20 @@ var CodexDriver = class {
|
|
|
2919
3430
|
cwd: ctx.workingDirectory,
|
|
2920
3431
|
approvalPolicy: "never",
|
|
2921
3432
|
sandbox: "danger-full-access",
|
|
2922
|
-
developerInstructions: ctx.standingPrompt
|
|
3433
|
+
developerInstructions: ctx.standingPrompt,
|
|
3434
|
+
// Raw response items are used only as payload-free liveness signals in
|
|
3435
|
+
// the daemon. They replace the previous transcript-mtime heuristic.
|
|
3436
|
+
experimentalRawEvents: true
|
|
2923
3437
|
};
|
|
2924
|
-
|
|
2925
|
-
|
|
3438
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
3439
|
+
if (launchRuntimeFields.model) {
|
|
3440
|
+
threadParams.model = launchRuntimeFields.model;
|
|
2926
3441
|
}
|
|
2927
|
-
if (
|
|
2928
|
-
threadParams.config = { model_reasoning_effort:
|
|
3442
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
3443
|
+
threadParams.config = { model_reasoning_effort: launchRuntimeFields.reasoningEffort };
|
|
3444
|
+
}
|
|
3445
|
+
if (launchRuntimeFields.mode.kind === "fast") {
|
|
3446
|
+
threadParams.serviceTier = "priority";
|
|
2929
3447
|
}
|
|
2930
3448
|
if (ctx.config.sessionId) {
|
|
2931
3449
|
return {
|
|
@@ -2943,35 +3461,21 @@ var CodexDriver = class {
|
|
|
2943
3461
|
}
|
|
2944
3462
|
process = null;
|
|
2945
3463
|
requestId = 0;
|
|
2946
|
-
threadId = null;
|
|
2947
|
-
activeTurnId = null;
|
|
2948
3464
|
pendingInitialPrompt = null;
|
|
2949
3465
|
initializeRequestId = null;
|
|
2950
3466
|
pendingThreadRequest = null;
|
|
2951
3467
|
initialTurnStarted = false;
|
|
2952
|
-
|
|
2953
|
-
streamedAgentMessageIds = /* @__PURE__ */ new Set();
|
|
2954
|
-
streamedReasoningIds = /* @__PURE__ */ new Set();
|
|
2955
|
-
/**
|
|
2956
|
-
* Post-tool window where the app-server may not yet accept stdin steering.
|
|
2957
|
-
* Gate busy-mode delivery until turn/completed or next progress.
|
|
2958
|
-
*/
|
|
2959
|
-
steeringGateActive = false;
|
|
3468
|
+
normalizer = new CodexEventNormalizer({ mcpToolPrefix: this.mcpToolPrefix });
|
|
2960
3469
|
async spawn(ctx) {
|
|
2961
3470
|
ensureGitRepoForCodex(ctx.workingDirectory);
|
|
2962
3471
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
2963
3472
|
this.process = null;
|
|
2964
3473
|
this.requestId = 0;
|
|
2965
|
-
this.threadId = ctx.config.sessionId || null;
|
|
2966
|
-
this.activeTurnId = null;
|
|
2967
3474
|
this.pendingInitialPrompt = ctx.prompt;
|
|
2968
3475
|
this.initializeRequestId = null;
|
|
2969
3476
|
this.pendingThreadRequest = null;
|
|
2970
3477
|
this.initialTurnStarted = false;
|
|
2971
|
-
this.
|
|
2972
|
-
this.streamedAgentMessageIds.clear();
|
|
2973
|
-
this.streamedReasoningIds.clear();
|
|
2974
|
-
this.steeringGateActive = false;
|
|
3478
|
+
this.normalizer.reset({ threadId: ctx.config.sessionId || null });
|
|
2975
3479
|
const args = ["app-server", "--listen", "stdio://"];
|
|
2976
3480
|
args.push(...this.buildRuntimeActionsConfigArgs(ctx));
|
|
2977
3481
|
const { command, args: spawnArgs } = resolveCodexSpawn(args);
|
|
@@ -2992,10 +3496,8 @@ var CodexDriver = class {
|
|
|
2992
3496
|
return { process: proc };
|
|
2993
3497
|
}
|
|
2994
3498
|
parseLine(line) {
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
message = JSON.parse(line);
|
|
2998
|
-
} catch {
|
|
3499
|
+
const message = parseCodexJsonRpcLine(line);
|
|
3500
|
+
if (!message) {
|
|
2999
3501
|
return [];
|
|
3000
3502
|
}
|
|
3001
3503
|
const events = [];
|
|
@@ -3009,194 +3511,34 @@ var CodexDriver = class {
|
|
|
3009
3511
|
}
|
|
3010
3512
|
return events;
|
|
3011
3513
|
}
|
|
3012
|
-
const thread = message.result.thread;
|
|
3013
|
-
if (thread && typeof thread.id === "string") {
|
|
3014
|
-
this.handleThreadReady(thread.id, events);
|
|
3015
|
-
return events;
|
|
3016
|
-
}
|
|
3017
|
-
const turn = message.result.turn;
|
|
3018
|
-
if (turn && typeof turn.id === "string") {
|
|
3019
|
-
this.activeTurnId = turn.id;
|
|
3020
|
-
return events;
|
|
3021
|
-
}
|
|
3022
|
-
if (typeof message.result.turnId === "string") {
|
|
3023
|
-
this.activeTurnId = message.result.turnId;
|
|
3024
|
-
return events;
|
|
3025
|
-
}
|
|
3026
3514
|
}
|
|
3027
|
-
if (message.error) {
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
this.pendingThreadRequest = null;
|
|
3031
|
-
}
|
|
3515
|
+
if (message.error && message.id === this.initializeRequestId) {
|
|
3516
|
+
this.initializeRequestId = null;
|
|
3517
|
+
this.pendingThreadRequest = null;
|
|
3032
3518
|
events.push({ kind: "error", message: message.error.message || "Codex app-server request failed" });
|
|
3033
3519
|
return events;
|
|
3034
3520
|
}
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
if (typeof threadId === "string") {
|
|
3039
|
-
this.handleThreadReady(threadId, events);
|
|
3040
|
-
}
|
|
3041
|
-
break;
|
|
3042
|
-
}
|
|
3043
|
-
case "turn/started": {
|
|
3044
|
-
const turnId = message.params?.turn?.id;
|
|
3045
|
-
if (typeof turnId === "string") {
|
|
3046
|
-
this.activeTurnId = turnId;
|
|
3047
|
-
}
|
|
3048
|
-
this.steeringGateActive = false;
|
|
3049
|
-
events.push({ kind: "thinking", text: "" });
|
|
3050
|
-
break;
|
|
3051
|
-
}
|
|
3052
|
-
case "item/agentMessage/delta": {
|
|
3053
|
-
const delta = message.params?.delta;
|
|
3054
|
-
const itemId = message.params?.itemId;
|
|
3055
|
-
if (typeof itemId === "string") {
|
|
3056
|
-
this.streamedAgentMessageIds.add(itemId);
|
|
3057
|
-
}
|
|
3058
|
-
if (typeof delta === "string" && delta.length > 0) {
|
|
3059
|
-
this.steeringGateActive = false;
|
|
3060
|
-
events.push({ kind: "text", text: delta });
|
|
3061
|
-
}
|
|
3062
|
-
break;
|
|
3063
|
-
}
|
|
3064
|
-
case "item/reasoning/summaryTextDelta":
|
|
3065
|
-
case "item/reasoning/textDelta": {
|
|
3066
|
-
const delta = message.params?.delta;
|
|
3067
|
-
const itemId = message.params?.itemId;
|
|
3068
|
-
if (typeof itemId === "string") {
|
|
3069
|
-
this.streamedReasoningIds.add(itemId);
|
|
3070
|
-
}
|
|
3071
|
-
if (typeof delta === "string" && delta.length > 0) {
|
|
3072
|
-
this.steeringGateActive = false;
|
|
3073
|
-
events.push({ kind: "thinking", text: delta });
|
|
3074
|
-
}
|
|
3075
|
-
break;
|
|
3076
|
-
}
|
|
3077
|
-
case "item/started":
|
|
3078
|
-
case "item/completed": {
|
|
3079
|
-
const item = message.params?.item;
|
|
3080
|
-
if (!item || typeof item !== "object" || typeof item.type !== "string") break;
|
|
3081
|
-
const itemType = item.type;
|
|
3082
|
-
const isStarted = message.method === "item/started";
|
|
3083
|
-
const isCompleted = message.method === "item/completed";
|
|
3084
|
-
switch (itemType) {
|
|
3085
|
-
case "reasoning":
|
|
3086
|
-
if (isCompleted && typeof item.id === "string" && !this.streamedReasoningIds.has(item.id)) {
|
|
3087
|
-
const text = joinReasoningText(item);
|
|
3088
|
-
if (text) {
|
|
3089
|
-
this.steeringGateActive = false;
|
|
3090
|
-
events.push({ kind: "thinking", text });
|
|
3091
|
-
}
|
|
3092
|
-
}
|
|
3093
|
-
if (isCompleted && typeof item.id === "string") {
|
|
3094
|
-
this.streamedReasoningIds.delete(item.id);
|
|
3095
|
-
}
|
|
3096
|
-
break;
|
|
3097
|
-
case "agentMessage":
|
|
3098
|
-
if (isCompleted && typeof item.id === "string" && !this.streamedAgentMessageIds.has(item.id) && typeof item.text === "string" && item.text.length > 0) {
|
|
3099
|
-
this.steeringGateActive = false;
|
|
3100
|
-
events.push({ kind: "text", text: item.text });
|
|
3101
|
-
}
|
|
3102
|
-
if (isCompleted && typeof item.id === "string") {
|
|
3103
|
-
this.streamedAgentMessageIds.delete(item.id);
|
|
3104
|
-
}
|
|
3105
|
-
break;
|
|
3106
|
-
case "commandExecution":
|
|
3107
|
-
if (isStarted && typeof item.command === "string") {
|
|
3108
|
-
events.push({ kind: "tool_call", name: "shell", input: { command: item.command } });
|
|
3109
|
-
}
|
|
3110
|
-
if (isCompleted) {
|
|
3111
|
-
events.push({ kind: "tool_output", name: "shell" });
|
|
3112
|
-
this.steeringGateActive = true;
|
|
3113
|
-
}
|
|
3114
|
-
break;
|
|
3115
|
-
case "contextCompaction":
|
|
3116
|
-
if (isStarted) {
|
|
3117
|
-
events.push({ kind: "compaction_started" });
|
|
3118
|
-
}
|
|
3119
|
-
if (isCompleted) {
|
|
3120
|
-
events.push({ kind: "compaction_finished" });
|
|
3121
|
-
}
|
|
3122
|
-
break;
|
|
3123
|
-
case "fileChange":
|
|
3124
|
-
if (isStarted && Array.isArray(item.changes)) {
|
|
3125
|
-
for (const change of item.changes) {
|
|
3126
|
-
events.push({
|
|
3127
|
-
kind: "tool_call",
|
|
3128
|
-
name: "file_change",
|
|
3129
|
-
input: { path: change?.path, kind: change?.kind }
|
|
3130
|
-
});
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
break;
|
|
3134
|
-
case "mcpToolCall":
|
|
3135
|
-
if (isStarted) {
|
|
3136
|
-
const toolName = item.server === "chat" ? `${this.mcpToolPrefix}${item.tool}` : `${this.mcpToolPrefix.replace(/_$/, "")}_${item.server}_${item.tool}`;
|
|
3137
|
-
events.push({ kind: "tool_call", name: toolName, input: item.arguments });
|
|
3138
|
-
}
|
|
3139
|
-
if (isCompleted) {
|
|
3140
|
-
const toolName = item.server === "chat" ? `${this.mcpToolPrefix}${item.tool}` : `${this.mcpToolPrefix.replace(/_$/, "")}_${item.server}_${item.tool}`;
|
|
3141
|
-
events.push({ kind: "tool_output", name: toolName });
|
|
3142
|
-
this.steeringGateActive = true;
|
|
3143
|
-
}
|
|
3144
|
-
break;
|
|
3145
|
-
case "collabAgentToolCall":
|
|
3146
|
-
if (isStarted) {
|
|
3147
|
-
events.push({ kind: "tool_call", name: "collab_tool_call", input: { tool: item.tool, prompt: item.prompt } });
|
|
3148
|
-
}
|
|
3149
|
-
if (isCompleted) {
|
|
3150
|
-
this.steeringGateActive = true;
|
|
3151
|
-
}
|
|
3152
|
-
break;
|
|
3153
|
-
case "webSearch":
|
|
3154
|
-
if (isStarted) {
|
|
3155
|
-
events.push({ kind: "tool_call", name: "web_search", input: { query: item.query } });
|
|
3156
|
-
}
|
|
3157
|
-
if (isCompleted) {
|
|
3158
|
-
this.steeringGateActive = true;
|
|
3159
|
-
}
|
|
3160
|
-
break;
|
|
3161
|
-
}
|
|
3162
|
-
break;
|
|
3163
|
-
}
|
|
3164
|
-
case "turn/completed": {
|
|
3165
|
-
const turn = message.params?.turn;
|
|
3166
|
-
if (turn?.status === "failed" && turn?.error?.message) {
|
|
3167
|
-
events.push({ kind: "error", message: turn.error.message });
|
|
3168
|
-
}
|
|
3169
|
-
this.activeTurnId = null;
|
|
3170
|
-
this.streamedAgentMessageIds.clear();
|
|
3171
|
-
this.streamedReasoningIds.clear();
|
|
3172
|
-
this.steeringGateActive = false;
|
|
3173
|
-
events.push({ kind: "turn_end", sessionId: this.threadId || void 0 });
|
|
3174
|
-
break;
|
|
3175
|
-
}
|
|
3176
|
-
case "error":
|
|
3177
|
-
events.push({
|
|
3178
|
-
kind: "error",
|
|
3179
|
-
message: getCodexNotificationErrorMessage(message.params) || "Unknown Codex app-server error"
|
|
3180
|
-
});
|
|
3181
|
-
break;
|
|
3521
|
+
const result = this.normalizer.normalizeMessage(message);
|
|
3522
|
+
if (result.threadReady) {
|
|
3523
|
+
this.startInitialTurn();
|
|
3182
3524
|
}
|
|
3183
|
-
return events;
|
|
3525
|
+
return result.events;
|
|
3184
3526
|
}
|
|
3185
3527
|
encodeStdinMessage(text, sessionId, opts) {
|
|
3186
|
-
if (!this.threadId && sessionId) {
|
|
3187
|
-
this.
|
|
3528
|
+
if (!this.normalizer.threadId && sessionId) {
|
|
3529
|
+
this.normalizer.adoptThreadId(sessionId);
|
|
3188
3530
|
}
|
|
3189
|
-
if (!this.threadId) return null;
|
|
3531
|
+
if (!this.normalizer.threadId) return null;
|
|
3190
3532
|
const mode = opts?.mode || "busy";
|
|
3191
3533
|
if (mode === "busy") {
|
|
3192
|
-
if (!this.
|
|
3534
|
+
if (!this.normalizer.canSteerBusy) return null;
|
|
3193
3535
|
return JSON.stringify({
|
|
3194
3536
|
jsonrpc: "2.0",
|
|
3195
3537
|
id: this.nextRequestId(),
|
|
3196
3538
|
method: "turn/steer",
|
|
3197
3539
|
params: {
|
|
3198
|
-
threadId: this.threadId,
|
|
3199
|
-
expectedTurnId: this.activeTurnId,
|
|
3540
|
+
threadId: this.normalizer.threadId,
|
|
3541
|
+
expectedTurnId: this.normalizer.activeTurnId,
|
|
3200
3542
|
input: [{ type: "text", text }]
|
|
3201
3543
|
}
|
|
3202
3544
|
});
|
|
@@ -3206,7 +3548,7 @@ var CodexDriver = class {
|
|
|
3206
3548
|
id: this.nextRequestId(),
|
|
3207
3549
|
method: "turn/start",
|
|
3208
3550
|
params: {
|
|
3209
|
-
threadId: this.threadId,
|
|
3551
|
+
threadId: this.normalizer.threadId,
|
|
3210
3552
|
input: [{ type: "text", text }]
|
|
3211
3553
|
}
|
|
3212
3554
|
});
|
|
@@ -3226,21 +3568,13 @@ var CodexDriver = class {
|
|
|
3226
3568
|
this.requestId += 1;
|
|
3227
3569
|
return this.requestId;
|
|
3228
3570
|
}
|
|
3229
|
-
handleThreadReady(threadId, events) {
|
|
3230
|
-
this.threadId = threadId;
|
|
3231
|
-
if (!this.sessionAnnounced) {
|
|
3232
|
-
events.push({ kind: "session_init", sessionId: threadId });
|
|
3233
|
-
this.sessionAnnounced = true;
|
|
3234
|
-
}
|
|
3235
|
-
this.startInitialTurn();
|
|
3236
|
-
}
|
|
3237
3571
|
startInitialTurn() {
|
|
3238
|
-
if (this.initialTurnStarted || !this.pendingInitialPrompt || !this.threadId) return;
|
|
3572
|
+
if (this.initialTurnStarted || !this.pendingInitialPrompt || !this.normalizer.threadId) return;
|
|
3239
3573
|
this.initialTurnStarted = true;
|
|
3240
3574
|
const prompt = this.pendingInitialPrompt;
|
|
3241
3575
|
this.pendingInitialPrompt = null;
|
|
3242
3576
|
this.sendRequest("turn/start", {
|
|
3243
|
-
threadId: this.threadId,
|
|
3577
|
+
threadId: this.normalizer.threadId,
|
|
3244
3578
|
input: [{ type: "text", text: prompt }]
|
|
3245
3579
|
});
|
|
3246
3580
|
}
|
|
@@ -3265,12 +3599,12 @@ var CodexDriver = class {
|
|
|
3265
3599
|
return detectCodexModels();
|
|
3266
3600
|
}
|
|
3267
3601
|
};
|
|
3268
|
-
function detectCodexModels(home =
|
|
3602
|
+
function detectCodexModels(home = os2.homedir()) {
|
|
3269
3603
|
const cachePath = path5.join(home, ".codex", "models_cache.json");
|
|
3270
3604
|
const configPath = path5.join(home, ".codex", "config.toml");
|
|
3271
3605
|
let models = [];
|
|
3272
3606
|
try {
|
|
3273
|
-
const raw =
|
|
3607
|
+
const raw = readFileSync2(cachePath, "utf8");
|
|
3274
3608
|
const parsed = JSON.parse(raw);
|
|
3275
3609
|
const entries = Array.isArray(parsed?.models) ? parsed.models : [];
|
|
3276
3610
|
for (const entry of entries) {
|
|
@@ -3287,7 +3621,7 @@ function detectCodexModels(home = os3.homedir()) {
|
|
|
3287
3621
|
if (models.length === 0) return null;
|
|
3288
3622
|
let defaultModel;
|
|
3289
3623
|
try {
|
|
3290
|
-
const raw =
|
|
3624
|
+
const raw = readFileSync2(configPath, "utf8");
|
|
3291
3625
|
const match = raw.match(/^\s*model\s*=\s*"([^"]+)"/m);
|
|
3292
3626
|
if (match) defaultModel = match[1];
|
|
3293
3627
|
} catch {
|
|
@@ -3318,7 +3652,7 @@ function buildAntigravityArgs(ctx) {
|
|
|
3318
3652
|
const args = [
|
|
3319
3653
|
"--print",
|
|
3320
3654
|
"--print-timeout",
|
|
3321
|
-
ctx.config.envVars?.ANTIGRAVITY_PRINT_TIMEOUT || DEFAULT_PRINT_TIMEOUT,
|
|
3655
|
+
runtimeConfigToLaunchFields(ctx.config).envVars?.ANTIGRAVITY_PRINT_TIMEOUT || DEFAULT_PRINT_TIMEOUT,
|
|
3322
3656
|
"--dangerously-skip-permissions"
|
|
3323
3657
|
];
|
|
3324
3658
|
if (ctx.config.sessionId) {
|
|
@@ -3497,11 +3831,12 @@ var CopilotDriver = class {
|
|
|
3497
3831
|
"-p",
|
|
3498
3832
|
ctx.prompt
|
|
3499
3833
|
];
|
|
3500
|
-
|
|
3501
|
-
|
|
3834
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
3835
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
3836
|
+
args.push("--model", launchRuntimeFields.model);
|
|
3502
3837
|
}
|
|
3503
|
-
if (
|
|
3504
|
-
args.push("--effort",
|
|
3838
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
3839
|
+
args.push("--effort", launchRuntimeFields.reasoningEffort);
|
|
3505
3840
|
}
|
|
3506
3841
|
if (ctx.config.sessionId) {
|
|
3507
3842
|
args.push(`--resume=${ctx.config.sessionId}`);
|
|
@@ -3601,7 +3936,7 @@ var CopilotDriver = class {
|
|
|
3601
3936
|
|
|
3602
3937
|
// src/drivers/cursor.ts
|
|
3603
3938
|
import { spawn as spawn5, spawnSync } from "child_process";
|
|
3604
|
-
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as
|
|
3939
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
3605
3940
|
import path7 from "path";
|
|
3606
3941
|
async function buildCursorSpawnEnv(ctx, deps = {}) {
|
|
3607
3942
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
@@ -3657,7 +3992,7 @@ var CursorDriver = class {
|
|
|
3657
3992
|
}
|
|
3658
3993
|
async spawn(ctx) {
|
|
3659
3994
|
const cursorDir = path7.join(ctx.workingDirectory, ".cursor");
|
|
3660
|
-
if (!
|
|
3995
|
+
if (!existsSync4(cursorDir)) {
|
|
3661
3996
|
mkdirSync2(cursorDir, { recursive: true });
|
|
3662
3997
|
}
|
|
3663
3998
|
const mcpConfigPath = path7.join(cursorDir, "mcp.json");
|
|
@@ -3670,8 +4005,9 @@ var CursorDriver = class {
|
|
|
3670
4005
|
"--approve-mcps",
|
|
3671
4006
|
"--trust"
|
|
3672
4007
|
];
|
|
3673
|
-
|
|
3674
|
-
|
|
4008
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4009
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4010
|
+
args.push("--model", launchRuntimeFields.model);
|
|
3675
4011
|
}
|
|
3676
4012
|
if (ctx.config.sessionId) {
|
|
3677
4013
|
args.push("--resume", ctx.config.sessionId);
|
|
@@ -3790,11 +4126,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
3790
4126
|
return parseCursorModelsOutput(String(result.stdout || ""));
|
|
3791
4127
|
}
|
|
3792
4128
|
function buildCursorModelProbeEnv(deps = {}) {
|
|
3793
|
-
return withWindowsUserEnvironment({
|
|
4129
|
+
return scrubDaemonChildEnv(withWindowsUserEnvironment({
|
|
3794
4130
|
...deps.env ?? process.env,
|
|
3795
4131
|
FORCE_COLOR: "0",
|
|
3796
4132
|
NO_COLOR: "1"
|
|
3797
|
-
}, deps);
|
|
4133
|
+
}, deps));
|
|
3798
4134
|
}
|
|
3799
4135
|
function runCursorModelsCommand() {
|
|
3800
4136
|
return spawnSync("cursor-agent", ["models"], {
|
|
@@ -3806,14 +4142,15 @@ function runCursorModelsCommand() {
|
|
|
3806
4142
|
|
|
3807
4143
|
// src/drivers/gemini.ts
|
|
3808
4144
|
import { execFileSync as execFileSync3, spawn as spawn6 } from "child_process";
|
|
3809
|
-
import { existsSync as
|
|
4145
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
3810
4146
|
import path8 from "path";
|
|
3811
4147
|
async function buildGeminiSpawnEnv(ctx, platform = process.platform) {
|
|
3812
4148
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
|
|
3813
|
-
|
|
4149
|
+
const launchEnvVars = runtimeConfigToLaunchFields(ctx.config).envVars;
|
|
4150
|
+
if (!Object.prototype.hasOwnProperty.call(launchEnvVars ?? {}, "GEMINI_CLI_TRUST_WORKSPACE")) {
|
|
3814
4151
|
spawnEnv.GEMINI_CLI_TRUST_WORKSPACE = "true";
|
|
3815
4152
|
}
|
|
3816
|
-
if (platform === "win32" && !Object.prototype.hasOwnProperty.call(
|
|
4153
|
+
if (platform === "win32" && !Object.prototype.hasOwnProperty.call(launchEnvVars ?? {}, "GEMINI_PTY_INFO")) {
|
|
3817
4154
|
spawnEnv.GEMINI_PTY_INFO = "child_process";
|
|
3818
4155
|
}
|
|
3819
4156
|
return spawnEnv;
|
|
@@ -3833,8 +4170,9 @@ function buildGeminiArgs(config) {
|
|
|
3833
4170
|
"-p",
|
|
3834
4171
|
""
|
|
3835
4172
|
];
|
|
3836
|
-
|
|
3837
|
-
|
|
4173
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(config);
|
|
4174
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4175
|
+
args.push("--model", launchRuntimeFields.model);
|
|
3838
4176
|
}
|
|
3839
4177
|
if (config.sessionId) {
|
|
3840
4178
|
args.push("--resume", config.sessionId);
|
|
@@ -3847,8 +4185,8 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
3847
4185
|
return { command: resolveCommandOnPath("gemini", deps) ?? "gemini", args: commandArgs };
|
|
3848
4186
|
}
|
|
3849
4187
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
3850
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
3851
|
-
const env = deps.env ?? process.env;
|
|
4188
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
4189
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
3852
4190
|
const winPath = path8.win32;
|
|
3853
4191
|
let geminiEntry = null;
|
|
3854
4192
|
try {
|
|
@@ -4020,13 +4358,16 @@ var GeminiDriver = class {
|
|
|
4020
4358
|
// src/drivers/kimi.ts
|
|
4021
4359
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4022
4360
|
import { spawn as spawn7 } from "child_process";
|
|
4023
|
-
import { existsSync as
|
|
4024
|
-
import
|
|
4361
|
+
import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
4362
|
+
import os3 from "os";
|
|
4025
4363
|
import path9 from "path";
|
|
4026
4364
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
4027
4365
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
4028
4366
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
4029
4367
|
var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
|
|
4368
|
+
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
4369
|
+
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
4370
|
+
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
4030
4371
|
function parseToolArguments(raw) {
|
|
4031
4372
|
if (typeof raw !== "string") return raw;
|
|
4032
4373
|
try {
|
|
@@ -4035,6 +4376,73 @@ function parseToolArguments(raw) {
|
|
|
4035
4376
|
return raw;
|
|
4036
4377
|
}
|
|
4037
4378
|
}
|
|
4379
|
+
function readKimiConfigSource(home = os3.homedir(), env = process.env) {
|
|
4380
|
+
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
4381
|
+
if (inlineConfig && inlineConfig.trim()) {
|
|
4382
|
+
return {
|
|
4383
|
+
raw: inlineConfig,
|
|
4384
|
+
explicitPath: null,
|
|
4385
|
+
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
4386
|
+
};
|
|
4387
|
+
}
|
|
4388
|
+
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
4389
|
+
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
|
|
4390
|
+
try {
|
|
4391
|
+
return {
|
|
4392
|
+
raw: readFileSync3(configPath, "utf8"),
|
|
4393
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
4394
|
+
sourcePath: configPath
|
|
4395
|
+
};
|
|
4396
|
+
} catch {
|
|
4397
|
+
return {
|
|
4398
|
+
raw: null,
|
|
4399
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
4400
|
+
sourcePath: configPath
|
|
4401
|
+
};
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
function buildKimiSpawnEnv(env = process.env) {
|
|
4405
|
+
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
4406
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
4407
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
4408
|
+
return scrubDaemonChildEnv(spawnEnv);
|
|
4409
|
+
}
|
|
4410
|
+
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
4411
|
+
return {
|
|
4412
|
+
...process.env,
|
|
4413
|
+
...ctx.config.envVars || {},
|
|
4414
|
+
...overrideEnv || {}
|
|
4415
|
+
};
|
|
4416
|
+
}
|
|
4417
|
+
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
4418
|
+
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
4419
|
+
const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
|
|
4420
|
+
const args = [];
|
|
4421
|
+
let configFilePath = null;
|
|
4422
|
+
let configContent = null;
|
|
4423
|
+
if (source.explicitPath) {
|
|
4424
|
+
configFilePath = source.explicitPath;
|
|
4425
|
+
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
4426
|
+
configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
4427
|
+
configContent = source.raw;
|
|
4428
|
+
if (opts.writeGeneratedConfig !== false) {
|
|
4429
|
+
writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
4430
|
+
chmodSync(configFilePath, 384);
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
if (configFilePath) {
|
|
4434
|
+
args.push("--config-file", configFilePath);
|
|
4435
|
+
}
|
|
4436
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
4437
|
+
args.push("--model", ctx.config.model);
|
|
4438
|
+
}
|
|
4439
|
+
return {
|
|
4440
|
+
args,
|
|
4441
|
+
env: buildKimiSpawnEnv(env),
|
|
4442
|
+
configFilePath,
|
|
4443
|
+
configContent
|
|
4444
|
+
};
|
|
4445
|
+
}
|
|
4038
4446
|
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
4039
4447
|
return {
|
|
4040
4448
|
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
@@ -4058,7 +4466,25 @@ var KimiDriver = class {
|
|
|
4058
4466
|
};
|
|
4059
4467
|
model = {
|
|
4060
4468
|
detectedModelsVerifiedAs: "launchable",
|
|
4061
|
-
toLaunchSpec: (modelId) =>
|
|
4469
|
+
toLaunchSpec: (modelId, ctx, opts) => {
|
|
4470
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
4471
|
+
const launchCtx = {
|
|
4472
|
+
...ctx,
|
|
4473
|
+
config: {
|
|
4474
|
+
...ctx.config,
|
|
4475
|
+
model: modelId
|
|
4476
|
+
}
|
|
4477
|
+
};
|
|
4478
|
+
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
4479
|
+
home: opts?.home,
|
|
4480
|
+
writeGeneratedConfig: false
|
|
4481
|
+
});
|
|
4482
|
+
return {
|
|
4483
|
+
args: launch.args,
|
|
4484
|
+
env: launch.env,
|
|
4485
|
+
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
4486
|
+
};
|
|
4487
|
+
}
|
|
4062
4488
|
};
|
|
4063
4489
|
supportsStdinNotification = true;
|
|
4064
4490
|
mcpToolPrefix = "";
|
|
@@ -4094,7 +4520,7 @@ var KimiDriver = class {
|
|
|
4094
4520
|
const systemPromptPath = path9.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
|
|
4095
4521
|
const agentFilePath = path9.join(ctx.workingDirectory, KIMI_AGENT_FILE);
|
|
4096
4522
|
const mcpConfigPath = path9.join(ctx.workingDirectory, KIMI_MCP_FILE);
|
|
4097
|
-
if (!isResume || !
|
|
4523
|
+
if (!isResume || !existsSync6(systemPromptPath)) {
|
|
4098
4524
|
writeFileSync6(systemPromptPath, ctx.prompt, "utf8");
|
|
4099
4525
|
}
|
|
4100
4526
|
writeFileSync6(agentFilePath, [
|
|
@@ -4112,6 +4538,7 @@ var KimiDriver = class {
|
|
|
4112
4538
|
}
|
|
4113
4539
|
}
|
|
4114
4540
|
}), "utf8");
|
|
4541
|
+
const launch = buildKimiLaunchOptions(ctx);
|
|
4115
4542
|
const args = [
|
|
4116
4543
|
"--wire",
|
|
4117
4544
|
"--yolo",
|
|
@@ -4120,14 +4547,16 @@ var KimiDriver = class {
|
|
|
4120
4547
|
"--mcp-config-file",
|
|
4121
4548
|
mcpConfigPath,
|
|
4122
4549
|
"--session",
|
|
4123
|
-
this.sessionId
|
|
4550
|
+
this.sessionId,
|
|
4551
|
+
...launch.args
|
|
4124
4552
|
];
|
|
4125
|
-
|
|
4126
|
-
|
|
4553
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4554
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4555
|
+
args.push("--model", launchRuntimeFields.model);
|
|
4127
4556
|
}
|
|
4128
4557
|
const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
4129
|
-
const
|
|
4130
|
-
const proc = spawn7(
|
|
4558
|
+
const spawnTarget = resolveKimiSpawn(args);
|
|
4559
|
+
const proc = spawn7(spawnTarget.command, spawnTarget.args, {
|
|
4131
4560
|
cwd: ctx.workingDirectory,
|
|
4132
4561
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4133
4562
|
env: spawnEnv,
|
|
@@ -4135,7 +4564,7 @@ var KimiDriver = class {
|
|
|
4135
4564
|
// and has an 8191-character command-line limit. Kimi's official
|
|
4136
4565
|
// installer/uv entrypoint is an executable, so launch it directly and
|
|
4137
4566
|
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
4138
|
-
shell:
|
|
4567
|
+
shell: spawnTarget.shell
|
|
4139
4568
|
});
|
|
4140
4569
|
proc.stdin?.write(JSON.stringify({
|
|
4141
4570
|
jsonrpc: "2.0",
|
|
@@ -4249,14 +4678,9 @@ var KimiDriver = class {
|
|
|
4249
4678
|
return detectKimiModels();
|
|
4250
4679
|
}
|
|
4251
4680
|
};
|
|
4252
|
-
function detectKimiModels(home =
|
|
4253
|
-
const
|
|
4254
|
-
|
|
4255
|
-
try {
|
|
4256
|
-
raw = readFileSync4(configPath, "utf8");
|
|
4257
|
-
} catch {
|
|
4258
|
-
return null;
|
|
4259
|
-
}
|
|
4681
|
+
function detectKimiModels(home = os3.homedir(), opts = {}) {
|
|
4682
|
+
const raw = readKimiConfigSource(home, opts.env).raw;
|
|
4683
|
+
if (raw === null) return null;
|
|
4260
4684
|
const models = [];
|
|
4261
4685
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
4262
4686
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -4277,8 +4701,8 @@ function detectKimiModels(home = os4.homedir()) {
|
|
|
4277
4701
|
|
|
4278
4702
|
// src/drivers/opencode.ts
|
|
4279
4703
|
import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
|
|
4280
|
-
import { existsSync as
|
|
4281
|
-
import
|
|
4704
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
|
|
4705
|
+
import os4 from "os";
|
|
4282
4706
|
import path10 from "path";
|
|
4283
4707
|
var CHAT_MCP_SERVER_NAME = "chat";
|
|
4284
4708
|
var CHAT_MCP_TOOL_PREFIX = `${CHAT_MCP_SERVER_NAME}_`;
|
|
@@ -4323,13 +4747,13 @@ function parseOpenCodeConfigContent(raw) {
|
|
|
4323
4747
|
return {};
|
|
4324
4748
|
}
|
|
4325
4749
|
function parseUserOpenCodeConfig(ctx) {
|
|
4326
|
-
const raw = ctx.config.envVars?.OPENCODE_CONFIG_CONTENT;
|
|
4750
|
+
const raw = runtimeConfigToLaunchFields(ctx.config).envVars?.OPENCODE_CONFIG_CONTENT;
|
|
4327
4751
|
return parseOpenCodeConfigContent(raw);
|
|
4328
4752
|
}
|
|
4329
|
-
function readLocalOpenCodeConfig(home =
|
|
4753
|
+
function readLocalOpenCodeConfig(home = os4.homedir()) {
|
|
4330
4754
|
const configPath = path10.join(home, ".config", "opencode", "opencode.json");
|
|
4331
4755
|
try {
|
|
4332
|
-
return parseOpenCodeConfigContent(
|
|
4756
|
+
return parseOpenCodeConfigContent(readFileSync4(configPath, "utf8"));
|
|
4333
4757
|
} catch {
|
|
4334
4758
|
}
|
|
4335
4759
|
return {};
|
|
@@ -4387,7 +4811,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
|
|
|
4387
4811
|
}
|
|
4388
4812
|
};
|
|
4389
4813
|
}
|
|
4390
|
-
function buildOpenCodeConfig(ctx, home =
|
|
4814
|
+
function buildOpenCodeConfig(ctx, home = os4.homedir()) {
|
|
4391
4815
|
const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
|
|
4392
4816
|
const userAgents = recordField(userConfig.agent);
|
|
4393
4817
|
const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
|
|
@@ -4412,7 +4836,7 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
|
|
|
4412
4836
|
}
|
|
4413
4837
|
};
|
|
4414
4838
|
}
|
|
4415
|
-
async function buildOpenCodeLaunchOptions(ctx, home =
|
|
4839
|
+
async function buildOpenCodeLaunchOptions(ctx, home = os4.homedir(), version = null) {
|
|
4416
4840
|
const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
4417
4841
|
const config = buildOpenCodeConfig(ctx, home);
|
|
4418
4842
|
const env = {
|
|
@@ -4428,8 +4852,9 @@ async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = n
|
|
|
4428
4852
|
"--dir",
|
|
4429
4853
|
ctx.workingDirectory
|
|
4430
4854
|
];
|
|
4431
|
-
|
|
4432
|
-
|
|
4855
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
4856
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
4857
|
+
args.push("--model", launchRuntimeFields.model);
|
|
4433
4858
|
}
|
|
4434
4859
|
if (requiresAgentCliFlag(version)) {
|
|
4435
4860
|
args.push("--agent", SLOCK_AGENT_NAME);
|
|
@@ -4510,7 +4935,7 @@ function formatOpenCodeLabelToken(token) {
|
|
|
4510
4935
|
if (/^\d/.test(token)) return token;
|
|
4511
4936
|
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
4512
4937
|
}
|
|
4513
|
-
function detectOpenCodeModels(home =
|
|
4938
|
+
function detectOpenCodeModels(home = os4.homedir(), runCommand = runOpenCodeModelsCommand) {
|
|
4514
4939
|
const commandResult = runCommand(home);
|
|
4515
4940
|
if (commandResult.error || commandResult.status !== 0) return null;
|
|
4516
4941
|
return parseOpenCodeModelsOutput(commandResult.stdout);
|
|
@@ -4519,7 +4944,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
4519
4944
|
const platform = deps.platform ?? process.platform;
|
|
4520
4945
|
const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
|
|
4521
4946
|
const result = spawnSyncFn("opencode", ["models"], {
|
|
4522
|
-
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
4947
|
+
env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
4523
4948
|
encoding: "utf8",
|
|
4524
4949
|
timeout: 5e3,
|
|
4525
4950
|
shell: platform === "win32"
|
|
@@ -4550,7 +4975,7 @@ function openCodeSpecForEntry(entry, commandArgs) {
|
|
|
4550
4975
|
return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
|
|
4551
4976
|
}
|
|
4552
4977
|
function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
4553
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
4978
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync7;
|
|
4554
4979
|
const execFileSyncFn = deps.execFileSyncFn;
|
|
4555
4980
|
const env = deps.env ?? process.env;
|
|
4556
4981
|
const winPath = path10.win32;
|
|
@@ -4580,7 +5005,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
|
4580
5005
|
}
|
|
4581
5006
|
function extractWindowsShimTargets(commandPath, deps = {}) {
|
|
4582
5007
|
if (!isWindowsCommandShim(commandPath)) return [];
|
|
4583
|
-
const readFileSyncFn = deps.readFileSyncFn ??
|
|
5008
|
+
const readFileSyncFn = deps.readFileSyncFn ?? readFileSync4;
|
|
4584
5009
|
const commandDir = path10.win32.dirname(commandPath);
|
|
4585
5010
|
let raw;
|
|
4586
5011
|
try {
|
|
@@ -4714,7 +5139,7 @@ var OpenCodeDriver = class {
|
|
|
4714
5139
|
if (unsupportedMessage) {
|
|
4715
5140
|
throw new Error(unsupportedMessage);
|
|
4716
5141
|
}
|
|
4717
|
-
const launch = await buildOpenCodeLaunchOptions(ctx,
|
|
5142
|
+
const launch = await buildOpenCodeLaunchOptions(ctx, os4.homedir(), version);
|
|
4718
5143
|
const spawnSpec = resolveOpenCodeSpawn(launch.args);
|
|
4719
5144
|
const proc = spawn8(spawnSpec.command, spawnSpec.args, {
|
|
4720
5145
|
cwd: ctx.workingDirectory,
|
|
@@ -4778,6 +5203,297 @@ var OpenCodeDriver = class {
|
|
|
4778
5203
|
}
|
|
4779
5204
|
};
|
|
4780
5205
|
|
|
5206
|
+
// src/drivers/pi.ts
|
|
5207
|
+
import { spawn as spawn9 } from "child_process";
|
|
5208
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
5209
|
+
import path11 from "path";
|
|
5210
|
+
import { fileURLToPath } from "url";
|
|
5211
|
+
import { getAgentDir, VERSION as PI_SDK_VERSION } from "@earendil-works/pi-coding-agent";
|
|
5212
|
+
var CHAT_MCP_TOOL_PREFIX2 = "chat_";
|
|
5213
|
+
var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
|
|
5214
|
+
var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
|
|
5215
|
+
var MIN_SUPPORTED_PI_VERSION = "0.74.0";
|
|
5216
|
+
function parseSemver2(version) {
|
|
5217
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
5218
|
+
if (!match) return null;
|
|
5219
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
5220
|
+
}
|
|
5221
|
+
function isSupportedPiVersion(version) {
|
|
5222
|
+
if (!version) return true;
|
|
5223
|
+
const actual = parseSemver2(version);
|
|
5224
|
+
const minimum = parseSemver2(MIN_SUPPORTED_PI_VERSION);
|
|
5225
|
+
if (!actual || !minimum) return true;
|
|
5226
|
+
for (let i = 0; i < 3; i += 1) {
|
|
5227
|
+
if (actual[i] > minimum[i]) return true;
|
|
5228
|
+
if (actual[i] < minimum[i]) return false;
|
|
5229
|
+
}
|
|
5230
|
+
return true;
|
|
5231
|
+
}
|
|
5232
|
+
function unsupportedPiVersionMessage(version) {
|
|
5233
|
+
if (!version || isSupportedPiVersion(version)) return null;
|
|
5234
|
+
return `Pi SDK ${version} is unsupported; requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION}. Upgrade the daemon Pi dependency before starting this runtime.`;
|
|
5235
|
+
}
|
|
5236
|
+
function probePi(version = PI_SDK_VERSION) {
|
|
5237
|
+
const unsupportedMessage = unsupportedPiVersionMessage(version);
|
|
5238
|
+
if (unsupportedMessage) {
|
|
5239
|
+
return {
|
|
5240
|
+
available: false,
|
|
5241
|
+
version: `${version} (requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION})`
|
|
5242
|
+
};
|
|
5243
|
+
}
|
|
5244
|
+
return { available: true, version };
|
|
5245
|
+
}
|
|
5246
|
+
function resolvePiSdkRunnerPath(moduleUrl = import.meta.url) {
|
|
5247
|
+
const moduleDir = path11.dirname(fileURLToPath(moduleUrl));
|
|
5248
|
+
const sourceSibling = path11.join(moduleDir, "piSdkRunner.ts");
|
|
5249
|
+
if (existsSync8(sourceSibling)) return sourceSibling;
|
|
5250
|
+
const bundledEntry = path11.join(moduleDir, "drivers", "piSdkRunner.js");
|
|
5251
|
+
if (existsSync8(bundledEntry)) return bundledEntry;
|
|
5252
|
+
return path11.join(moduleDir, "piSdkRunner.js");
|
|
5253
|
+
}
|
|
5254
|
+
function buildPiSdkNodeArgs(runnerPath = resolvePiSdkRunnerPath()) {
|
|
5255
|
+
if (runnerPath.endsWith(".ts")) {
|
|
5256
|
+
return [...process.execArgv, runnerPath];
|
|
5257
|
+
}
|
|
5258
|
+
return [runnerPath];
|
|
5259
|
+
}
|
|
5260
|
+
async function buildPiLaunchOptions(ctx, opts = {}) {
|
|
5261
|
+
const command = opts.command ?? process.execPath;
|
|
5262
|
+
const piDir = path11.join(ctx.workingDirectory, ".slock", "pi");
|
|
5263
|
+
const sessionDir = path11.join(piDir, "sessions");
|
|
5264
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
5265
|
+
const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
5266
|
+
const runnerPath = opts.runnerPath ?? resolvePiSdkRunnerPath();
|
|
5267
|
+
const agentDir = opts.agentDir ?? getAgentDir();
|
|
5268
|
+
const runnerConfigPath = path11.join(
|
|
5269
|
+
piDir,
|
|
5270
|
+
`sdk-run-${(ctx.launchId || "launch").replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`
|
|
5271
|
+
);
|
|
5272
|
+
const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
|
|
5273
|
+
const runnerConfig = {
|
|
5274
|
+
cwd: ctx.workingDirectory,
|
|
5275
|
+
agentDir,
|
|
5276
|
+
sessionDir,
|
|
5277
|
+
sessionId: ctx.config.sessionId || null,
|
|
5278
|
+
standingPrompt: ctx.standingPrompt,
|
|
5279
|
+
prompt: turnPrompt,
|
|
5280
|
+
model: ctx.config.model && ctx.config.model !== "default" ? ctx.config.model : null
|
|
5281
|
+
};
|
|
5282
|
+
writeFileSync7(runnerConfigPath, `${JSON.stringify(runnerConfig)}
|
|
5283
|
+
`, { encoding: "utf8", mode: 384 });
|
|
5284
|
+
const args = [
|
|
5285
|
+
...buildPiSdkNodeArgs(runnerPath),
|
|
5286
|
+
"--config",
|
|
5287
|
+
runnerConfigPath
|
|
5288
|
+
];
|
|
5289
|
+
return {
|
|
5290
|
+
command,
|
|
5291
|
+
args,
|
|
5292
|
+
env: slock.spawnEnv,
|
|
5293
|
+
sessionDir,
|
|
5294
|
+
agentDir,
|
|
5295
|
+
runnerConfigPath,
|
|
5296
|
+
sdkVersion: PI_SDK_VERSION
|
|
5297
|
+
};
|
|
5298
|
+
}
|
|
5299
|
+
function isSystemFirstMessageTask2(message) {
|
|
5300
|
+
return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
|
|
5301
|
+
}
|
|
5302
|
+
function buildPiSystemPrompt(config) {
|
|
5303
|
+
return buildCliTransportSystemPrompt(config, {
|
|
5304
|
+
toolPrefix: CHAT_MCP_TOOL_PREFIX2,
|
|
5305
|
+
extraCriticalRules: [
|
|
5306
|
+
"- Runtime Profile migration controls are not available in the Pi runtime yet. If asked to acknowledge a runtime migration, explain the blocker instead of inventing a command."
|
|
5307
|
+
],
|
|
5308
|
+
postStartupNotes: [
|
|
5309
|
+
"**Pi runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
5310
|
+
],
|
|
5311
|
+
includeStdinNotificationSection: false,
|
|
5312
|
+
messageNotificationStyle: "poll"
|
|
5313
|
+
});
|
|
5314
|
+
}
|
|
5315
|
+
function contentText(content) {
|
|
5316
|
+
if (!content) return "";
|
|
5317
|
+
const chunks = [];
|
|
5318
|
+
for (const item of content) {
|
|
5319
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
5320
|
+
chunks.push(item.text);
|
|
5321
|
+
}
|
|
5322
|
+
}
|
|
5323
|
+
return chunks.join("");
|
|
5324
|
+
}
|
|
5325
|
+
function apiKeyErrorMessage(line) {
|
|
5326
|
+
const trimmed = line.trim();
|
|
5327
|
+
if (!trimmed) return null;
|
|
5328
|
+
if (/no api key found/i.test(trimmed)) return trimmed;
|
|
5329
|
+
if (/api key.+required/i.test(trimmed)) return trimmed;
|
|
5330
|
+
if (/no models available/i.test(trimmed)) return trimmed;
|
|
5331
|
+
return null;
|
|
5332
|
+
}
|
|
5333
|
+
var PiDriver = class {
|
|
5334
|
+
id = "pi";
|
|
5335
|
+
lifecycle = {
|
|
5336
|
+
kind: "per_turn",
|
|
5337
|
+
start: "defer_until_concrete_message",
|
|
5338
|
+
exit: "terminate_on_turn_end",
|
|
5339
|
+
inFlightWake: "coalesce_into_pending"
|
|
5340
|
+
};
|
|
5341
|
+
communication = {
|
|
5342
|
+
chat: "slock_cli",
|
|
5343
|
+
runtimeControl: "none"
|
|
5344
|
+
};
|
|
5345
|
+
session = {
|
|
5346
|
+
recovery: "resume_or_fresh"
|
|
5347
|
+
};
|
|
5348
|
+
model = {
|
|
5349
|
+
detectedModelsVerifiedAs: "launchable",
|
|
5350
|
+
toLaunchSpec: async (modelId, ctx) => {
|
|
5351
|
+
if (!ctx) return modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] };
|
|
5352
|
+
const launchCtx = {
|
|
5353
|
+
...ctx,
|
|
5354
|
+
config: {
|
|
5355
|
+
...ctx.config,
|
|
5356
|
+
model: modelId
|
|
5357
|
+
}
|
|
5358
|
+
};
|
|
5359
|
+
const launch = await buildPiLaunchOptions(launchCtx);
|
|
5360
|
+
return {
|
|
5361
|
+
args: launch.args,
|
|
5362
|
+
env: launch.env,
|
|
5363
|
+
configFiles: [launch.runnerConfigPath],
|
|
5364
|
+
params: {
|
|
5365
|
+
agentDir: launch.agentDir,
|
|
5366
|
+
sessionDir: launch.sessionDir,
|
|
5367
|
+
sdkVersion: launch.sdkVersion,
|
|
5368
|
+
resources: "extensions/skills/prompt-templates/themes/context-files disabled by Slock policy"
|
|
5369
|
+
}
|
|
5370
|
+
};
|
|
5371
|
+
}
|
|
5372
|
+
};
|
|
5373
|
+
supportsStdinNotification = false;
|
|
5374
|
+
mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
|
|
5375
|
+
busyDeliveryMode = "none";
|
|
5376
|
+
terminateProcessOnTurnEnd = true;
|
|
5377
|
+
deferSpawnUntilMessage = true;
|
|
5378
|
+
usesSlockCliForCommunication = true;
|
|
5379
|
+
sessionId = null;
|
|
5380
|
+
sessionAnnounced = false;
|
|
5381
|
+
apiKeyErrorAnnounced = false;
|
|
5382
|
+
turnEnded = false;
|
|
5383
|
+
assistantTextByMessageId = /* @__PURE__ */ new Map();
|
|
5384
|
+
shouldDeferWakeMessage(message) {
|
|
5385
|
+
return isSystemFirstMessageTask2(message);
|
|
5386
|
+
}
|
|
5387
|
+
probe() {
|
|
5388
|
+
return probePi();
|
|
5389
|
+
}
|
|
5390
|
+
async detectModels() {
|
|
5391
|
+
return null;
|
|
5392
|
+
}
|
|
5393
|
+
async spawn(ctx) {
|
|
5394
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
5395
|
+
this.sessionAnnounced = false;
|
|
5396
|
+
this.apiKeyErrorAnnounced = false;
|
|
5397
|
+
this.turnEnded = false;
|
|
5398
|
+
this.assistantTextByMessageId.clear();
|
|
5399
|
+
const unsupportedMessage = unsupportedPiVersionMessage(PI_SDK_VERSION);
|
|
5400
|
+
if (unsupportedMessage) throw new Error(unsupportedMessage);
|
|
5401
|
+
const launch = await buildPiLaunchOptions(ctx);
|
|
5402
|
+
const proc = spawn9(launch.command, launch.args, {
|
|
5403
|
+
cwd: ctx.workingDirectory,
|
|
5404
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5405
|
+
env: launch.env,
|
|
5406
|
+
shell: false
|
|
5407
|
+
});
|
|
5408
|
+
proc.stdin?.end();
|
|
5409
|
+
return { process: proc };
|
|
5410
|
+
}
|
|
5411
|
+
parseLine(line) {
|
|
5412
|
+
let event;
|
|
5413
|
+
try {
|
|
5414
|
+
event = JSON.parse(line);
|
|
5415
|
+
} catch {
|
|
5416
|
+
if (this.apiKeyErrorAnnounced) return [];
|
|
5417
|
+
const message = apiKeyErrorMessage(line);
|
|
5418
|
+
if (!message) return [];
|
|
5419
|
+
this.apiKeyErrorAnnounced = true;
|
|
5420
|
+
this.turnEnded = true;
|
|
5421
|
+
return [
|
|
5422
|
+
{ kind: "error", message },
|
|
5423
|
+
{ kind: "turn_end", sessionId: this.sessionId || void 0 }
|
|
5424
|
+
];
|
|
5425
|
+
}
|
|
5426
|
+
const events = [];
|
|
5427
|
+
if (event.type === "session" && event.id) {
|
|
5428
|
+
this.sessionId = event.id;
|
|
5429
|
+
}
|
|
5430
|
+
if (!this.sessionAnnounced && this.sessionId) {
|
|
5431
|
+
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
5432
|
+
this.sessionAnnounced = true;
|
|
5433
|
+
}
|
|
5434
|
+
switch (event.type) {
|
|
5435
|
+
case "agent_start":
|
|
5436
|
+
case "turn_start":
|
|
5437
|
+
events.push({ kind: "thinking", text: "" });
|
|
5438
|
+
break;
|
|
5439
|
+
case "message_update":
|
|
5440
|
+
case "message_end":
|
|
5441
|
+
if (event.message?.role === "assistant") {
|
|
5442
|
+
const key = event.message.id || "current";
|
|
5443
|
+
const currentText = contentText(event.message.content);
|
|
5444
|
+
const previousText = this.assistantTextByMessageId.get(key) ?? "";
|
|
5445
|
+
if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
|
|
5446
|
+
events.push({ kind: "text", text: currentText.slice(previousText.length) });
|
|
5447
|
+
} else if (currentText && currentText !== previousText) {
|
|
5448
|
+
events.push({ kind: "text", text: currentText });
|
|
5449
|
+
}
|
|
5450
|
+
this.assistantTextByMessageId.set(key, currentText);
|
|
5451
|
+
if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
|
|
5452
|
+
events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
|
|
5453
|
+
}
|
|
5454
|
+
}
|
|
5455
|
+
break;
|
|
5456
|
+
case "tool_execution_start":
|
|
5457
|
+
events.push({
|
|
5458
|
+
kind: "tool_call",
|
|
5459
|
+
name: event.toolName || "unknown_tool",
|
|
5460
|
+
input: event.args
|
|
5461
|
+
});
|
|
5462
|
+
break;
|
|
5463
|
+
case "tool_execution_end":
|
|
5464
|
+
events.push({
|
|
5465
|
+
kind: "tool_output",
|
|
5466
|
+
name: event.toolName || "unknown_tool"
|
|
5467
|
+
});
|
|
5468
|
+
if (event.isError) {
|
|
5469
|
+
events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
|
|
5470
|
+
}
|
|
5471
|
+
break;
|
|
5472
|
+
case "compaction_start":
|
|
5473
|
+
events.push({ kind: "compaction_started" });
|
|
5474
|
+
break;
|
|
5475
|
+
case "compaction_end":
|
|
5476
|
+
events.push({ kind: "compaction_finished" });
|
|
5477
|
+
if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
|
|
5478
|
+
break;
|
|
5479
|
+
case "turn_end":
|
|
5480
|
+
case "agent_end":
|
|
5481
|
+
if (!this.turnEnded) {
|
|
5482
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
5483
|
+
this.turnEnded = true;
|
|
5484
|
+
}
|
|
5485
|
+
break;
|
|
5486
|
+
}
|
|
5487
|
+
return events;
|
|
5488
|
+
}
|
|
5489
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
5490
|
+
return null;
|
|
5491
|
+
}
|
|
5492
|
+
buildSystemPrompt(config, _agentId) {
|
|
5493
|
+
return buildPiSystemPrompt(config);
|
|
5494
|
+
}
|
|
5495
|
+
};
|
|
5496
|
+
|
|
4781
5497
|
// src/drivers/index.ts
|
|
4782
5498
|
var driverFactories = {
|
|
4783
5499
|
claude: () => new ClaudeDriver(),
|
|
@@ -4787,7 +5503,8 @@ var driverFactories = {
|
|
|
4787
5503
|
cursor: () => new CursorDriver(),
|
|
4788
5504
|
gemini: () => new GeminiDriver(),
|
|
4789
5505
|
kimi: () => new KimiDriver(),
|
|
4790
|
-
opencode: () => new OpenCodeDriver()
|
|
5506
|
+
opencode: () => new OpenCodeDriver(),
|
|
5507
|
+
pi: () => new PiDriver()
|
|
4791
5508
|
};
|
|
4792
5509
|
function getDriver(runtimeId) {
|
|
4793
5510
|
const createDriver = driverFactories[runtimeId];
|
|
@@ -4800,7 +5517,7 @@ function getDriver(runtimeId) {
|
|
|
4800
5517
|
|
|
4801
5518
|
// src/workspaces.ts
|
|
4802
5519
|
import { readdir, rm, stat } from "fs/promises";
|
|
4803
|
-
import
|
|
5520
|
+
import path12 from "path";
|
|
4804
5521
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
4805
5522
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
4806
5523
|
}
|
|
@@ -4808,7 +5525,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
4808
5525
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
4809
5526
|
return null;
|
|
4810
5527
|
}
|
|
4811
|
-
return
|
|
5528
|
+
return path12.join(dataDir, directoryName);
|
|
4812
5529
|
}
|
|
4813
5530
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
4814
5531
|
return {
|
|
@@ -4857,7 +5574,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
4857
5574
|
return summary;
|
|
4858
5575
|
}
|
|
4859
5576
|
const childSummaries = await Promise.all(
|
|
4860
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
5577
|
+
entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
|
|
4861
5578
|
);
|
|
4862
5579
|
for (const childSummary of childSummaries) {
|
|
4863
5580
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -4876,7 +5593,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
4876
5593
|
if (!entry.isDirectory()) {
|
|
4877
5594
|
return null;
|
|
4878
5595
|
}
|
|
4879
|
-
const dirPath =
|
|
5596
|
+
const dirPath = path12.join(dataDir, entry.name);
|
|
4880
5597
|
try {
|
|
4881
5598
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
4882
5599
|
return {
|
|
@@ -5079,6 +5796,87 @@ function redactUrlQuery(value) {
|
|
|
5079
5796
|
}
|
|
5080
5797
|
}
|
|
5081
5798
|
|
|
5799
|
+
// src/runtimeProgressState.ts
|
|
5800
|
+
var RuntimeProgressState = class {
|
|
5801
|
+
lastEventAtMs;
|
|
5802
|
+
lastEventKindValue = null;
|
|
5803
|
+
staleSinceMs = null;
|
|
5804
|
+
constructor(nowMs = Date.now()) {
|
|
5805
|
+
this.lastEventAtMs = nowMs;
|
|
5806
|
+
}
|
|
5807
|
+
get lastEventAt() {
|
|
5808
|
+
return this.lastEventAtMs;
|
|
5809
|
+
}
|
|
5810
|
+
get lastEventKind() {
|
|
5811
|
+
return this.lastEventKindValue;
|
|
5812
|
+
}
|
|
5813
|
+
get staleSince() {
|
|
5814
|
+
return this.staleSinceMs;
|
|
5815
|
+
}
|
|
5816
|
+
get isStale() {
|
|
5817
|
+
return this.staleSinceMs !== null;
|
|
5818
|
+
}
|
|
5819
|
+
ageMs(nowMs = Date.now()) {
|
|
5820
|
+
return nowMs - this.lastEventAtMs;
|
|
5821
|
+
}
|
|
5822
|
+
noteRuntimeEvent(eventKind, nowMs = Date.now()) {
|
|
5823
|
+
this.lastEventAtMs = nowMs;
|
|
5824
|
+
this.lastEventKindValue = eventKind ?? null;
|
|
5825
|
+
this.staleSinceMs = null;
|
|
5826
|
+
}
|
|
5827
|
+
noteInternalProgress(observedAtMs = Date.now()) {
|
|
5828
|
+
this.lastEventAtMs = observedAtMs;
|
|
5829
|
+
this.staleSinceMs = null;
|
|
5830
|
+
}
|
|
5831
|
+
markStale(nowMs = Date.now()) {
|
|
5832
|
+
this.staleSinceMs ??= nowMs;
|
|
5833
|
+
return this.staleSinceMs;
|
|
5834
|
+
}
|
|
5835
|
+
};
|
|
5836
|
+
|
|
5837
|
+
// src/runtimeNotificationState.ts
|
|
5838
|
+
var RuntimeNotificationState = class {
|
|
5839
|
+
timerValue = null;
|
|
5840
|
+
pendingCountValue = 0;
|
|
5841
|
+
get pendingCount() {
|
|
5842
|
+
return this.pendingCountValue;
|
|
5843
|
+
}
|
|
5844
|
+
get timer() {
|
|
5845
|
+
return this.timerValue;
|
|
5846
|
+
}
|
|
5847
|
+
get hasTimer() {
|
|
5848
|
+
return this.timerValue !== null;
|
|
5849
|
+
}
|
|
5850
|
+
add(count = 1) {
|
|
5851
|
+
this.pendingCountValue += count;
|
|
5852
|
+
return this.pendingCountValue;
|
|
5853
|
+
}
|
|
5854
|
+
clearPending() {
|
|
5855
|
+
this.pendingCountValue = 0;
|
|
5856
|
+
}
|
|
5857
|
+
clearTimer() {
|
|
5858
|
+
if (this.timerValue) {
|
|
5859
|
+
clearTimeout(this.timerValue);
|
|
5860
|
+
this.timerValue = null;
|
|
5861
|
+
}
|
|
5862
|
+
}
|
|
5863
|
+
clear() {
|
|
5864
|
+
this.clearPending();
|
|
5865
|
+
this.clearTimer();
|
|
5866
|
+
}
|
|
5867
|
+
schedule(callback, delayMs) {
|
|
5868
|
+
if (this.timerValue) return false;
|
|
5869
|
+
this.timerValue = setTimeout(callback, delayMs);
|
|
5870
|
+
return true;
|
|
5871
|
+
}
|
|
5872
|
+
takePendingAndClearTimer() {
|
|
5873
|
+
const count = this.pendingCountValue;
|
|
5874
|
+
this.pendingCountValue = 0;
|
|
5875
|
+
this.clearTimer();
|
|
5876
|
+
return count;
|
|
5877
|
+
}
|
|
5878
|
+
};
|
|
5879
|
+
|
|
5082
5880
|
// src/agentProcessManager.ts
|
|
5083
5881
|
var DEFAULT_MAX_CONCURRENT_AGENT_STARTS = 5;
|
|
5084
5882
|
var DEFAULT_AGENT_START_INTERVAL_MS = 500;
|
|
@@ -5225,19 +6023,19 @@ function findSessionJsonl(root, predicate) {
|
|
|
5225
6023
|
if (depth < 0 || visited >= maxEntries) return null;
|
|
5226
6024
|
let entries;
|
|
5227
6025
|
try {
|
|
5228
|
-
entries =
|
|
6026
|
+
entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
|
|
5229
6027
|
} catch {
|
|
5230
6028
|
return null;
|
|
5231
6029
|
}
|
|
5232
6030
|
for (const entry of entries) {
|
|
5233
6031
|
if (++visited > maxEntries) return null;
|
|
5234
6032
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
5235
|
-
return
|
|
6033
|
+
return path13.join(dir, entry.name);
|
|
5236
6034
|
}
|
|
5237
6035
|
for (const entry of entries) {
|
|
5238
6036
|
if (++visited > maxEntries) return null;
|
|
5239
6037
|
if (!entry.isDirectory()) continue;
|
|
5240
|
-
const found = visit(
|
|
6038
|
+
const found = visit(path13.join(dir, entry.name), depth - 1);
|
|
5241
6039
|
if (found) return found;
|
|
5242
6040
|
}
|
|
5243
6041
|
return null;
|
|
@@ -5250,10 +6048,10 @@ function safeSessionFilename(value) {
|
|
|
5250
6048
|
}
|
|
5251
6049
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
5252
6050
|
try {
|
|
5253
|
-
const dir =
|
|
5254
|
-
|
|
5255
|
-
const filePath =
|
|
5256
|
-
|
|
6051
|
+
const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
|
|
6052
|
+
mkdirSync5(dir, { recursive: true });
|
|
6053
|
+
const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
6054
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
5257
6055
|
type: "runtime_session_handoff",
|
|
5258
6056
|
runtime,
|
|
5259
6057
|
sessionId,
|
|
@@ -5271,17 +6069,17 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
5271
6069
|
return null;
|
|
5272
6070
|
}
|
|
5273
6071
|
}
|
|
5274
|
-
function resolveRuntimeSessionRef(runtime, sessionId, homeDir =
|
|
5275
|
-
const directPath =
|
|
6072
|
+
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
|
|
6073
|
+
const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
|
|
5276
6074
|
if (directPath) {
|
|
5277
6075
|
try {
|
|
5278
|
-
if (
|
|
6076
|
+
if (statSync(directPath).isFile()) {
|
|
5279
6077
|
return { label: sessionId, path: directPath, runtime, reachable: true };
|
|
5280
6078
|
}
|
|
5281
6079
|
} catch {
|
|
5282
6080
|
}
|
|
5283
6081
|
}
|
|
5284
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
6082
|
+
const resolvedPath = runtime === "claude" ? findSessionJsonl(path13.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path13.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
|
|
5285
6083
|
if (!resolvedPath && fallbackDir) {
|
|
5286
6084
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
5287
6085
|
if (fallback) return fallback;
|
|
@@ -5297,6 +6095,36 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), f
|
|
|
5297
6095
|
}
|
|
5298
6096
|
return ref;
|
|
5299
6097
|
}
|
|
6098
|
+
function classifySpawnFailure(error) {
|
|
6099
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
6100
|
+
const lower = detail.toLowerCase();
|
|
6101
|
+
if (lower.includes("agent credential proxy") && lower.includes("failed to bind")) {
|
|
6102
|
+
return {
|
|
6103
|
+
reason: "agent_proxy_bind_failed",
|
|
6104
|
+
detail,
|
|
6105
|
+
userMessage: "Local agent proxy could not start. Check if another daemon or service is using the required local port."
|
|
6106
|
+
};
|
|
6107
|
+
}
|
|
6108
|
+
if (lower.includes("runner_credential_mint") || error instanceof RunnerCredentialMintError) {
|
|
6109
|
+
return {
|
|
6110
|
+
reason: "runner_credential_mint_failed",
|
|
6111
|
+
detail,
|
|
6112
|
+
userMessage: "Runner credential mint failed. Ensure the server is deployed and the daemon binary is compatible."
|
|
6113
|
+
};
|
|
6114
|
+
}
|
|
6115
|
+
if (lower.includes("enoent") || lower.includes("cannot resolve") || lower.includes("not found")) {
|
|
6116
|
+
return {
|
|
6117
|
+
reason: "runtime_not_found",
|
|
6118
|
+
detail,
|
|
6119
|
+
userMessage: "Runtime executable not found. Ensure the required CLI is installed and available on PATH."
|
|
6120
|
+
};
|
|
6121
|
+
}
|
|
6122
|
+
return {
|
|
6123
|
+
reason: "runtime_spawn_failed",
|
|
6124
|
+
detail,
|
|
6125
|
+
userMessage: `Start failed: ${detail}`
|
|
6126
|
+
};
|
|
6127
|
+
}
|
|
5300
6128
|
function formatSenderHandle(message) {
|
|
5301
6129
|
return message.sender_description ? `@${message.sender_name} \u2014 ${message.sender_description}` : `@${message.sender_name}`;
|
|
5302
6130
|
}
|
|
@@ -5945,26 +6773,29 @@ function hasDirectStdinRecoveryEvidence(ap) {
|
|
|
5945
6773
|
(text) => /write_stdin failed|stdin is closed|closed for this session|session.*closed/i.test(text)
|
|
5946
6774
|
);
|
|
5947
6775
|
}
|
|
5948
|
-
function
|
|
5949
|
-
if (!ap.sessionId) return
|
|
6776
|
+
function resumeSessionRecoveryReason(ap) {
|
|
6777
|
+
if (!ap.sessionId) return null;
|
|
5950
6778
|
const candidates = [
|
|
5951
6779
|
ap.lastRuntimeError,
|
|
5952
6780
|
...ap.recentStderr
|
|
5953
6781
|
].filter((value) => !!value);
|
|
5954
6782
|
if (ap.driver.id === "claude") {
|
|
5955
|
-
return candidates.some((text) => /No conversation found with session ID/i.test(text));
|
|
6783
|
+
return candidates.some((text) => /No conversation found with session ID/i.test(text)) ? "missing" : null;
|
|
5956
6784
|
}
|
|
5957
6785
|
if (ap.driver.id === "opencode") {
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
6786
|
+
if (candidates.some((text) => /Session not found/i.test(text) && text.includes(ap.sessionId))) return "missing";
|
|
6787
|
+
if (candidates.some(isOpenCodeReplayRejectedByProvider)) return "provider_replay_rejected";
|
|
6788
|
+
return null;
|
|
5961
6789
|
}
|
|
5962
6790
|
if (ap.driver.id === "gemini") {
|
|
5963
6791
|
return candidates.some(
|
|
5964
6792
|
(text) => /Error resuming session:\s*Invalid session identifier/i.test(text) && text.includes(ap.sessionId)
|
|
5965
|
-
);
|
|
6793
|
+
) ? "missing" : null;
|
|
5966
6794
|
}
|
|
5967
|
-
return
|
|
6795
|
+
return null;
|
|
6796
|
+
}
|
|
6797
|
+
function isOpenCodeReplayRejectedByProvider(text) {
|
|
6798
|
+
return /Invalid request:\s*the message at position \d+ with role ['"]?assistant['"]? must not be empty/i.test(text);
|
|
5968
6799
|
}
|
|
5969
6800
|
function resumeSessionRuntimeLabel(runtimeId) {
|
|
5970
6801
|
if (runtimeId === "opencode") return "OpenCode";
|
|
@@ -6021,7 +6852,7 @@ function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
|
|
|
6021
6852
|
launchId: ap.launchId || void 0,
|
|
6022
6853
|
sessionIdPresent: Boolean(ap.sessionId),
|
|
6023
6854
|
inboxCount: ap.inbox.length,
|
|
6024
|
-
pendingNotificationCount: ap.
|
|
6855
|
+
pendingNotificationCount: ap.notifications.pendingCount,
|
|
6025
6856
|
processPidPresent: typeof ap.process.pid === "number",
|
|
6026
6857
|
busyDeliveryMode: ap.driver.busyDeliveryMode,
|
|
6027
6858
|
supportsStdinNotification: ap.driver.supportsStdinNotification,
|
|
@@ -6035,7 +6866,7 @@ function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
|
|
|
6035
6866
|
};
|
|
6036
6867
|
}
|
|
6037
6868
|
function classifyRuntimeStallReason(ap) {
|
|
6038
|
-
if (ap.
|
|
6869
|
+
if (ap.runtimeProgress.lastEventKind === "tool_output" && ap.gatedSteering.outstandingToolUses === 0) {
|
|
6039
6870
|
return "harness_post_tool_silent_wedge";
|
|
6040
6871
|
}
|
|
6041
6872
|
return "no_runtime_events";
|
|
@@ -6196,7 +7027,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6196
7027
|
this.daemonApiKey = daemonApiKey;
|
|
6197
7028
|
this.serverUrl = opts.serverUrl;
|
|
6198
7029
|
this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
|
|
6199
|
-
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir ||
|
|
7030
|
+
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os5.homedir();
|
|
6200
7031
|
this.driverResolver = opts.driverResolver || getDriver;
|
|
6201
7032
|
this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
|
|
6202
7033
|
this.tracer = opts.tracer ?? noopTracer;
|
|
@@ -6608,7 +7439,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6608
7439
|
);
|
|
6609
7440
|
wakeMessage = void 0;
|
|
6610
7441
|
}
|
|
6611
|
-
const agentDataDir =
|
|
7442
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
6612
7443
|
await mkdir(agentDataDir, { recursive: true });
|
|
6613
7444
|
let runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
6614
7445
|
const legacyRuntimeProfileControl = runtimeConfig.runtimeProfileControl?.kind === "migration" ? runtimeConfig.runtimeProfileControl : null;
|
|
@@ -6622,23 +7453,23 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
6622
7453
|
);
|
|
6623
7454
|
runtimeConfig = { ...runtimeConfig, runtimeProfileControl: null };
|
|
6624
7455
|
}
|
|
6625
|
-
const memoryMdPath =
|
|
7456
|
+
const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
|
|
6626
7457
|
try {
|
|
6627
7458
|
await access(memoryMdPath);
|
|
6628
7459
|
} catch {
|
|
6629
7460
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
6630
7461
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
6631
7462
|
}
|
|
6632
|
-
const notesDir =
|
|
7463
|
+
const notesDir = path13.join(agentDataDir, "notes");
|
|
6633
7464
|
await mkdir(notesDir, { recursive: true });
|
|
6634
7465
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
6635
7466
|
const seedFiles = buildOnboardingSeedFiles();
|
|
6636
7467
|
for (const { relativePath, content } of seedFiles) {
|
|
6637
|
-
const fullPath =
|
|
7468
|
+
const fullPath = path13.join(agentDataDir, relativePath);
|
|
6638
7469
|
try {
|
|
6639
7470
|
await access(fullPath);
|
|
6640
7471
|
} catch {
|
|
6641
|
-
await mkdir(
|
|
7472
|
+
await mkdir(path13.dirname(fullPath), { recursive: true });
|
|
6642
7473
|
await writeFile(fullPath, content);
|
|
6643
7474
|
}
|
|
6644
7475
|
}
|
|
@@ -6766,16 +7597,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6766
7597
|
startupUnreadSummary: unreadSummary,
|
|
6767
7598
|
startupResumePrompt: resumePrompt,
|
|
6768
7599
|
isIdle: false,
|
|
6769
|
-
|
|
6770
|
-
pendingNotificationCount: 0,
|
|
7600
|
+
notifications: new RuntimeNotificationState(),
|
|
6771
7601
|
activityHeartbeat: null,
|
|
6772
7602
|
startupTimeoutTimer: null,
|
|
6773
7603
|
startupTimedOut: false,
|
|
6774
7604
|
compactionWatchdog: null,
|
|
6775
7605
|
compactionStartedAt: null,
|
|
6776
|
-
|
|
6777
|
-
lastRuntimeEventKind: null,
|
|
6778
|
-
runtimeProgressStaleSince: null,
|
|
7606
|
+
runtimeProgress: new RuntimeProgressState(Date.now()),
|
|
6779
7607
|
runtimeTraceSpan: null,
|
|
6780
7608
|
runtimeTraceCounters: createRuntimeTraceCounters(),
|
|
6781
7609
|
lastActivity: "",
|
|
@@ -6884,7 +7712,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6884
7712
|
clean_exit: code === 0,
|
|
6885
7713
|
runtime_trace_active: Boolean(current?.runtimeTraceSpan),
|
|
6886
7714
|
inbox_count: current?.inbox.length ?? 0,
|
|
6887
|
-
pending_notification_count: current?.
|
|
7715
|
+
pending_notification_count: current?.notifications.pendingCount ?? 0,
|
|
6888
7716
|
...this.processExitTraceAttrs.get(proc)
|
|
6889
7717
|
});
|
|
6890
7718
|
logger.info(`[Agent ${agentId}] Process exited with code ${code}${signal ? ` (signal ${signal})` : ""}`);
|
|
@@ -6893,9 +7721,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6893
7721
|
if (this.agents.has(agentId)) {
|
|
6894
7722
|
const ap = this.agents.get(agentId);
|
|
6895
7723
|
if (ap.process !== proc) return;
|
|
6896
|
-
|
|
6897
|
-
clearTimeout(ap.notificationTimer);
|
|
6898
|
-
}
|
|
7724
|
+
ap.notifications.clearTimer();
|
|
6899
7725
|
if (ap.pendingTrajectory?.timer) {
|
|
6900
7726
|
clearTimeout(ap.pendingTrajectory.timer);
|
|
6901
7727
|
}
|
|
@@ -6909,7 +7735,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6909
7735
|
const expectedTermination = Boolean(ap.expectedTerminationReason) || ap.startupTimedOut;
|
|
6910
7736
|
const processEndedCleanly = finalCode === 0 || expectedTermination && !ap.lastRuntimeError;
|
|
6911
7737
|
const terminalFailureDetail = processEndedCleanly ? null : classifyTerminalFailure(ap);
|
|
6912
|
-
const
|
|
7738
|
+
const resumeRecoveryReason = resumeSessionRecoveryReason(ap);
|
|
7739
|
+
const shouldColdStartResumeSession = resumeRecoveryReason !== null;
|
|
6913
7740
|
const summary = summarizeCrash(finalCode, finalSignal);
|
|
6914
7741
|
this.endRuntimeTrace(ap, processEndedCleanly ? "ok" : "error", {
|
|
6915
7742
|
outcome: processEndedCleanly ? "process-exit" : "process-crash",
|
|
@@ -6923,20 +7750,22 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6923
7750
|
cleanupAgentCredentialProxy(agentId, ap.launchId);
|
|
6924
7751
|
this.revokeManagedRunnerCredential(agentId, ap.config, ap.launchId);
|
|
6925
7752
|
this.agents.delete(agentId);
|
|
6926
|
-
if (
|
|
7753
|
+
if (shouldColdStartResumeSession) {
|
|
6927
7754
|
const staleSessionId = ap.sessionId;
|
|
6928
7755
|
const runtimeLabel = resumeSessionRuntimeLabel(ap.driver.id);
|
|
6929
7756
|
const restartConfig = { ...stripManagedRunnerCredential(ap.config), sessionId: null };
|
|
7757
|
+
const reasonText = resumeRecoveryReason === "provider_replay_rejected" ? "was rejected by the provider during replay" : "is unavailable locally";
|
|
7758
|
+
const activityText = resumeRecoveryReason === "provider_replay_rejected" ? `Stored ${runtimeLabel} session replay rejected; cold-starting a new session\u2026` : `Stored ${runtimeLabel} session missing; cold-starting a new session\u2026`;
|
|
6930
7759
|
logger.warn(
|
|
6931
|
-
`[Agent ${agentId}] Stored ${runtimeLabel} session ${staleSessionId}
|
|
7760
|
+
`[Agent ${agentId}] Stored ${runtimeLabel} session ${staleSessionId} ${reasonText}; falling back to cold start`
|
|
6932
7761
|
);
|
|
6933
7762
|
this.broadcastActivity(
|
|
6934
7763
|
agentId,
|
|
6935
7764
|
"working",
|
|
6936
|
-
|
|
7765
|
+
activityText,
|
|
6937
7766
|
[{
|
|
6938
7767
|
kind: "text",
|
|
6939
|
-
text: `Stored ${runtimeLabel} session ${staleSessionId}
|
|
7768
|
+
text: `Stored ${runtimeLabel} session ${staleSessionId} ${reasonText}. Falling back to a cold start; earlier runtime context may not be restored.`
|
|
6940
7769
|
}]
|
|
6941
7770
|
);
|
|
6942
7771
|
this.startAgent(
|
|
@@ -7082,7 +7911,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7082
7911
|
"X-Slock-Client": "daemon-server-session-worker"
|
|
7083
7912
|
},
|
|
7084
7913
|
body: JSON.stringify({
|
|
7085
|
-
scopes: ["send", "read", "mentions", "tasks", "reactions", "server", "channels"],
|
|
7914
|
+
scopes: ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"],
|
|
7086
7915
|
name: `runner:${config.runtime}:${agentId.slice(0, 8)}`
|
|
7087
7916
|
})
|
|
7088
7917
|
});
|
|
@@ -7205,15 +8034,15 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7205
8034
|
enqueueRuntimeProfileNotification(agentId, ap, message, kind, key) {
|
|
7206
8035
|
ap.inbox.push(message);
|
|
7207
8036
|
if (ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
7208
|
-
ap.
|
|
8037
|
+
ap.notifications.add();
|
|
7209
8038
|
if (ap.driver.busyDeliveryMode === "gated") {
|
|
7210
8039
|
this.recordGatedSteeringEvent(agentId, ap, "buffer", {
|
|
7211
8040
|
reason: "runtime_profile",
|
|
7212
8041
|
kind,
|
|
7213
8042
|
pendingMessages: ap.inbox.length
|
|
7214
8043
|
});
|
|
7215
|
-
} else if (!ap.
|
|
7216
|
-
ap.
|
|
8044
|
+
} else if (!ap.notifications.hasTimer) {
|
|
8045
|
+
ap.notifications.schedule(() => {
|
|
7217
8046
|
this.sendStdinNotification(agentId);
|
|
7218
8047
|
}, 3e3);
|
|
7219
8048
|
}
|
|
@@ -7228,7 +8057,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7228
8057
|
session_id_present: Boolean(ap.sessionId),
|
|
7229
8058
|
launchId: ap.launchId || void 0,
|
|
7230
8059
|
inbox_count: ap.inbox.length,
|
|
7231
|
-
pending_notification_count: ap.
|
|
8060
|
+
pending_notification_count: ap.notifications.pendingCount,
|
|
7232
8061
|
busy_delivery_mode: ap.driver.busyDeliveryMode,
|
|
7233
8062
|
supports_stdin_notification: ap.driver.supportsStdinNotification
|
|
7234
8063
|
});
|
|
@@ -7266,9 +8095,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7266
8095
|
}
|
|
7267
8096
|
return;
|
|
7268
8097
|
}
|
|
7269
|
-
|
|
7270
|
-
clearTimeout(ap.notificationTimer);
|
|
7271
|
-
}
|
|
8098
|
+
ap.notifications.clearTimer();
|
|
7272
8099
|
if (ap.activityHeartbeat) {
|
|
7273
8100
|
clearInterval(ap.activityHeartbeat);
|
|
7274
8101
|
}
|
|
@@ -7449,11 +8276,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7449
8276
|
return true;
|
|
7450
8277
|
}
|
|
7451
8278
|
if (ap.gatedSteering.compacting) {
|
|
7452
|
-
ap.
|
|
7453
|
-
|
|
7454
|
-
clearTimeout(ap.notificationTimer);
|
|
7455
|
-
ap.notificationTimer = null;
|
|
7456
|
-
}
|
|
8279
|
+
ap.notifications.add();
|
|
8280
|
+
ap.notifications.clearTimer();
|
|
7457
8281
|
if (ap.driver.busyDeliveryMode === "gated") {
|
|
7458
8282
|
this.recordGatedSteeringEvent(agentId, ap, "buffer", {
|
|
7459
8283
|
reason: "compaction_boundary",
|
|
@@ -7461,7 +8285,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7461
8285
|
});
|
|
7462
8286
|
}
|
|
7463
8287
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.delivery_buffered", {
|
|
7464
|
-
pendingNotificationCount: ap.
|
|
8288
|
+
pendingNotificationCount: ap.notifications.pendingCount,
|
|
7465
8289
|
pendingMessages: ap.inbox.length,
|
|
7466
8290
|
busyDeliveryMode: ap.driver.busyDeliveryMode
|
|
7467
8291
|
});
|
|
@@ -7473,16 +8297,16 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7473
8297
|
session_id_present: true,
|
|
7474
8298
|
launchId: ap.launchId || void 0,
|
|
7475
8299
|
inbox_count: ap.inbox.length,
|
|
7476
|
-
pending_notification_count: ap.
|
|
8300
|
+
pending_notification_count: ap.notifications.pendingCount,
|
|
7477
8301
|
busy_delivery_mode: ap.driver.busyDeliveryMode,
|
|
7478
8302
|
notification_timer_present: false
|
|
7479
8303
|
}));
|
|
7480
8304
|
return true;
|
|
7481
8305
|
}
|
|
7482
8306
|
if (ap.driver.busyDeliveryMode === "gated") {
|
|
7483
|
-
ap.
|
|
7484
|
-
if (!ap.
|
|
7485
|
-
ap.
|
|
8307
|
+
ap.notifications.add();
|
|
8308
|
+
if (!ap.notifications.hasTimer) {
|
|
8309
|
+
ap.notifications.schedule(() => {
|
|
7486
8310
|
this.sendStdinNotification(agentId);
|
|
7487
8311
|
}, 3e3);
|
|
7488
8312
|
}
|
|
@@ -7498,14 +8322,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7498
8322
|
session_id_present: true,
|
|
7499
8323
|
launchId: ap.launchId || void 0,
|
|
7500
8324
|
inbox_count: ap.inbox.length,
|
|
7501
|
-
pending_notification_count: ap.
|
|
7502
|
-
notification_timer_present:
|
|
8325
|
+
pending_notification_count: ap.notifications.pendingCount,
|
|
8326
|
+
notification_timer_present: ap.notifications.hasTimer
|
|
7503
8327
|
}));
|
|
7504
8328
|
return true;
|
|
7505
8329
|
}
|
|
7506
|
-
ap.
|
|
7507
|
-
if (!ap.
|
|
7508
|
-
ap.
|
|
8330
|
+
ap.notifications.add();
|
|
8331
|
+
if (!ap.notifications.hasTimer) {
|
|
8332
|
+
ap.notifications.schedule(() => {
|
|
7509
8333
|
this.sendStdinNotification(agentId);
|
|
7510
8334
|
}, 3e3);
|
|
7511
8335
|
}
|
|
@@ -7516,13 +8340,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7516
8340
|
runtime: ap.config.runtime,
|
|
7517
8341
|
session_id_present: true,
|
|
7518
8342
|
inbox_count: ap.inbox.length,
|
|
7519
|
-
pending_notification_count: ap.
|
|
7520
|
-
notification_timer_present:
|
|
8343
|
+
pending_notification_count: ap.notifications.pendingCount,
|
|
8344
|
+
notification_timer_present: ap.notifications.hasTimer
|
|
7521
8345
|
}));
|
|
7522
8346
|
return true;
|
|
7523
8347
|
}
|
|
7524
8348
|
async resetWorkspace(agentId) {
|
|
7525
|
-
const agentDataDir =
|
|
8349
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
7526
8350
|
try {
|
|
7527
8351
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
7528
8352
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -7583,7 +8407,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7583
8407
|
return result;
|
|
7584
8408
|
}
|
|
7585
8409
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
7586
|
-
const workspacePath =
|
|
8410
|
+
const workspacePath = path13.join(this.dataDir, agentId);
|
|
7587
8411
|
return {
|
|
7588
8412
|
agentId,
|
|
7589
8413
|
launchId,
|
|
@@ -7840,7 +8664,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7840
8664
|
}
|
|
7841
8665
|
// Workspace file browsing
|
|
7842
8666
|
async getFileTree(agentId, dirPath) {
|
|
7843
|
-
const agentDir =
|
|
8667
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
7844
8668
|
try {
|
|
7845
8669
|
await stat2(agentDir);
|
|
7846
8670
|
} catch {
|
|
@@ -7848,8 +8672,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7848
8672
|
}
|
|
7849
8673
|
let targetDir = agentDir;
|
|
7850
8674
|
if (dirPath) {
|
|
7851
|
-
const resolved =
|
|
7852
|
-
if (!resolved.startsWith(agentDir +
|
|
8675
|
+
const resolved = path13.resolve(agentDir, dirPath);
|
|
8676
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
7853
8677
|
return [];
|
|
7854
8678
|
}
|
|
7855
8679
|
targetDir = resolved;
|
|
@@ -7857,14 +8681,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7857
8681
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
7858
8682
|
}
|
|
7859
8683
|
async readFile(agentId, filePath) {
|
|
7860
|
-
const agentDir =
|
|
7861
|
-
const resolved =
|
|
7862
|
-
if (!resolved.startsWith(agentDir +
|
|
8684
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
8685
|
+
const resolved = path13.resolve(agentDir, filePath);
|
|
8686
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
7863
8687
|
throw new Error("Access denied");
|
|
7864
8688
|
}
|
|
7865
8689
|
const info = await stat2(resolved);
|
|
7866
8690
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
7867
|
-
const ext =
|
|
8691
|
+
const ext = path13.extname(resolved).toLowerCase();
|
|
7868
8692
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
7869
8693
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
7870
8694
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -7898,14 +8722,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7898
8722
|
async listSkills(agentId, runtimeHint) {
|
|
7899
8723
|
const agent = this.agents.get(agentId);
|
|
7900
8724
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
7901
|
-
const home =
|
|
7902
|
-
const workspaceDir =
|
|
8725
|
+
const home = os5.homedir();
|
|
8726
|
+
const workspaceDir = path13.join(this.dataDir, agentId);
|
|
7903
8727
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
7904
8728
|
const globalResults = await Promise.all(
|
|
7905
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
8729
|
+
paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
|
|
7906
8730
|
);
|
|
7907
8731
|
const workspaceResults = await Promise.all(
|
|
7908
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
8732
|
+
paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
|
|
7909
8733
|
);
|
|
7910
8734
|
const dedup = (skills) => {
|
|
7911
8735
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7934,7 +8758,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7934
8758
|
const skills = [];
|
|
7935
8759
|
for (const entry of entries) {
|
|
7936
8760
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
7937
|
-
const skillMd =
|
|
8761
|
+
const skillMd = path13.join(dir, entry.name, "SKILL.md");
|
|
7938
8762
|
try {
|
|
7939
8763
|
const content = await readFile(skillMd, "utf-8");
|
|
7940
8764
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -7945,7 +8769,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7945
8769
|
} else if (entry.name.endsWith(".md")) {
|
|
7946
8770
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
7947
8771
|
try {
|
|
7948
|
-
const content = await readFile(
|
|
8772
|
+
const content = await readFile(path13.join(dir, entry.name), "utf-8");
|
|
7949
8773
|
const skill = this.parseSkillMd(cmdName, content);
|
|
7950
8774
|
skill.sourcePath = dir;
|
|
7951
8775
|
skills.push(skill);
|
|
@@ -8312,40 +9136,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8312
9136
|
this.startRuntimeTrace(agentId, ap, "runtime-progress").addEvent(name, attrs);
|
|
8313
9137
|
}
|
|
8314
9138
|
noteRuntimeProgress(ap, eventKind) {
|
|
8315
|
-
ap.
|
|
8316
|
-
ap.lastRuntimeEventKind = eventKind ?? null;
|
|
8317
|
-
ap.runtimeProgressStaleSince = null;
|
|
8318
|
-
}
|
|
8319
|
-
observeRuntimeTranscriptProgress(agentId, ap, staleForMs, source) {
|
|
8320
|
-
if (ap.config.runtime !== "codex" || !ap.sessionId) return false;
|
|
8321
|
-
const ref = resolveRuntimeSessionRef(ap.config.runtime, ap.sessionId, this.runtimeSessionHomeDir);
|
|
8322
|
-
if (!ref.reachable || !ref.path) return false;
|
|
8323
|
-
let mtimeMs;
|
|
8324
|
-
try {
|
|
8325
|
-
const stats = statSync2(ref.path);
|
|
8326
|
-
if (!stats.isFile()) return false;
|
|
8327
|
-
mtimeMs = stats.mtimeMs;
|
|
8328
|
-
} catch {
|
|
8329
|
-
return false;
|
|
8330
|
-
}
|
|
8331
|
-
if (mtimeMs <= ap.lastRuntimeEventAt) return false;
|
|
8332
|
-
const now = Date.now();
|
|
8333
|
-
const transcriptAgeMs = Math.max(0, now - mtimeMs);
|
|
8334
|
-
if (transcriptAgeMs >= RUNTIME_PROGRESS_STALE_MS) return false;
|
|
8335
|
-
ap.lastRuntimeEventAt = mtimeMs;
|
|
8336
|
-
ap.runtimeProgressStaleSince = null;
|
|
8337
|
-
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.internal_observed", {
|
|
8338
|
-
turn_outcome: "held",
|
|
8339
|
-
turn_subtype: "runtime_progress",
|
|
8340
|
-
turn_reason: "internal_activity_observed",
|
|
8341
|
-
signal: "runtime_transcript_mtime",
|
|
8342
|
-
source,
|
|
8343
|
-
runtime: ap.config.runtime,
|
|
8344
|
-
sessionRefReachable: true,
|
|
8345
|
-
transcriptAgeMs,
|
|
8346
|
-
previousStaleForMs: staleForMs
|
|
8347
|
-
});
|
|
8348
|
-
return true;
|
|
9139
|
+
ap.runtimeProgress.noteRuntimeEvent(eventKind);
|
|
8349
9140
|
}
|
|
8350
9141
|
recordGatedSteeringEvent(agentId, ap, event, attrs = {}) {
|
|
8351
9142
|
if (ap.driver.busyDeliveryMode !== "gated") return;
|
|
@@ -8376,13 +9167,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8376
9167
|
return this.sendStdinNotification(agentId);
|
|
8377
9168
|
}
|
|
8378
9169
|
const pendingMessages = ap.inbox.length;
|
|
8379
|
-
const pendingNotificationCount = ap.
|
|
9170
|
+
const pendingNotificationCount = ap.notifications.pendingCount;
|
|
8380
9171
|
const nextMessages = ap.inbox.splice(0, ap.inbox.length);
|
|
8381
|
-
ap.
|
|
8382
|
-
if (ap.notificationTimer) {
|
|
8383
|
-
clearTimeout(ap.notificationTimer);
|
|
8384
|
-
ap.notificationTimer = null;
|
|
8385
|
-
}
|
|
9172
|
+
ap.notifications.clear();
|
|
8386
9173
|
ap.gatedSteering.lastFlushReason = reason;
|
|
8387
9174
|
if (reason !== "turn_end") {
|
|
8388
9175
|
ap.gatedSteering.inFlightBatch = {
|
|
@@ -8405,16 +9192,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8405
9192
|
if (reason !== "turn_end") {
|
|
8406
9193
|
ap.gatedSteering.inFlightBatch = null;
|
|
8407
9194
|
}
|
|
8408
|
-
ap.pendingNotificationCount
|
|
9195
|
+
ap.notifications.add(pendingNotificationCount || pendingMessages);
|
|
8409
9196
|
return false;
|
|
8410
9197
|
}
|
|
8411
9198
|
flushCompactionBoundaryMessages(agentId, ap) {
|
|
8412
9199
|
if (!ap.sessionId || !ap.driver.supportsStdinNotification || ap.inbox.length === 0) return false;
|
|
8413
|
-
if (ap.
|
|
8414
|
-
|
|
8415
|
-
clearTimeout(ap.notificationTimer);
|
|
8416
|
-
ap.notificationTimer = null;
|
|
8417
|
-
}
|
|
9200
|
+
if (ap.notifications.pendingCount > 0) {
|
|
9201
|
+
ap.notifications.clearTimer();
|
|
8418
9202
|
this.sendStdinNotification(agentId);
|
|
8419
9203
|
return true;
|
|
8420
9204
|
}
|
|
@@ -8442,7 +9226,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8442
9226
|
handleRuntimeStartupTimeout(agentId, ap, timeoutMs) {
|
|
8443
9227
|
const current = this.agents.get(agentId);
|
|
8444
9228
|
if (current !== ap) return;
|
|
8445
|
-
if (ap.
|
|
9229
|
+
if (ap.runtimeProgress.lastEventKind) {
|
|
8446
9230
|
this.clearRuntimeStartupTimeout(ap);
|
|
8447
9231
|
return;
|
|
8448
9232
|
}
|
|
@@ -8451,8 +9235,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8451
9235
|
const detail = terminalFailureDetail?.detail ?? formatRuntimeStartTimeoutMessage(ap.driver.id);
|
|
8452
9236
|
ap.startupTimedOut = true;
|
|
8453
9237
|
ap.lastRuntimeError = detail;
|
|
8454
|
-
ap.
|
|
8455
|
-
const staleForMs = Math.max(timeoutMs,
|
|
9238
|
+
ap.runtimeProgress.markStale();
|
|
9239
|
+
const staleForMs = Math.max(timeoutMs, ap.runtimeProgress.ageMs());
|
|
8456
9240
|
const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, Math.max(1, Math.floor(staleForMs / 6e4)));
|
|
8457
9241
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.start.timeout", {
|
|
8458
9242
|
turn_outcome: "failed",
|
|
@@ -8491,7 +9275,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8491
9275
|
const batch = ap.gatedSteering.inFlightBatch;
|
|
8492
9276
|
ap.gatedSteering.inFlightBatch = null;
|
|
8493
9277
|
ap.inbox.unshift(...batch.messages);
|
|
8494
|
-
ap.
|
|
9278
|
+
ap.notifications.add(batch.messages.length);
|
|
8495
9279
|
this.recordGatedSteeringEvent(agentId, ap, "requeue", {
|
|
8496
9280
|
reason,
|
|
8497
9281
|
originalFlushReason: batch.reason,
|
|
@@ -8503,11 +9287,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8503
9287
|
}
|
|
8504
9288
|
markRuntimeProgressStaleIfNeeded(agentId, ap) {
|
|
8505
9289
|
if (ap.lastActivity !== "working" && ap.lastActivity !== "thinking") return false;
|
|
8506
|
-
if (ap.
|
|
8507
|
-
const staleForMs =
|
|
9290
|
+
if (ap.runtimeProgress.isStale) return true;
|
|
9291
|
+
const staleForMs = ap.runtimeProgress.ageMs();
|
|
8508
9292
|
if (staleForMs < RUNTIME_PROGRESS_STALE_MS) return false;
|
|
8509
|
-
|
|
8510
|
-
ap.runtimeProgressStaleSince = Date.now();
|
|
9293
|
+
ap.runtimeProgress.markStale();
|
|
8511
9294
|
const staleForMinutes = Math.max(1, Math.floor(staleForMs / 6e4));
|
|
8512
9295
|
const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes);
|
|
8513
9296
|
const turnReason = classifyRuntimeStallReason(ap);
|
|
@@ -8540,11 +9323,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8540
9323
|
const canRestartDirectStdinProcess = directStdinRuntime && Boolean(ap.sessionId) && (ap.gatedSteering.outstandingToolUses === 0 || hasDirectStdinRecoveryEvidence(ap));
|
|
8541
9324
|
const canRestartStalledProcess = !ap.driver.supportsStdinNotification || canRestartDirectStdinProcess;
|
|
8542
9325
|
if (!canRestartStalledProcess) return false;
|
|
8543
|
-
const staleForMs =
|
|
8544
|
-
if (staleForMs < RUNTIME_PROGRESS_STALE_MS && !ap.
|
|
8545
|
-
if (this.observeRuntimeTranscriptProgress(agentId, ap, staleForMs, "queued_recovery")) return false;
|
|
9326
|
+
const staleForMs = ap.runtimeProgress.ageMs();
|
|
9327
|
+
if (staleForMs < RUNTIME_PROGRESS_STALE_MS && !ap.runtimeProgress.isStale) return false;
|
|
8546
9328
|
const staleForMinutes = Math.max(1, Math.floor(staleForMs / 6e4));
|
|
8547
|
-
ap.
|
|
9329
|
+
ap.runtimeProgress.markStale();
|
|
8548
9330
|
const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes);
|
|
8549
9331
|
const turnReason = classifyRuntimeStallReason(ap);
|
|
8550
9332
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.stalled", {
|
|
@@ -8593,14 +9375,38 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8593
9375
|
/** Handle a single ParsedEvent from any runtime driver */
|
|
8594
9376
|
handleParsedEvent(agentId, event, driver) {
|
|
8595
9377
|
const ap = this.agents.get(agentId);
|
|
9378
|
+
if (event.kind === "telemetry") {
|
|
9379
|
+
if (ap) this.recordRuntimeTelemetry(agentId, ap, event);
|
|
9380
|
+
return;
|
|
9381
|
+
}
|
|
8596
9382
|
if (ap) {
|
|
8597
|
-
const wasStalled =
|
|
9383
|
+
const wasStalled = ap.runtimeProgress.isStale;
|
|
8598
9384
|
this.clearRuntimeStartupTimeout(ap);
|
|
8599
9385
|
this.noteRuntimeTraceCounter(ap, event);
|
|
8600
|
-
|
|
9386
|
+
const eventAttrs = event.kind === "internal_progress" ? {
|
|
9387
|
+
kind: event.kind,
|
|
9388
|
+
source: event.source,
|
|
9389
|
+
itemType: event.itemType,
|
|
9390
|
+
payloadBytes: event.payloadBytes
|
|
9391
|
+
} : { kind: event.kind };
|
|
9392
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.event.received", eventAttrs);
|
|
8601
9393
|
if (wasStalled) {
|
|
8602
9394
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.observed", { afterStall: true });
|
|
8603
9395
|
}
|
|
9396
|
+
if (event.kind === "internal_progress") {
|
|
9397
|
+
ap.runtimeProgress.noteInternalProgress();
|
|
9398
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.internal_observed", {
|
|
9399
|
+
turn_outcome: "held",
|
|
9400
|
+
turn_subtype: "runtime_progress",
|
|
9401
|
+
turn_reason: "internal_activity_observed",
|
|
9402
|
+
signal: event.source,
|
|
9403
|
+
source: "runtime_event",
|
|
9404
|
+
runtime: ap.config.runtime,
|
|
9405
|
+
itemType: event.itemType,
|
|
9406
|
+
payloadBytes: event.payloadBytes
|
|
9407
|
+
});
|
|
9408
|
+
return;
|
|
9409
|
+
}
|
|
8604
9410
|
this.noteRuntimeProgress(ap, event.kind);
|
|
8605
9411
|
}
|
|
8606
9412
|
switch (event.kind) {
|
|
@@ -8704,11 +9510,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8704
9510
|
this.setGatedSteeringPhase(agentId, ap, "idle", { event: "turn_end" });
|
|
8705
9511
|
if (ap.inbox.length > 0 && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
8706
9512
|
const nextMessages = ap.inbox.splice(0, ap.inbox.length);
|
|
8707
|
-
ap.
|
|
8708
|
-
if (ap.notificationTimer) {
|
|
8709
|
-
clearTimeout(ap.notificationTimer);
|
|
8710
|
-
ap.notificationTimer = null;
|
|
8711
|
-
}
|
|
9513
|
+
ap.notifications.clear();
|
|
8712
9514
|
if (ap.driver.busyDeliveryMode === "gated") {
|
|
8713
9515
|
ap.gatedSteering.lastFlushReason = "turn_end";
|
|
8714
9516
|
this.recordGatedSteeringEvent(agentId, ap, "flush", {
|
|
@@ -8802,11 +9604,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8802
9604
|
}
|
|
8803
9605
|
} else {
|
|
8804
9606
|
ap.isIdle = true;
|
|
8805
|
-
ap.
|
|
8806
|
-
if (ap.notificationTimer) {
|
|
8807
|
-
clearTimeout(ap.notificationTimer);
|
|
8808
|
-
ap.notificationTimer = null;
|
|
8809
|
-
}
|
|
9607
|
+
ap.notifications.clear();
|
|
8810
9608
|
logger.info(`[Agent ${agentId}] Marked ${ap.driver.id} wakeable after terminal runtime error`);
|
|
8811
9609
|
}
|
|
8812
9610
|
}
|
|
@@ -8818,6 +9616,18 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8818
9616
|
}
|
|
8819
9617
|
}
|
|
8820
9618
|
}
|
|
9619
|
+
recordRuntimeTelemetry(agentId, ap, event) {
|
|
9620
|
+
const attrs = {
|
|
9621
|
+
agentId,
|
|
9622
|
+
launchId: ap.launchId || void 0,
|
|
9623
|
+
runtime: ap.config.runtime,
|
|
9624
|
+
model: ap.config.model,
|
|
9625
|
+
telemetry_name: event.name,
|
|
9626
|
+
...event.attrs
|
|
9627
|
+
};
|
|
9628
|
+
ap.runtimeTraceSpan?.addEvent(`runtime.telemetry.${event.name}`, event.attrs);
|
|
9629
|
+
this.recordDaemonTrace(`daemon.runtime.telemetry.${event.name}`, attrs);
|
|
9630
|
+
}
|
|
8821
9631
|
sendAgentStatus(agentId, status, launchId) {
|
|
8822
9632
|
this.sendToServer({ type: "agent:status", agentId, status, launchId: launchId || void 0 });
|
|
8823
9633
|
}
|
|
@@ -8871,12 +9681,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8871
9681
|
sendStdinNotification(agentId) {
|
|
8872
9682
|
const ap = this.agents.get(agentId);
|
|
8873
9683
|
if (!ap) return false;
|
|
8874
|
-
const count = ap.
|
|
8875
|
-
ap.pendingNotificationCount = 0;
|
|
8876
|
-
if (ap.notificationTimer) {
|
|
8877
|
-
clearTimeout(ap.notificationTimer);
|
|
8878
|
-
ap.notificationTimer = null;
|
|
8879
|
-
}
|
|
9684
|
+
const count = ap.notifications.takePendingAndClearTimer();
|
|
8880
9685
|
if (count === 0) return false;
|
|
8881
9686
|
if (ap.isIdle) return false;
|
|
8882
9687
|
if (!ap.sessionId) return false;
|
|
@@ -8886,7 +9691,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8886
9691
|
pendingMessages: ap.inbox.length,
|
|
8887
9692
|
busyDeliveryMode: ap.driver.busyDeliveryMode
|
|
8888
9693
|
});
|
|
8889
|
-
ap.
|
|
9694
|
+
ap.notifications.add(count);
|
|
8890
9695
|
logger.info(
|
|
8891
9696
|
`[Agent ${agentId}] Suppressing stdin delivery until context compaction finishes; pending=${ap.inbox.length}`
|
|
8892
9697
|
);
|
|
@@ -8912,7 +9717,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8912
9717
|
});
|
|
8913
9718
|
return true;
|
|
8914
9719
|
} else {
|
|
8915
|
-
ap.
|
|
9720
|
+
ap.notifications.add(count);
|
|
8916
9721
|
this.recordDaemonTrace("daemon.agent.stdin_notification", {
|
|
8917
9722
|
agentId,
|
|
8918
9723
|
runtime: ap.config.runtime,
|
|
@@ -8963,7 +9768,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8963
9768
|
messages_count: messages.length,
|
|
8964
9769
|
session_id_present: Boolean(ap.sessionId),
|
|
8965
9770
|
inbox_count: ap.inbox.length,
|
|
8966
|
-
pending_notification_count: ap.
|
|
9771
|
+
pending_notification_count: ap.notifications.pendingCount,
|
|
8967
9772
|
busy_delivery_mode: ap.driver.busyDeliveryMode,
|
|
8968
9773
|
supports_stdin_notification: ap.driver.supportsStdinNotification,
|
|
8969
9774
|
...this.messagesTraceAttrs(messages)
|
|
@@ -9038,8 +9843,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
9038
9843
|
const nodes = [];
|
|
9039
9844
|
for (const entry of entries) {
|
|
9040
9845
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
9041
|
-
const fullPath =
|
|
9042
|
-
const relativePath =
|
|
9846
|
+
const fullPath = path13.join(dir, entry.name);
|
|
9847
|
+
const relativePath = path13.relative(rootDir, fullPath);
|
|
9043
9848
|
let info;
|
|
9044
9849
|
try {
|
|
9045
9850
|
info = await stat2(fullPath);
|
|
@@ -9344,9 +10149,9 @@ var ReminderCache = class {
|
|
|
9344
10149
|
|
|
9345
10150
|
// src/machineLock.ts
|
|
9346
10151
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
9347
|
-
import { mkdirSync as
|
|
9348
|
-
import
|
|
9349
|
-
import
|
|
10152
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
10153
|
+
import os6 from "os";
|
|
10154
|
+
import path14 from "path";
|
|
9350
10155
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
9351
10156
|
var DaemonMachineLockConflictError = class extends Error {
|
|
9352
10157
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -9368,18 +10173,18 @@ function resolveDefaultMachineStateRoot() {
|
|
|
9368
10173
|
return resolveSlockHomePath("machines");
|
|
9369
10174
|
}
|
|
9370
10175
|
function ownerPath(lockDir) {
|
|
9371
|
-
return
|
|
10176
|
+
return path14.join(lockDir, "owner.json");
|
|
9372
10177
|
}
|
|
9373
10178
|
function readOwner(lockDir) {
|
|
9374
10179
|
try {
|
|
9375
|
-
return JSON.parse(
|
|
10180
|
+
return JSON.parse(readFileSync5(ownerPath(lockDir), "utf8"));
|
|
9376
10181
|
} catch {
|
|
9377
10182
|
return null;
|
|
9378
10183
|
}
|
|
9379
10184
|
}
|
|
9380
10185
|
function lockAgeMs(lockDir) {
|
|
9381
10186
|
try {
|
|
9382
|
-
return Date.now() -
|
|
10187
|
+
return Date.now() - statSync2(lockDir).mtimeMs;
|
|
9383
10188
|
} catch {
|
|
9384
10189
|
return null;
|
|
9385
10190
|
}
|
|
@@ -9398,23 +10203,23 @@ function acquireDaemonMachineLock(options) {
|
|
|
9398
10203
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
9399
10204
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
9400
10205
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
9401
|
-
const machineDir =
|
|
9402
|
-
const lockDir =
|
|
10206
|
+
const machineDir = path14.join(rootDir, lockId);
|
|
10207
|
+
const lockDir = path14.join(machineDir, "daemon.lock");
|
|
9403
10208
|
const token = randomUUID3();
|
|
9404
|
-
|
|
10209
|
+
mkdirSync6(machineDir, { recursive: true });
|
|
9405
10210
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
9406
10211
|
try {
|
|
9407
|
-
|
|
10212
|
+
mkdirSync6(lockDir);
|
|
9408
10213
|
const owner = {
|
|
9409
10214
|
pid: process.pid,
|
|
9410
10215
|
token,
|
|
9411
|
-
hostname:
|
|
10216
|
+
hostname: os6.hostname(),
|
|
9412
10217
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9413
10218
|
serverUrl: options.serverUrl,
|
|
9414
10219
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
9415
10220
|
};
|
|
9416
10221
|
try {
|
|
9417
|
-
|
|
10222
|
+
writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
9418
10223
|
`, { mode: 384 });
|
|
9419
10224
|
} catch (err) {
|
|
9420
10225
|
rmSync3(lockDir, { recursive: true, force: true });
|
|
@@ -9451,8 +10256,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
9451
10256
|
}
|
|
9452
10257
|
|
|
9453
10258
|
// src/localTraceSink.ts
|
|
9454
|
-
import { appendFileSync, mkdirSync as
|
|
9455
|
-
import
|
|
10259
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync10 } from "fs";
|
|
10260
|
+
import path15 from "path";
|
|
9456
10261
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
9457
10262
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
9458
10263
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -9488,7 +10293,7 @@ var LocalRotatingTraceSink = class {
|
|
|
9488
10293
|
currentSize = 0;
|
|
9489
10294
|
sequence = 0;
|
|
9490
10295
|
constructor(options) {
|
|
9491
|
-
this.traceDir =
|
|
10296
|
+
this.traceDir = path15.join(options.machineDir, "traces");
|
|
9492
10297
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
9493
10298
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
9494
10299
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -9514,26 +10319,26 @@ var LocalRotatingTraceSink = class {
|
|
|
9514
10319
|
return this.currentFile;
|
|
9515
10320
|
}
|
|
9516
10321
|
ensureFile(nextBytes) {
|
|
9517
|
-
|
|
10322
|
+
mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
|
|
9518
10323
|
const nowMs = this.nowMsProvider();
|
|
9519
10324
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
9520
10325
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
9521
|
-
this.currentFile =
|
|
10326
|
+
this.currentFile = path15.join(
|
|
9522
10327
|
this.traceDir,
|
|
9523
10328
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
9524
10329
|
);
|
|
9525
|
-
|
|
9526
|
-
this.currentSize =
|
|
10330
|
+
writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
|
|
10331
|
+
this.currentSize = statSync3(this.currentFile).size;
|
|
9527
10332
|
this.currentFileOpenedAtMs = nowMs;
|
|
9528
10333
|
this.pruneOldFiles();
|
|
9529
10334
|
}
|
|
9530
10335
|
}
|
|
9531
10336
|
pruneOldFiles() {
|
|
9532
|
-
const files =
|
|
10337
|
+
const files = readdirSync2(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
|
|
9533
10338
|
const excess = files.length - this.maxFiles;
|
|
9534
10339
|
if (excess <= 0) return;
|
|
9535
10340
|
for (const file of files.slice(0, excess)) {
|
|
9536
|
-
rmSync4(
|
|
10341
|
+
rmSync4(path15.join(this.traceDir, file), { force: true });
|
|
9537
10342
|
}
|
|
9538
10343
|
}
|
|
9539
10344
|
};
|
|
@@ -9620,11 +10425,11 @@ function isDiagnosticErrorAttr(key) {
|
|
|
9620
10425
|
import { createHash as createHash5, randomUUID as randomUUID4 } from "crypto";
|
|
9621
10426
|
import { gzipSync } from "zlib";
|
|
9622
10427
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
9623
|
-
import
|
|
10428
|
+
import path16 from "path";
|
|
9624
10429
|
|
|
9625
10430
|
// src/directUploadCapability.ts
|
|
9626
|
-
function joinUrl(base,
|
|
9627
|
-
return `${base.replace(/\/+$/, "")}${
|
|
10431
|
+
function joinUrl(base, path18) {
|
|
10432
|
+
return `${base.replace(/\/+$/, "")}${path18}`;
|
|
9628
10433
|
}
|
|
9629
10434
|
function jsonHeaders(apiKey) {
|
|
9630
10435
|
return {
|
|
@@ -9843,7 +10648,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
9843
10648
|
}, nextMs);
|
|
9844
10649
|
}
|
|
9845
10650
|
async findUploadCandidates() {
|
|
9846
|
-
const traceDir =
|
|
10651
|
+
const traceDir = path16.join(this.options.machineDir, "traces");
|
|
9847
10652
|
let names;
|
|
9848
10653
|
try {
|
|
9849
10654
|
names = await readdir3(traceDir);
|
|
@@ -9855,8 +10660,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
9855
10660
|
const currentFile = this.options.currentFileProvider?.();
|
|
9856
10661
|
const candidates = [];
|
|
9857
10662
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
9858
|
-
const file =
|
|
9859
|
-
if (currentFile &&
|
|
10663
|
+
const file = path16.join(traceDir, name);
|
|
10664
|
+
if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
|
|
9860
10665
|
if (await this.isUploaded(file)) continue;
|
|
9861
10666
|
try {
|
|
9862
10667
|
const info = await stat3(file);
|
|
@@ -9930,8 +10735,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
9930
10735
|
}
|
|
9931
10736
|
}
|
|
9932
10737
|
uploadStatePath(file) {
|
|
9933
|
-
const stateDir =
|
|
9934
|
-
return
|
|
10738
|
+
const stateDir = path16.join(this.options.machineDir, "trace-uploads");
|
|
10739
|
+
return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
|
|
9935
10740
|
}
|
|
9936
10741
|
async isUploaded(file) {
|
|
9937
10742
|
try {
|
|
@@ -9943,9 +10748,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
9943
10748
|
}
|
|
9944
10749
|
async markUploaded(file, metadata) {
|
|
9945
10750
|
const stateFile = this.uploadStatePath(file);
|
|
9946
|
-
await mkdir2(
|
|
10751
|
+
await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
|
|
9947
10752
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
9948
|
-
file:
|
|
10753
|
+
file: path16.basename(file),
|
|
9949
10754
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9950
10755
|
...metadata
|
|
9951
10756
|
}, null, 2)}
|
|
@@ -9964,10 +10769,10 @@ function readPositiveIntegerEnv2(name, fallback) {
|
|
|
9964
10769
|
|
|
9965
10770
|
// src/core.ts
|
|
9966
10771
|
var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
9967
|
-
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels"];
|
|
10772
|
+
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
|
|
9968
10773
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
9969
10774
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
9970
|
-
var DAEMON_CLI_USAGE =
|
|
10775
|
+
var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
|
|
9971
10776
|
var RunnerCredentialMintError2 = class extends Error {
|
|
9972
10777
|
code;
|
|
9973
10778
|
retryable;
|
|
@@ -10003,9 +10808,9 @@ function runnerCredentialErrorDetail2(error) {
|
|
|
10003
10808
|
async function waitForRunnerCredentialRetry2() {
|
|
10004
10809
|
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
10005
10810
|
}
|
|
10006
|
-
function parseDaemonCliArgs(args) {
|
|
10811
|
+
function parseDaemonCliArgs(args, env = {}) {
|
|
10007
10812
|
let serverUrl = "";
|
|
10008
|
-
let apiKey = "";
|
|
10813
|
+
let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
|
|
10009
10814
|
for (let i = 0; i < args.length; i++) {
|
|
10010
10815
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
10011
10816
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -10022,23 +10827,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
10022
10827
|
}
|
|
10023
10828
|
}
|
|
10024
10829
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
10025
|
-
const dirname =
|
|
10026
|
-
const jsPath =
|
|
10830
|
+
const dirname = path17.dirname(fileURLToPath2(moduleUrl));
|
|
10831
|
+
const jsPath = path17.resolve(dirname, "chat-bridge.js");
|
|
10027
10832
|
try {
|
|
10028
10833
|
accessSync(jsPath);
|
|
10029
10834
|
return jsPath;
|
|
10030
10835
|
} catch {
|
|
10031
|
-
return
|
|
10836
|
+
return path17.resolve(dirname, "chat-bridge.ts");
|
|
10032
10837
|
}
|
|
10033
10838
|
}
|
|
10034
10839
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
10035
|
-
const thisDir =
|
|
10036
|
-
const bundledDistPath =
|
|
10840
|
+
const thisDir = path17.dirname(fileURLToPath2(moduleUrl));
|
|
10841
|
+
const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
|
|
10037
10842
|
try {
|
|
10038
10843
|
accessSync(bundledDistPath);
|
|
10039
10844
|
return bundledDistPath;
|
|
10040
10845
|
} catch {
|
|
10041
|
-
const workspaceDistPath =
|
|
10846
|
+
const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
10042
10847
|
accessSync(workspaceDistPath);
|
|
10043
10848
|
return workspaceDistPath;
|
|
10044
10849
|
}
|
|
@@ -10217,7 +11022,7 @@ var DaemonCore = class {
|
|
|
10217
11022
|
}
|
|
10218
11023
|
resolveMachineStateRoot() {
|
|
10219
11024
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
10220
|
-
if (this.options.dataDir) return
|
|
11025
|
+
if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
|
|
10221
11026
|
return resolveDefaultMachineStateRoot();
|
|
10222
11027
|
}
|
|
10223
11028
|
shouldEnableLocalTrace() {
|
|
@@ -10443,10 +11248,19 @@ var DaemonCore = class {
|
|
|
10443
11248
|
case "agent:start":
|
|
10444
11249
|
logger.info(`[Agent ${msg.agentId}] Start requested (runtime=${msg.config.runtime}, model=${msg.config.model}, session=${msg.config.sessionId || "new"}${msg.wakeMessage ? ", wake=true" : ""})`);
|
|
10445
11250
|
this.startAgentFromMessage(msg).catch((err) => {
|
|
10446
|
-
const
|
|
10447
|
-
logger.error(`[Agent ${msg.agentId}] Start failed (${reason})`);
|
|
11251
|
+
const classification = classifySpawnFailure(err);
|
|
11252
|
+
logger.error(`[Agent ${msg.agentId}] Start failed (${classification.reason}): ${classification.detail}`);
|
|
11253
|
+
this.recordDaemonTrace("daemon.agent.spawn.failed", {
|
|
11254
|
+
agentId: msg.agentId,
|
|
11255
|
+
launchId: msg.launchId,
|
|
11256
|
+
runtime: msg.config.runtime,
|
|
11257
|
+
model: msg.config.model,
|
|
11258
|
+
failure_reason: classification.reason,
|
|
11259
|
+
failure_detail: classification.detail,
|
|
11260
|
+
session_id_present: Boolean(msg.config.sessionId)
|
|
11261
|
+
}, "error");
|
|
10448
11262
|
this.connection.send({ type: "agent:status", agentId: msg.agentId, status: "inactive", launchId: msg.launchId });
|
|
10449
|
-
this.connection.send({ type: "agent:activity", agentId: msg.agentId, activity: "offline", detail:
|
|
11263
|
+
this.connection.send({ type: "agent:activity", agentId: msg.agentId, activity: "offline", detail: classification.userMessage, launchId: msg.launchId });
|
|
10450
11264
|
});
|
|
10451
11265
|
break;
|
|
10452
11266
|
case "agent:stop":
|
|
@@ -10656,8 +11470,8 @@ var DaemonCore = class {
|
|
|
10656
11470
|
capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
|
|
10657
11471
|
runtimes,
|
|
10658
11472
|
runningAgents: runningAgentIds,
|
|
10659
|
-
hostname: this.options.hostname ??
|
|
10660
|
-
os: this.options.osDescription ?? `${
|
|
11473
|
+
hostname: this.options.hostname ?? os7.hostname(),
|
|
11474
|
+
os: this.options.osDescription ?? `${os7.platform()} ${os7.arch()}`,
|
|
10661
11475
|
daemonVersion: this.daemonVersion
|
|
10662
11476
|
});
|
|
10663
11477
|
this.recordDaemonTrace("daemon.ready.sent", {
|
|
@@ -10719,6 +11533,8 @@ var DaemonCore = class {
|
|
|
10719
11533
|
};
|
|
10720
11534
|
|
|
10721
11535
|
export {
|
|
11536
|
+
DAEMON_API_KEY_ENV,
|
|
11537
|
+
scrubDaemonAuthEnv,
|
|
10722
11538
|
resolveWorkspaceDirectoryPath,
|
|
10723
11539
|
scanWorkspaceDirectories,
|
|
10724
11540
|
deleteWorkspaceDirectory,
|