@slock-ai/daemon 0.54.2 → 0.55.0

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.
@@ -9,11 +9,35 @@ import {
9
9
 
10
10
  // src/core.ts
11
11
  import path16 from "path";
12
- import os8 from "os";
12
+ import os7 from "os";
13
13
  import { createRequire as createRequire2 } from "module";
14
14
  import { accessSync } from "fs";
15
15
  import { fileURLToPath } from "url";
16
16
 
17
+ // ../shared/src/slockRefs.ts
18
+ var SLOCK_REF_CHANNEL_NAME_PATTERN = String.raw`[\p{L}\p{N}_-]+`;
19
+ var SLOCK_REF_USER_NAME_PATTERN = SLOCK_REF_CHANNEL_NAME_PATTERN;
20
+ var SLOCK_REF_DM_PEER_PATTERN = String.raw`[\w-]+`;
21
+ var SLOCK_REF_THREAD_SHORT_ID_PATTERN = String.raw`[\da-f]{6,8}`;
22
+ var SLOCK_REF_MESSAGE_ID_PATTERN = String.raw`[A-Za-z0-9][A-Za-z0-9-]{1,63}`;
23
+ var SLOCK_REF_TASK_NUMBER_PATTERN = String.raw`[1-9]\d*`;
24
+ var USER_RE = new RegExp(String.raw`^@(${SLOCK_REF_USER_NAME_PATTERN})$`, "u");
25
+ var CHANNEL_RE = new RegExp(String.raw`^#(${SLOCK_REF_CHANNEL_NAME_PATTERN})$`, "u");
26
+ var CHANNEL_THREAD_RE = new RegExp(
27
+ String.raw`^#(${SLOCK_REF_CHANNEL_NAME_PATTERN}):(${SLOCK_REF_THREAD_SHORT_ID_PATTERN})$`,
28
+ "iu"
29
+ );
30
+ var DM_RE = new RegExp(String.raw`^dm:@(${SLOCK_REF_DM_PEER_PATTERN})$`, "iu");
31
+ var DM_THREAD_RE = new RegExp(
32
+ String.raw`^dm:@(${SLOCK_REF_DM_PEER_PATTERN}):(${SLOCK_REF_THREAD_SHORT_ID_PATTERN})$`,
33
+ "iu"
34
+ );
35
+ var TASK_RE = new RegExp(String.raw`^task\s+#(${SLOCK_REF_TASK_NUMBER_PATTERN})$`, "iu");
36
+ var CHANNEL_MESSAGE_RE = new RegExp(
37
+ String.raw`^#(${SLOCK_REF_CHANNEL_NAME_PATTERN})(?::(${SLOCK_REF_THREAD_SHORT_ID_PATTERN}))?\s+msg=(${SLOCK_REF_MESSAGE_ID_PATTERN})$`,
38
+ "iu"
39
+ );
40
+
17
41
  // ../shared/src/tracing/index.ts
18
42
  var DEFAULT_TRACE_FLAGS = "00";
19
43
  var TRACEPARENT_VERSION = "00";
@@ -724,6 +748,7 @@ var SERVER_CAPABILITY_MATRIX = {
724
748
  };
725
749
 
726
750
  // ../shared/src/index.ts
751
+ var RUNTIME_CONFIG_VERSION = 1;
727
752
  var RUNTIMES = [
728
753
  { id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
729
754
  { id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
@@ -734,6 +759,164 @@ var RUNTIMES = [
734
759
  { id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true },
735
760
  { id: "opencode", displayName: "OpenCode", binary: "opencode", supported: true }
736
761
  ];
762
+ var RUNTIME_MODELS = {
763
+ claude: [
764
+ { id: "opus", label: "Opus" },
765
+ { id: "sonnet", label: "Sonnet" },
766
+ { id: "haiku", label: "Haiku" }
767
+ ],
768
+ codex: [
769
+ { id: "gpt-5.5", label: "GPT-5.5" },
770
+ { id: "gpt-5.4", label: "GPT-5.4" },
771
+ { id: "gpt-5.3-codex", label: "GPT-5.3 Codex" },
772
+ { id: "gpt-5.3-codex-spark", label: "GPT-5.3 Codex Spark" },
773
+ { id: "gpt-5.2-codex", label: "GPT-5.2 Codex" },
774
+ { id: "gpt-5.2", label: "GPT-5.2" },
775
+ { id: "gpt-5.1-codex-max", label: "GPT-5.1 Codex Max" },
776
+ { id: "gpt-5.1-codex", label: "GPT-5.1 Codex" },
777
+ { id: "gpt-5-codex", label: "GPT-5 Codex" },
778
+ { id: "gpt-5", label: "GPT-5" }
779
+ ],
780
+ antigravity: [
781
+ { id: "default", label: "AGY configured default", verified: "suggestion_only" }
782
+ ],
783
+ copilot: [
784
+ { id: "gpt-5.4", label: "GPT-5.4" },
785
+ { id: "gpt-5.2", label: "GPT-5.2" },
786
+ { id: "claude-4-sonnet", label: "Claude 4 Sonnet" },
787
+ { id: "claude-4.5-sonnet", label: "Claude 4.5 Sonnet" }
788
+ ],
789
+ cursor: [
790
+ { id: "composer-2-fast", label: "Composer 2 Fast" },
791
+ { id: "composer-2", label: "Composer 2" },
792
+ { id: "auto", label: "Auto" }
793
+ ],
794
+ gemini: [
795
+ { id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
796
+ { id: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro (Preview)" },
797
+ { id: "gemini-3-flash-preview", label: "Gemini 3 Flash (Preview)" },
798
+ { id: "gemini-2.5-pro", label: "Gemini 2.5 Pro" },
799
+ { id: "gemini-2.5-flash", label: "Gemini 2.5 Flash" }
800
+ ],
801
+ opencode: [
802
+ { id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
803
+ { id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro (OpenCode)", verified: "suggestion_only" },
804
+ { id: "openrouter/anthropic/claude-opus-4.5", label: "Claude Opus 4.5 via OpenRouter", verified: "suggestion_only" },
805
+ { id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
806
+ ],
807
+ // Kimi CLI resolves model keys from each user's local config, so the safest
808
+ // built-in option is to defer to whatever default model the CLI already uses.
809
+ kimi: [
810
+ { id: "default", label: "Configured Default" }
811
+ ]
812
+ };
813
+ function getDefaultModel(runtimeId) {
814
+ const models = RUNTIME_MODELS[runtimeId];
815
+ return models?.[0]?.id ?? "sonnet";
816
+ }
817
+ var CONTROLLED_RUNTIME_ENV_KEYS = {
818
+ claude: ["ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY", "ANTHROPIC_CUSTOM_MODEL_OPTION"]
819
+ };
820
+ function isPlainRecord(value) {
821
+ return value !== null && typeof value === "object" && !Array.isArray(value);
822
+ }
823
+ function normalizeEnvVars(envVars) {
824
+ if (!isPlainRecord(envVars)) return null;
825
+ const normalized = {};
826
+ for (const [key, value] of Object.entries(envVars)) {
827
+ if (typeof key === "string" && typeof value === "string") {
828
+ normalized[key] = value;
829
+ }
830
+ }
831
+ return Object.keys(normalized).length > 0 ? normalized : null;
832
+ }
833
+ function getControlledRuntimeEnvKeys(runtime) {
834
+ return CONTROLLED_RUNTIME_ENV_KEYS[runtime] ?? [];
835
+ }
836
+ function stripControlledRuntimeEnvVars(runtime, envVars) {
837
+ const normalized = normalizeEnvVars(envVars);
838
+ if (!normalized) return null;
839
+ const controlled = new Set(getControlledRuntimeEnvKeys(runtime));
840
+ for (const key of controlled) {
841
+ delete normalized[key];
842
+ }
843
+ return Object.keys(normalized).length > 0 ? normalized : null;
844
+ }
845
+ function isPresetRuntimeModel(runtime, model) {
846
+ return (RUNTIME_MODELS[runtime] ?? []).some((candidate) => candidate.id === model);
847
+ }
848
+ function modelConfigFromLegacy(runtime, model) {
849
+ return isPresetRuntimeModel(runtime, model) ? { kind: "preset", id: model } : { kind: "custom", name: model };
850
+ }
851
+ function parseProviderConfig(runtime, value) {
852
+ if (runtime !== "claude") return void 0;
853
+ if (!isPlainRecord(value)) return { kind: "default" };
854
+ if (value.kind === "custom" && typeof value.apiUrl === "string" && value.apiUrl.trim() && typeof value.apiKey === "string" && value.apiKey.trim()) {
855
+ return { kind: "custom", apiUrl: value.apiUrl.trim(), apiKey: value.apiKey.trim() };
856
+ }
857
+ return { kind: "default" };
858
+ }
859
+ function parseModelConfig(runtime, value, fallback) {
860
+ if (!isPlainRecord(value)) return modelConfigFromLegacy(runtime, fallback);
861
+ if (value.kind === "custom" && typeof value.name === "string" && value.name.trim()) {
862
+ return { kind: "custom", name: value.name.trim() };
863
+ }
864
+ if (value.kind === "preset" && typeof value.id === "string" && value.id.trim()) {
865
+ return { kind: "preset", id: value.id.trim() };
866
+ }
867
+ return modelConfigFromLegacy(runtime, fallback);
868
+ }
869
+ function parseModeConfig(value) {
870
+ if (!isPlainRecord(value)) return { kind: "default" };
871
+ if (value.kind === "fast") return { kind: "fast" };
872
+ return { kind: "default" };
873
+ }
874
+ function hydrateRuntimeConfig(input) {
875
+ const stored = isPlainRecord(input.runtimeConfig) && input.runtimeConfig.version === RUNTIME_CONFIG_VERSION ? input.runtimeConfig : null;
876
+ const runtime = typeof stored?.runtime === "string" && stored.runtime.trim() || typeof input.runtime === "string" && input.runtime.trim() || "claude";
877
+ const fallbackModel = typeof input.model === "string" && input.model.trim() || getDefaultModel(runtime);
878
+ const legacyEnvVars = normalizeEnvVars(input.envVars);
879
+ const storedEnvVars = normalizeEnvVars(stored?.envVars);
880
+ const legacyClaudeApiUrl = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_BASE_URL : void 0;
881
+ const legacyClaudeApiKey = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_API_KEY : void 0;
882
+ const provider = stored ? parseProviderConfig(runtime, stored.provider) : runtime === "claude" && legacyClaudeApiUrl && legacyClaudeApiKey ? { kind: "custom", apiUrl: legacyClaudeApiUrl, apiKey: legacyClaudeApiKey } : runtime === "claude" ? { kind: "default" } : void 0;
883
+ return {
884
+ version: RUNTIME_CONFIG_VERSION,
885
+ runtime,
886
+ ...provider ? { provider } : {},
887
+ model: stored ? parseModelConfig(runtime, stored.model, fallbackModel) : modelConfigFromLegacy(runtime, fallbackModel),
888
+ mode: stored ? parseModeConfig(stored.mode) : { kind: "default" },
889
+ reasoningEffort: stored?.reasoningEffort ?? input.reasoningEffort ?? null,
890
+ envVars: stripControlledRuntimeEnvVars(runtime, stored ? storedEnvVars : legacyEnvVars)
891
+ };
892
+ }
893
+ function runtimeConfigModelValue(config) {
894
+ return config.model.kind === "custom" ? config.model.name : config.model.id;
895
+ }
896
+ function runtimeConfigToLaunchFields(config) {
897
+ const normalized = isPlainRecord(config) && config.version === RUNTIME_CONFIG_VERSION ? hydrateRuntimeConfig({ runtimeConfig: config }) : hydrateRuntimeConfig(config);
898
+ const generatedEnvVars = {};
899
+ if (normalized.runtime === "claude") {
900
+ if (normalized.provider?.kind === "custom") {
901
+ generatedEnvVars.ANTHROPIC_BASE_URL = normalized.provider.apiUrl;
902
+ generatedEnvVars.ANTHROPIC_API_KEY = normalized.provider.apiKey;
903
+ }
904
+ if (normalized.model.kind === "custom") {
905
+ generatedEnvVars.ANTHROPIC_CUSTOM_MODEL_OPTION = normalized.model.name;
906
+ }
907
+ }
908
+ const envVars = {
909
+ ...normalized.envVars ?? {},
910
+ ...generatedEnvVars
911
+ };
912
+ return {
913
+ runtime: normalized.runtime,
914
+ model: runtimeConfigModelValue(normalized),
915
+ mode: normalized.mode,
916
+ reasoningEffort: normalized.reasoningEffort ?? null,
917
+ envVars: Object.keys(envVars).length > 0 ? envVars : null
918
+ };
919
+ }
737
920
  var PLAN_CONFIG = {
738
921
  free: {
739
922
  displayName: "Hobby",
@@ -769,17 +952,14 @@ var DISPLAY_PLAN_CONFIG = {
769
952
  };
770
953
 
771
954
  // src/agentProcessManager.ts
772
- import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
955
+ import { mkdirSync as mkdirSync4, readdirSync, statSync, writeFileSync as writeFileSync7 } from "fs";
773
956
  import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
774
957
  import { createHash as createHash2 } from "crypto";
775
958
  import path12 from "path";
776
- import os6 from "os";
959
+ import os5 from "os";
777
960
 
778
961
  // src/drivers/claude.ts
779
962
  import { spawn } from "child_process";
780
- import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
781
- import os2 from "os";
782
- import path4 from "path";
783
963
 
784
964
  // src/drivers/cliTransport.ts
785
965
  import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
@@ -1949,7 +2129,7 @@ function unregisterAgentCredentialProxyForLaunch(input) {
1949
2129
  // src/drivers/cliTransport.ts
1950
2130
  var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
1951
2131
  var powershellSingleQuote = (value) => `'${value.replace(/'/g, "''")}'`;
1952
- var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels";
2132
+ var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels,knowledge";
1953
2133
  var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
1954
2134
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
1955
2135
  var RAW_CREDENTIAL_ENV_DENYLIST = [
@@ -2152,10 +2332,11 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
2152
2332
  }
2153
2333
  }
2154
2334
  const wrapperPath = platform === "win32" ? path2.join(slockDir, "slock.cmd") : posixWrapper;
2335
+ const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
2155
2336
  const spawnEnv = {
2156
2337
  ...process.env,
2157
2338
  FORCE_COLOR: "0",
2158
- ...ctx.config.envVars || {},
2339
+ ...launchRuntimeFields.envVars || {},
2159
2340
  ...extraEnv,
2160
2341
  ...platform === "win32" ? windowsUtf8Env() : {},
2161
2342
  ...runtimeContextEnv(ctx.config),
@@ -2186,6 +2367,129 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
2186
2367
  };
2187
2368
  }
2188
2369
 
2370
+ // src/drivers/claudeEventNormalizer.ts
2371
+ function collectResultErrorDetail(message, fallback) {
2372
+ const parts = [];
2373
+ if (Array.isArray(message.errors)) {
2374
+ for (const err of message.errors) {
2375
+ if (typeof err === "string" && err.trim()) parts.push(err.trim());
2376
+ }
2377
+ }
2378
+ if (typeof message.result === "string" && message.result.trim()) {
2379
+ parts.push(message.result.trim());
2380
+ }
2381
+ return parts.join(" | ") || fallback;
2382
+ }
2383
+ function isProviderApiFailureText(value, hasToolUse) {
2384
+ 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));
2385
+ }
2386
+ var ClaudeEventNormalizer = class {
2387
+ normalizeLine(line) {
2388
+ let event;
2389
+ try {
2390
+ event = JSON.parse(line);
2391
+ } catch {
2392
+ return [];
2393
+ }
2394
+ const events = [];
2395
+ const pushResultError = (message, fallback) => {
2396
+ events.push({ kind: "error", message: collectResultErrorDetail(message, fallback) });
2397
+ };
2398
+ switch (event.type) {
2399
+ case "system":
2400
+ if (event.subtype === "init" && event.session_id) {
2401
+ events.push({ kind: "session_init", sessionId: event.session_id });
2402
+ }
2403
+ if (event.subtype === "status" && event.status === "compacting") {
2404
+ events.push({ kind: "compaction_started" });
2405
+ }
2406
+ if (event.subtype === "status" && event.status === "requesting") {
2407
+ events.push({
2408
+ kind: "internal_progress",
2409
+ source: "claude_system_status",
2410
+ itemType: "requesting",
2411
+ payloadBytes: Buffer.byteLength(line, "utf8")
2412
+ });
2413
+ }
2414
+ if (event.subtype === "compact_boundary") {
2415
+ events.push({ kind: "compaction_finished" });
2416
+ }
2417
+ break;
2418
+ case "stream_event":
2419
+ events.push({
2420
+ kind: "internal_progress",
2421
+ source: "claude_stream_event",
2422
+ itemType: typeof event.event?.type === "string" && event.event.type.length > 0 ? event.event.type : "unknown",
2423
+ payloadBytes: Buffer.byteLength(line, "utf8")
2424
+ });
2425
+ break;
2426
+ case "assistant": {
2427
+ const content = event.message?.content;
2428
+ if (Array.isArray(content)) {
2429
+ const hasToolUse = content.some((block) => block?.type === "tool_use");
2430
+ for (const block of content) {
2431
+ if (block.type === "thinking" && block.thinking) {
2432
+ events.push({ kind: "thinking", text: block.thinking });
2433
+ } else if (block.type === "text" && block.text) {
2434
+ if (isProviderApiFailureText(block.text, hasToolUse)) {
2435
+ events.push({ kind: "error", message: block.text });
2436
+ } else {
2437
+ events.push({ kind: "text", text: block.text });
2438
+ }
2439
+ } else if (block.type === "tool_use") {
2440
+ events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
2441
+ }
2442
+ }
2443
+ }
2444
+ break;
2445
+ }
2446
+ case "user": {
2447
+ const content = event.message?.content;
2448
+ if (Array.isArray(content)) {
2449
+ for (const block of content) {
2450
+ if (block.type === "tool_result") {
2451
+ events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result" });
2452
+ }
2453
+ }
2454
+ }
2455
+ break;
2456
+ }
2457
+ case "result": {
2458
+ const subtype = typeof event.subtype === "string" ? event.subtype : "success";
2459
+ const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
2460
+ switch (subtype) {
2461
+ case "success":
2462
+ if (event.is_error && stopReason !== "max_tokens") {
2463
+ pushResultError(event, "Execution failed");
2464
+ }
2465
+ break;
2466
+ case "error_during_execution":
2467
+ if (stopReason !== "max_tokens") {
2468
+ pushResultError(event, "Execution failed");
2469
+ }
2470
+ break;
2471
+ case "error_max_budget_usd":
2472
+ pushResultError(event, "Budget limit exceeded");
2473
+ break;
2474
+ case "error_max_turns":
2475
+ pushResultError(event, "Max turns exceeded");
2476
+ break;
2477
+ case "error_max_structured_output_retries":
2478
+ pushResultError(event, "Structured output retries exceeded");
2479
+ break;
2480
+ }
2481
+ events.push({ kind: "turn_end", sessionId: event.session_id });
2482
+ break;
2483
+ }
2484
+ }
2485
+ return events;
2486
+ }
2487
+ };
2488
+
2489
+ // src/drivers/claudeLaunch.ts
2490
+ import { writeFileSync as writeFileSync2 } from "fs";
2491
+ import path4 from "path";
2492
+
2189
2493
  // src/drivers/probe.ts
2190
2494
  import { execFileSync } from "child_process";
2191
2495
  import { existsSync as existsSync2 } from "fs";
@@ -2386,12 +2690,10 @@ function resolveHomePath(relativePath, deps = {}) {
2386
2690
  return path3.join(homeDir, relativePath);
2387
2691
  }
2388
2692
 
2389
- // src/drivers/claude.ts
2693
+ // src/drivers/claudeLaunch.ts
2390
2694
  var CLAUDE_DESKTOP_CLI_RELATIVE_PATH = path4.join("Applications", "Claude Code URL Handler.app", "Contents", "MacOS", "claude");
2391
2695
  var CLAUDE_DESKTOP_CLI_SYSTEM_PATH = "/Applications/Claude Code URL Handler.app/Contents/MacOS/claude";
2392
2696
  var CLAUDE_SYSTEM_PROMPT_FILE = "claude-system-prompt.md";
2393
- var CLAUDE_MCP_CONFIG_FILE = "claude-mcp-config.json";
2394
- var SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME = "chat";
2395
2697
  var CLAUDE_DISALLOWED_TOOLS = [
2396
2698
  "EnterPlanMode",
2397
2699
  "ExitPlanMode",
@@ -2417,81 +2719,54 @@ function probeClaude(deps = {}) {
2417
2719
  version: readCommandVersion(command, [], deps) ?? void 0
2418
2720
  };
2419
2721
  }
2420
- function isRecord(value) {
2421
- return value !== null && typeof value === "object" && !Array.isArray(value);
2422
- }
2423
- function expandClaudeMcpConfigVariables(raw, vars) {
2424
- let expanded = raw;
2425
- for (const [name, value] of Object.entries(vars)) {
2426
- const escapedValue = process.platform === "win32" ? value.replace(/\\/g, "\\\\") : value;
2427
- expanded = expanded.replaceAll(`\${${name}}`, escapedValue);
2722
+ function buildClaudeArgs(config, opts) {
2723
+ const launchRuntimeFields = runtimeConfigToLaunchFields(config);
2724
+ const args = [
2725
+ "--allow-dangerously-skip-permissions",
2726
+ "--dangerously-skip-permissions",
2727
+ "--verbose",
2728
+ "--permission-mode",
2729
+ "bypassPermissions",
2730
+ "--output-format",
2731
+ "stream-json",
2732
+ "--input-format",
2733
+ "stream-json",
2734
+ "--include-partial-messages",
2735
+ "--model",
2736
+ launchRuntimeFields.model || "sonnet",
2737
+ "--disallowed-tools",
2738
+ CLAUDE_DISALLOWED_TOOLS,
2739
+ "--append-system-prompt-file",
2740
+ opts.standingPromptFilePath
2741
+ ];
2742
+ if (launchRuntimeFields.reasoningEffort) {
2743
+ args.push("--effort", launchRuntimeFields.reasoningEffort);
2428
2744
  }
2429
- return expanded;
2430
- }
2431
- function readClaudeMcpServers(configPath, vars = {}) {
2432
- try {
2433
- const parsed = JSON.parse(
2434
- expandClaudeMcpConfigVariables(readFileSync2(configPath, "utf8"), vars)
2435
- );
2436
- if (!isRecord(parsed) || !isRecord(parsed.mcpServers)) return null;
2437
- return parsed.mcpServers;
2438
- } catch (err) {
2439
- logger.warn(
2440
- `[Claude] failed to read MCP config ${configPath}: ${err instanceof Error ? err.message : String(err)}`
2441
- );
2442
- return null;
2745
+ if (launchRuntimeFields.mode.kind === "fast") {
2746
+ args.push("--bare");
2747
+ }
2748
+ if (config.sessionId) {
2749
+ args.push("--resume", config.sessionId);
2443
2750
  }
2751
+ return args;
2444
2752
  }
2445
- function resolveClaudeConfigDir(ctx, home) {
2446
- const configured = ctx.config.envVars?.CLAUDE_CONFIG_DIR || process.env.CLAUDE_CONFIG_DIR;
2447
- return configured && path4.isAbsolute(configured) ? configured : path4.join(home, ".claude");
2753
+ function writeClaudeSystemPromptFile(standingPrompt, slockDir) {
2754
+ const systemPromptPath = path4.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
2755
+ writeFileSync2(systemPromptPath, standingPrompt, { mode: 384 });
2756
+ return systemPromptPath;
2448
2757
  }
2449
- function collectClaudeMcpConfigFiles(ctx, home) {
2450
- const files = [];
2451
- const pushIfFile = (candidate) => {
2452
- try {
2453
- if (existsSync3(candidate) && statSync(candidate).isFile()) {
2454
- files.push({ path: candidate });
2455
- }
2456
- } catch {
2457
- }
2758
+ function buildClaudeSpawnSpec(claudeCommand, platform = process.platform) {
2759
+ const lowerClaudeCommand = claudeCommand?.toLowerCase();
2760
+ const isBatchFile = Boolean(
2761
+ platform === "win32" && lowerClaudeCommand && (lowerClaudeCommand.endsWith(".cmd") || lowerClaudeCommand.endsWith(".bat"))
2762
+ );
2763
+ return {
2764
+ command: claudeCommand ?? "claude",
2765
+ shell: platform === "win32" && (!claudeCommand || isBatchFile)
2458
2766
  };
2459
- pushIfFile(path4.join(home, ".claude.json"));
2460
- pushIfFile(path4.join(ctx.workingDirectory, ".mcp.json"));
2461
- const pluginRoot = path4.join(resolveClaudeConfigDir(ctx, home), "plugins");
2462
- try {
2463
- for (const entry of readdirSync(pluginRoot)) {
2464
- const pluginPath = path4.join(pluginRoot, entry);
2465
- const configPath = path4.join(pluginPath, ".mcp.json");
2466
- try {
2467
- if (existsSync3(configPath) && statSync(configPath).isFile()) {
2468
- files.push({
2469
- path: configPath,
2470
- vars: { CLAUDE_PLUGIN_ROOT: pluginPath }
2471
- });
2472
- }
2473
- } catch {
2474
- }
2475
- }
2476
- } catch {
2477
- }
2478
- return files;
2479
- }
2480
- function buildClaudeUserMcpServers(ctx, home) {
2481
- const servers = /* @__PURE__ */ Object.create(null);
2482
- for (const configFile of collectClaudeMcpConfigFiles(ctx, home)) {
2483
- const mcpServers = readClaudeMcpServers(configFile.path, configFile.vars);
2484
- if (!mcpServers) continue;
2485
- for (const [name, server] of Object.entries(mcpServers)) {
2486
- if (!isRecord(server)) {
2487
- logger.warn(`[Claude] ignoring invalid MCP server "${name}" in ${configFile.path}`);
2488
- continue;
2489
- }
2490
- servers[name] = server;
2491
- }
2492
- }
2493
- return servers;
2494
2767
  }
2768
+
2769
+ // src/drivers/claude.ts
2495
2770
  var ClaudeDriver = class {
2496
2771
  id = "claude";
2497
2772
  lifecycle = {
@@ -2501,7 +2776,7 @@ var ClaudeDriver = class {
2501
2776
  };
2502
2777
  communication = {
2503
2778
  chat: "slock_cli",
2504
- runtimeControl: "mcp_runtime_actions"
2779
+ runtimeControl: "none"
2505
2780
  };
2506
2781
  session = {
2507
2782
  recovery: "resume_or_fresh"
@@ -2511,107 +2786,37 @@ var ClaudeDriver = class {
2511
2786
  toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
2512
2787
  };
2513
2788
  supportsStdinNotification = true;
2514
- mcpToolPrefix = "mcp__chat__";
2789
+ mcpToolPrefix = "";
2515
2790
  usesSlockCliForCommunication = true;
2516
2791
  // Claude Code supports same-turn steering, but raw stdin injection at an
2517
2792
  // arbitrary busy instant can collide with active signed thinking blocks. The
2518
2793
  // daemon therefore gates busy delivery on Claude stream-json boundaries.
2519
2794
  busyDeliveryMode = "gated";
2520
2795
  supportsNativeStandingPrompt = true;
2796
+ eventNormalizer = new ClaudeEventNormalizer();
2521
2797
  probe() {
2522
2798
  return probeClaude();
2523
2799
  }
2524
- buildClaudeArgs(config, standingPrompt, opts = {}) {
2525
- const args = [
2526
- "--allow-dangerously-skip-permissions",
2527
- "--dangerously-skip-permissions",
2528
- "--verbose",
2529
- "--permission-mode",
2530
- "bypassPermissions",
2531
- "--output-format",
2532
- "stream-json",
2533
- "--input-format",
2534
- "stream-json",
2535
- "--model",
2536
- config.model || "sonnet",
2537
- "--disallowed-tools",
2538
- CLAUDE_DISALLOWED_TOOLS
2539
- ];
2540
- if (opts.standingPromptFilePath) {
2541
- args.push("--append-system-prompt-file", opts.standingPromptFilePath);
2542
- } else {
2543
- args.push("--append-system-prompt", standingPrompt);
2544
- }
2545
- if (config.sessionId) {
2546
- args.push("--resume", config.sessionId);
2547
- }
2548
- return args;
2549
- }
2550
- buildRuntimeActionsMcpServer(ctx) {
2551
- const isTsSource = ctx.chatBridgePath.endsWith(".ts");
2552
- const command = isTsSource ? "npx" : "node";
2553
- const bridgeArgs = isTsSource ? ["tsx", ctx.chatBridgePath] : [ctx.chatBridgePath];
2554
- return {
2555
- command,
2556
- args: [
2557
- ...bridgeArgs,
2558
- "--agent-id",
2559
- ctx.agentId,
2560
- "--server-url",
2561
- ctx.config.serverUrl,
2562
- "--auth-token",
2563
- ctx.config.authToken || ctx.daemonApiKey,
2564
- "--runtime",
2565
- this.id,
2566
- ...ctx.launchId ? ["--launch-id", ctx.launchId] : [],
2567
- "--runtime-actions-only"
2568
- ]
2569
- };
2570
- }
2571
- buildRuntimeActionsMcpConfig(ctx, home = os2.homedir()) {
2572
- const userMcpServers = buildClaudeUserMcpServers(ctx, home);
2573
- if (Object.prototype.hasOwnProperty.call(userMcpServers, SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME)) {
2574
- logger.warn(
2575
- `[Agent ${ctx.agentId}] Claude user MCP server "${SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME}" is reserved by Slock runtime actions and will be ignored`
2576
- );
2577
- }
2578
- return JSON.stringify({
2579
- mcpServers: {
2580
- ...userMcpServers,
2581
- [SLOCK_RUNTIME_ACTIONS_MCP_SERVER_NAME]: this.buildRuntimeActionsMcpServer(ctx)
2582
- }
2583
- });
2584
- }
2585
- writeClaudeLaunchFiles(ctx, slockDir, home = os2.homedir()) {
2586
- const systemPromptPath = path4.join(slockDir, CLAUDE_SYSTEM_PROMPT_FILE);
2587
- const mcpConfigPath = path4.join(slockDir, CLAUDE_MCP_CONFIG_FILE);
2588
- writeFileSync2(systemPromptPath, ctx.standingPrompt, { mode: 384 });
2589
- writeFileSync2(mcpConfigPath, this.buildRuntimeActionsMcpConfig(ctx, home), { mode: 384 });
2590
- return { systemPromptPath, mcpConfigPath };
2800
+ buildClaudeArgs(config, opts) {
2801
+ return buildClaudeArgs(config, opts);
2591
2802
  }
2592
2803
  async spawn(ctx) {
2593
2804
  const { slockDir, tokenFile, spawnEnv } = await prepareCliTransport(ctx);
2594
- const { systemPromptPath, mcpConfigPath } = this.writeClaudeLaunchFiles(ctx, slockDir);
2595
- const args = this.buildClaudeArgs(ctx.config, ctx.standingPrompt, {
2805
+ const systemPromptPath = writeClaudeSystemPromptFile(ctx.standingPrompt, slockDir);
2806
+ const args = this.buildClaudeArgs(ctx.config, {
2596
2807
  standingPromptFilePath: systemPromptPath
2597
2808
  });
2598
- args.push("--mcp-config", mcpConfigPath, "--strict-mcp-config");
2599
2809
  delete spawnEnv.CLAUDECODE;
2600
2810
  logger.info(
2601
2811
  `[Agent ${ctx.agentId}] transport=cli cli=${ctx.slockCliPath} token_file=${tokenFile}`
2602
2812
  );
2603
2813
  const claudeCommand = resolveClaudeCommand();
2604
- const isWindows = process.platform === "win32";
2605
- const lowerClaudeCommand = claudeCommand?.toLowerCase();
2606
- const isBatchFile = Boolean(
2607
- isWindows && lowerClaudeCommand && (lowerClaudeCommand.endsWith(".cmd") || lowerClaudeCommand.endsWith(".bat"))
2608
- );
2609
- const useShell = isWindows && (!claudeCommand || isBatchFile);
2610
- const proc = spawn(claudeCommand ?? "claude", args, {
2814
+ const spawnSpec = buildClaudeSpawnSpec(claudeCommand);
2815
+ const proc = spawn(spawnSpec.command, args, {
2611
2816
  cwd: ctx.workingDirectory,
2612
2817
  stdio: ["pipe", "pipe", "pipe"],
2613
2818
  env: spawnEnv,
2614
- shell: useShell
2819
+ shell: spawnSpec.shell
2615
2820
  });
2616
2821
  const stdinMsg = JSON.stringify({
2617
2822
  type: "user",
@@ -2625,99 +2830,7 @@ var ClaudeDriver = class {
2625
2830
  return { process: proc };
2626
2831
  }
2627
2832
  parseLine(line) {
2628
- let event;
2629
- try {
2630
- event = JSON.parse(line);
2631
- } catch {
2632
- return [];
2633
- }
2634
- const events = [];
2635
- const pushResultError = (message, fallback) => {
2636
- const parts = [];
2637
- if (Array.isArray(message.errors)) {
2638
- for (const err of message.errors) {
2639
- if (typeof err === "string" && err.trim()) parts.push(err.trim());
2640
- }
2641
- }
2642
- if (typeof message.result === "string" && message.result.trim()) {
2643
- parts.push(message.result.trim());
2644
- }
2645
- const detail = parts.join(" | ") || fallback;
2646
- events.push({ kind: "error", message: detail });
2647
- };
2648
- const isProviderApiFailureText = (value, hasToolUse) => !hasToolUse && /^\s*API Error:/i.test(value) && (/\b(?:ECONNRESET|EPIPE|ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/i.test(value) || /\bUnable to connect to API\b/i.test(value) || /\b(?:timed out|timeout)\b/i.test(value) || /\b5\d{2}\b/.test(value));
2649
- switch (event.type) {
2650
- case "system":
2651
- if (event.subtype === "init" && event.session_id) {
2652
- events.push({ kind: "session_init", sessionId: event.session_id });
2653
- }
2654
- if (event.subtype === "status" && event.status === "compacting") {
2655
- events.push({ kind: "compaction_started" });
2656
- }
2657
- if (event.subtype === "compact_boundary") {
2658
- events.push({ kind: "compaction_finished" });
2659
- }
2660
- break;
2661
- case "assistant": {
2662
- const content = event.message?.content;
2663
- if (Array.isArray(content)) {
2664
- const hasToolUse = content.some((block) => block?.type === "tool_use");
2665
- for (const block of content) {
2666
- if (block.type === "thinking" && block.thinking) {
2667
- events.push({ kind: "thinking", text: block.thinking });
2668
- } else if (block.type === "text" && block.text) {
2669
- if (isProviderApiFailureText(block.text, hasToolUse)) {
2670
- events.push({ kind: "error", message: block.text });
2671
- } else {
2672
- events.push({ kind: "text", text: block.text });
2673
- }
2674
- } else if (block.type === "tool_use") {
2675
- events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
2676
- }
2677
- }
2678
- }
2679
- break;
2680
- }
2681
- case "user": {
2682
- const content = event.message?.content;
2683
- if (Array.isArray(content)) {
2684
- for (const block of content) {
2685
- if (block.type === "tool_result") {
2686
- events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result" });
2687
- }
2688
- }
2689
- }
2690
- break;
2691
- }
2692
- case "result": {
2693
- const subtype = typeof event.subtype === "string" ? event.subtype : "success";
2694
- const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
2695
- switch (subtype) {
2696
- case "success":
2697
- if (event.is_error && stopReason !== "max_tokens") {
2698
- pushResultError(event, "Execution failed");
2699
- }
2700
- break;
2701
- case "error_during_execution":
2702
- if (stopReason !== "max_tokens") {
2703
- pushResultError(event, "Execution failed");
2704
- }
2705
- break;
2706
- case "error_max_budget_usd":
2707
- pushResultError(event, "Budget limit exceeded");
2708
- break;
2709
- case "error_max_turns":
2710
- pushResultError(event, "Max turns exceeded");
2711
- break;
2712
- case "error_max_structured_output_retries":
2713
- pushResultError(event, "Structured output retries exceeded");
2714
- break;
2715
- }
2716
- events.push({ kind: "turn_end", sessionId: event.session_id });
2717
- break;
2718
- }
2719
- }
2720
- return events;
2833
+ return this.eventNormalizer.normalizeLine(line);
2721
2834
  }
2722
2835
  encodeStdinMessage(text, sessionId, _opts) {
2723
2836
  return JSON.stringify({
@@ -2731,7 +2844,7 @@ var ClaudeDriver = class {
2731
2844
  }
2732
2845
  buildSystemPrompt(config, _agentId) {
2733
2846
  return buildCliTransportSystemPrompt(config, {
2734
- toolPrefix: "mcp__chat__",
2847
+ toolPrefix: "",
2735
2848
  extraCriticalRules: [],
2736
2849
  postStartupNotes: [
2737
2850
  "**Claude runtime note:** While you are busy, Slock batches inbox-count notifications instead of injecting message content. Use `slock message check` at natural breakpoints to pull the pending messages before side-effect actions that depend on current context."
@@ -2744,8 +2857,8 @@ var ClaudeDriver = class {
2744
2857
 
2745
2858
  // src/drivers/codex.ts
2746
2859
  import { spawn as spawn2, execFileSync as execFileSync2, execSync } from "child_process";
2747
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
2748
- import os3 from "os";
2860
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
2861
+ import os2 from "os";
2749
2862
  import path5 from "path";
2750
2863
 
2751
2864
  // src/runtimeTurnState.ts
@@ -3107,7 +3220,7 @@ var CodexEventNormalizer = class {
3107
3220
 
3108
3221
  // src/drivers/codex.ts
3109
3222
  function ensureGitRepoForCodex(workingDirectory, deps = {}) {
3110
- const existsSyncFn = deps.existsSyncFn ?? existsSync4;
3223
+ const existsSyncFn = deps.existsSyncFn ?? existsSync3;
3111
3224
  const execSyncFn = deps.execSyncFn ?? execSync;
3112
3225
  const gitDir = path5.join(workingDirectory, ".git");
3113
3226
  if (existsSyncFn(gitDir)) return;
@@ -3125,7 +3238,7 @@ function isWindowsSandboxRunner(commandPath) {
3125
3238
  return path5.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
3126
3239
  }
3127
3240
  function resolveWindowsNpmCodexEntry(deps = {}) {
3128
- const existsSyncFn = deps.existsSyncFn ?? existsSync4;
3241
+ const existsSyncFn = deps.existsSyncFn ?? existsSync3;
3129
3242
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
3130
3243
  const env = deps.env ?? process.env;
3131
3244
  const winPath = path5.win32;
@@ -3147,7 +3260,7 @@ function resolveWindowsNpmCodexEntry(deps = {}) {
3147
3260
  return null;
3148
3261
  }
3149
3262
  function resolveWindowsCodexDesktopEntry(deps = {}) {
3150
- const existsSyncFn = deps.existsSyncFn ?? existsSync4;
3263
+ const existsSyncFn = deps.existsSyncFn ?? existsSync3;
3151
3264
  const env = deps.env ?? process.env;
3152
3265
  const homeDir = deps.homeDir;
3153
3266
  const winPath = path5.win32;
@@ -3299,11 +3412,15 @@ var CodexDriver = class {
3299
3412
  // the daemon. They replace the previous transcript-mtime heuristic.
3300
3413
  experimentalRawEvents: true
3301
3414
  };
3302
- if (ctx.config.model) {
3303
- threadParams.model = ctx.config.model;
3415
+ const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
3416
+ if (launchRuntimeFields.model) {
3417
+ threadParams.model = launchRuntimeFields.model;
3418
+ }
3419
+ if (launchRuntimeFields.reasoningEffort) {
3420
+ threadParams.config = { model_reasoning_effort: launchRuntimeFields.reasoningEffort };
3304
3421
  }
3305
- if (ctx.config.reasoningEffort) {
3306
- threadParams.config = { model_reasoning_effort: ctx.config.reasoningEffort };
3422
+ if (launchRuntimeFields.mode.kind === "fast") {
3423
+ threadParams.serviceTier = "priority";
3307
3424
  }
3308
3425
  if (ctx.config.sessionId) {
3309
3426
  return {
@@ -3459,12 +3576,12 @@ var CodexDriver = class {
3459
3576
  return detectCodexModels();
3460
3577
  }
3461
3578
  };
3462
- function detectCodexModels(home = os3.homedir()) {
3579
+ function detectCodexModels(home = os2.homedir()) {
3463
3580
  const cachePath = path5.join(home, ".codex", "models_cache.json");
3464
3581
  const configPath = path5.join(home, ".codex", "config.toml");
3465
3582
  let models = [];
3466
3583
  try {
3467
- const raw = readFileSync3(cachePath, "utf8");
3584
+ const raw = readFileSync2(cachePath, "utf8");
3468
3585
  const parsed = JSON.parse(raw);
3469
3586
  const entries = Array.isArray(parsed?.models) ? parsed.models : [];
3470
3587
  for (const entry of entries) {
@@ -3481,7 +3598,7 @@ function detectCodexModels(home = os3.homedir()) {
3481
3598
  if (models.length === 0) return null;
3482
3599
  let defaultModel;
3483
3600
  try {
3484
- const raw = readFileSync3(configPath, "utf8");
3601
+ const raw = readFileSync2(configPath, "utf8");
3485
3602
  const match = raw.match(/^\s*model\s*=\s*"([^"]+)"/m);
3486
3603
  if (match) defaultModel = match[1];
3487
3604
  } catch {
@@ -3512,7 +3629,7 @@ function buildAntigravityArgs(ctx) {
3512
3629
  const args = [
3513
3630
  "--print",
3514
3631
  "--print-timeout",
3515
- ctx.config.envVars?.ANTIGRAVITY_PRINT_TIMEOUT || DEFAULT_PRINT_TIMEOUT,
3632
+ runtimeConfigToLaunchFields(ctx.config).envVars?.ANTIGRAVITY_PRINT_TIMEOUT || DEFAULT_PRINT_TIMEOUT,
3516
3633
  "--dangerously-skip-permissions"
3517
3634
  ];
3518
3635
  if (ctx.config.sessionId) {
@@ -3691,11 +3808,12 @@ var CopilotDriver = class {
3691
3808
  "-p",
3692
3809
  ctx.prompt
3693
3810
  ];
3694
- if (ctx.config.model && ctx.config.model !== "default") {
3695
- args.push("--model", ctx.config.model);
3811
+ const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
3812
+ if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
3813
+ args.push("--model", launchRuntimeFields.model);
3696
3814
  }
3697
- if (ctx.config.reasoningEffort) {
3698
- args.push("--effort", ctx.config.reasoningEffort);
3815
+ if (launchRuntimeFields.reasoningEffort) {
3816
+ args.push("--effort", launchRuntimeFields.reasoningEffort);
3699
3817
  }
3700
3818
  if (ctx.config.sessionId) {
3701
3819
  args.push(`--resume=${ctx.config.sessionId}`);
@@ -3795,7 +3913,7 @@ var CopilotDriver = class {
3795
3913
 
3796
3914
  // src/drivers/cursor.ts
3797
3915
  import { spawn as spawn5, spawnSync } from "child_process";
3798
- import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync5 } from "fs";
3916
+ import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
3799
3917
  import path7 from "path";
3800
3918
  async function buildCursorSpawnEnv(ctx, deps = {}) {
3801
3919
  const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
@@ -3851,7 +3969,7 @@ var CursorDriver = class {
3851
3969
  }
3852
3970
  async spawn(ctx) {
3853
3971
  const cursorDir = path7.join(ctx.workingDirectory, ".cursor");
3854
- if (!existsSync5(cursorDir)) {
3972
+ if (!existsSync4(cursorDir)) {
3855
3973
  mkdirSync2(cursorDir, { recursive: true });
3856
3974
  }
3857
3975
  const mcpConfigPath = path7.join(cursorDir, "mcp.json");
@@ -3864,8 +3982,9 @@ var CursorDriver = class {
3864
3982
  "--approve-mcps",
3865
3983
  "--trust"
3866
3984
  ];
3867
- if (ctx.config.model && ctx.config.model !== "default") {
3868
- args.push("--model", ctx.config.model);
3985
+ const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
3986
+ if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
3987
+ args.push("--model", launchRuntimeFields.model);
3869
3988
  }
3870
3989
  if (ctx.config.sessionId) {
3871
3990
  args.push("--resume", ctx.config.sessionId);
@@ -4000,14 +4119,15 @@ function runCursorModelsCommand() {
4000
4119
 
4001
4120
  // src/drivers/gemini.ts
4002
4121
  import { execFileSync as execFileSync3, spawn as spawn6 } from "child_process";
4003
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
4122
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
4004
4123
  import path8 from "path";
4005
4124
  async function buildGeminiSpawnEnv(ctx, platform = process.platform) {
4006
4125
  const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
4007
- if (!Object.prototype.hasOwnProperty.call(ctx.config.envVars ?? {}, "GEMINI_CLI_TRUST_WORKSPACE")) {
4126
+ const launchEnvVars = runtimeConfigToLaunchFields(ctx.config).envVars;
4127
+ if (!Object.prototype.hasOwnProperty.call(launchEnvVars ?? {}, "GEMINI_CLI_TRUST_WORKSPACE")) {
4008
4128
  spawnEnv.GEMINI_CLI_TRUST_WORKSPACE = "true";
4009
4129
  }
4010
- if (platform === "win32" && !Object.prototype.hasOwnProperty.call(ctx.config.envVars ?? {}, "GEMINI_PTY_INFO")) {
4130
+ if (platform === "win32" && !Object.prototype.hasOwnProperty.call(launchEnvVars ?? {}, "GEMINI_PTY_INFO")) {
4011
4131
  spawnEnv.GEMINI_PTY_INFO = "child_process";
4012
4132
  }
4013
4133
  return spawnEnv;
@@ -4027,8 +4147,9 @@ function buildGeminiArgs(config) {
4027
4147
  "-p",
4028
4148
  ""
4029
4149
  ];
4030
- if (config.model && config.model !== "default") {
4031
- args.push("--model", config.model);
4150
+ const launchRuntimeFields = runtimeConfigToLaunchFields(config);
4151
+ if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
4152
+ args.push("--model", launchRuntimeFields.model);
4032
4153
  }
4033
4154
  if (config.sessionId) {
4034
4155
  args.push("--resume", config.sessionId);
@@ -4041,7 +4162,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
4041
4162
  return { command: resolveCommandOnPath("gemini", deps) ?? "gemini", args: commandArgs };
4042
4163
  }
4043
4164
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
4044
- const existsSyncFn = deps.existsSyncFn ?? existsSync6;
4165
+ const existsSyncFn = deps.existsSyncFn ?? existsSync5;
4045
4166
  const env = deps.env ?? process.env;
4046
4167
  const winPath = path8.win32;
4047
4168
  let geminiEntry = null;
@@ -4214,8 +4335,8 @@ var GeminiDriver = class {
4214
4335
  // src/drivers/kimi.ts
4215
4336
  import { randomUUID as randomUUID2 } from "crypto";
4216
4337
  import { spawn as spawn7 } from "child_process";
4217
- import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync6 } from "fs";
4218
- import os4 from "os";
4338
+ import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
4339
+ import os3 from "os";
4219
4340
  import path9 from "path";
4220
4341
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
4221
4342
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
@@ -4288,7 +4409,7 @@ var KimiDriver = class {
4288
4409
  const systemPromptPath = path9.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
4289
4410
  const agentFilePath = path9.join(ctx.workingDirectory, KIMI_AGENT_FILE);
4290
4411
  const mcpConfigPath = path9.join(ctx.workingDirectory, KIMI_MCP_FILE);
4291
- if (!isResume || !existsSync7(systemPromptPath)) {
4412
+ if (!isResume || !existsSync6(systemPromptPath)) {
4292
4413
  writeFileSync6(systemPromptPath, ctx.prompt, "utf8");
4293
4414
  }
4294
4415
  writeFileSync6(agentFilePath, [
@@ -4316,8 +4437,9 @@ var KimiDriver = class {
4316
4437
  "--session",
4317
4438
  this.sessionId
4318
4439
  ];
4319
- if (ctx.config.model && ctx.config.model !== "default") {
4320
- args.push("--model", ctx.config.model);
4440
+ const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
4441
+ if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
4442
+ args.push("--model", launchRuntimeFields.model);
4321
4443
  }
4322
4444
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
4323
4445
  const launch = resolveKimiSpawn(args);
@@ -4443,11 +4565,11 @@ var KimiDriver = class {
4443
4565
  return detectKimiModels();
4444
4566
  }
4445
4567
  };
4446
- function detectKimiModels(home = os4.homedir()) {
4568
+ function detectKimiModels(home = os3.homedir()) {
4447
4569
  const configPath = path9.join(home, ".kimi", "config.toml");
4448
4570
  let raw;
4449
4571
  try {
4450
- raw = readFileSync4(configPath, "utf8");
4572
+ raw = readFileSync3(configPath, "utf8");
4451
4573
  } catch {
4452
4574
  return null;
4453
4575
  }
@@ -4471,8 +4593,8 @@ function detectKimiModels(home = os4.homedir()) {
4471
4593
 
4472
4594
  // src/drivers/opencode.ts
4473
4595
  import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
4474
- import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
4475
- import os5 from "os";
4596
+ import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
4597
+ import os4 from "os";
4476
4598
  import path10 from "path";
4477
4599
  var CHAT_MCP_SERVER_NAME = "chat";
4478
4600
  var CHAT_MCP_TOOL_PREFIX = `${CHAT_MCP_SERVER_NAME}_`;
@@ -4517,13 +4639,13 @@ function parseOpenCodeConfigContent(raw) {
4517
4639
  return {};
4518
4640
  }
4519
4641
  function parseUserOpenCodeConfig(ctx) {
4520
- const raw = ctx.config.envVars?.OPENCODE_CONFIG_CONTENT;
4642
+ const raw = runtimeConfigToLaunchFields(ctx.config).envVars?.OPENCODE_CONFIG_CONTENT;
4521
4643
  return parseOpenCodeConfigContent(raw);
4522
4644
  }
4523
- function readLocalOpenCodeConfig(home = os5.homedir()) {
4645
+ function readLocalOpenCodeConfig(home = os4.homedir()) {
4524
4646
  const configPath = path10.join(home, ".config", "opencode", "opencode.json");
4525
4647
  try {
4526
- return parseOpenCodeConfigContent(readFileSync5(configPath, "utf8"));
4648
+ return parseOpenCodeConfigContent(readFileSync4(configPath, "utf8"));
4527
4649
  } catch {
4528
4650
  }
4529
4651
  return {};
@@ -4581,7 +4703,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
4581
4703
  }
4582
4704
  };
4583
4705
  }
4584
- function buildOpenCodeConfig(ctx, home = os5.homedir()) {
4706
+ function buildOpenCodeConfig(ctx, home = os4.homedir()) {
4585
4707
  const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
4586
4708
  const userAgents = recordField(userConfig.agent);
4587
4709
  const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
@@ -4606,7 +4728,7 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
4606
4728
  }
4607
4729
  };
4608
4730
  }
4609
- async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = null) {
4731
+ async function buildOpenCodeLaunchOptions(ctx, home = os4.homedir(), version = null) {
4610
4732
  const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
4611
4733
  const config = buildOpenCodeConfig(ctx, home);
4612
4734
  const env = {
@@ -4622,8 +4744,9 @@ async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = n
4622
4744
  "--dir",
4623
4745
  ctx.workingDirectory
4624
4746
  ];
4625
- if (ctx.config.model && ctx.config.model !== "default") {
4626
- args.push("--model", ctx.config.model);
4747
+ const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
4748
+ if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
4749
+ args.push("--model", launchRuntimeFields.model);
4627
4750
  }
4628
4751
  if (requiresAgentCliFlag(version)) {
4629
4752
  args.push("--agent", SLOCK_AGENT_NAME);
@@ -4704,7 +4827,7 @@ function formatOpenCodeLabelToken(token) {
4704
4827
  if (/^\d/.test(token)) return token;
4705
4828
  return normalized.charAt(0).toUpperCase() + normalized.slice(1);
4706
4829
  }
4707
- function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeModelsCommand) {
4830
+ function detectOpenCodeModels(home = os4.homedir(), runCommand = runOpenCodeModelsCommand) {
4708
4831
  const commandResult = runCommand(home);
4709
4832
  if (commandResult.error || commandResult.status !== 0) return null;
4710
4833
  return parseOpenCodeModelsOutput(commandResult.stdout);
@@ -4744,7 +4867,7 @@ function openCodeSpecForEntry(entry, commandArgs) {
4744
4867
  return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
4745
4868
  }
4746
4869
  function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
4747
- const existsSyncFn = deps.existsSyncFn ?? existsSync8;
4870
+ const existsSyncFn = deps.existsSyncFn ?? existsSync7;
4748
4871
  const execFileSyncFn = deps.execFileSyncFn;
4749
4872
  const env = deps.env ?? process.env;
4750
4873
  const winPath = path10.win32;
@@ -4774,7 +4897,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
4774
4897
  }
4775
4898
  function extractWindowsShimTargets(commandPath, deps = {}) {
4776
4899
  if (!isWindowsCommandShim(commandPath)) return [];
4777
- const readFileSyncFn = deps.readFileSyncFn ?? readFileSync5;
4900
+ const readFileSyncFn = deps.readFileSyncFn ?? readFileSync4;
4778
4901
  const commandDir = path10.win32.dirname(commandPath);
4779
4902
  let raw;
4780
4903
  try {
@@ -4908,7 +5031,7 @@ var OpenCodeDriver = class {
4908
5031
  if (unsupportedMessage) {
4909
5032
  throw new Error(unsupportedMessage);
4910
5033
  }
4911
- const launch = await buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
5034
+ const launch = await buildOpenCodeLaunchOptions(ctx, os4.homedir(), version);
4912
5035
  const spawnSpec = resolveOpenCodeSpawn(launch.args);
4913
5036
  const proc = spawn8(spawnSpec.command, spawnSpec.args, {
4914
5037
  cwd: ctx.workingDirectory,
@@ -5500,7 +5623,7 @@ function findSessionJsonl(root, predicate) {
5500
5623
  if (depth < 0 || visited >= maxEntries) return null;
5501
5624
  let entries;
5502
5625
  try {
5503
- entries = readdirSync2(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
5626
+ entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
5504
5627
  } catch {
5505
5628
  return null;
5506
5629
  }
@@ -5546,11 +5669,11 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
5546
5669
  return null;
5547
5670
  }
5548
5671
  }
5549
- function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
5672
+ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
5550
5673
  const directPath = path12.isAbsolute(sessionId) ? sessionId : null;
5551
5674
  if (directPath) {
5552
5675
  try {
5553
- if (statSync2(directPath).isFile()) {
5676
+ if (statSync(directPath).isFile()) {
5554
5677
  return { label: sessionId, path: directPath, runtime, reachable: true };
5555
5678
  }
5556
5679
  } catch {
@@ -6504,7 +6627,7 @@ var AgentProcessManager = class _AgentProcessManager {
6504
6627
  this.daemonApiKey = daemonApiKey;
6505
6628
  this.serverUrl = opts.serverUrl;
6506
6629
  this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
6507
- this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os6.homedir();
6630
+ this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os5.homedir();
6508
6631
  this.driverResolver = opts.driverResolver || getDriver;
6509
6632
  this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
6510
6633
  this.tracer = opts.tracer ?? noopTracer;
@@ -7388,7 +7511,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
7388
7511
  "X-Slock-Client": "daemon-server-session-worker"
7389
7512
  },
7390
7513
  body: JSON.stringify({
7391
- scopes: ["send", "read", "mentions", "tasks", "reactions", "server", "channels"],
7514
+ scopes: ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"],
7392
7515
  name: `runner:${config.runtime}:${agentId.slice(0, 8)}`
7393
7516
  })
7394
7517
  });
@@ -8199,7 +8322,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8199
8322
  async listSkills(agentId, runtimeHint) {
8200
8323
  const agent = this.agents.get(agentId);
8201
8324
  const runtime = runtimeHint || agent?.config.runtime || "claude";
8202
- const home = os6.homedir();
8325
+ const home = os5.homedir();
8203
8326
  const workspaceDir = path12.join(this.dataDir, agentId);
8204
8327
  const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
8205
8328
  const globalResults = await Promise.all(
@@ -9626,8 +9749,8 @@ var ReminderCache = class {
9626
9749
 
9627
9750
  // src/machineLock.ts
9628
9751
  import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
9629
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync6, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync8 } from "fs";
9630
- import os7 from "os";
9752
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
9753
+ import os6 from "os";
9631
9754
  import path13 from "path";
9632
9755
  var INCOMPLETE_LOCK_STALE_MS = 3e4;
9633
9756
  var DaemonMachineLockConflictError = class extends Error {
@@ -9654,14 +9777,14 @@ function ownerPath(lockDir) {
9654
9777
  }
9655
9778
  function readOwner(lockDir) {
9656
9779
  try {
9657
- return JSON.parse(readFileSync6(ownerPath(lockDir), "utf8"));
9780
+ return JSON.parse(readFileSync5(ownerPath(lockDir), "utf8"));
9658
9781
  } catch {
9659
9782
  return null;
9660
9783
  }
9661
9784
  }
9662
9785
  function lockAgeMs(lockDir) {
9663
9786
  try {
9664
- return Date.now() - statSync3(lockDir).mtimeMs;
9787
+ return Date.now() - statSync2(lockDir).mtimeMs;
9665
9788
  } catch {
9666
9789
  return null;
9667
9790
  }
@@ -9690,7 +9813,7 @@ function acquireDaemonMachineLock(options) {
9690
9813
  const owner = {
9691
9814
  pid: process.pid,
9692
9815
  token,
9693
- hostname: os7.hostname(),
9816
+ hostname: os6.hostname(),
9694
9817
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
9695
9818
  serverUrl: options.serverUrl,
9696
9819
  apiKeyFingerprint: fingerprint.slice(0, 16)
@@ -9733,7 +9856,7 @@ function acquireDaemonMachineLock(options) {
9733
9856
  }
9734
9857
 
9735
9858
  // src/localTraceSink.ts
9736
- import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync3, rmSync as rmSync4, statSync as statSync4, writeFileSync as writeFileSync9 } from "fs";
9859
+ import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
9737
9860
  import path14 from "path";
9738
9861
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
9739
9862
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
@@ -9805,13 +9928,13 @@ var LocalRotatingTraceSink = class {
9805
9928
  `daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
9806
9929
  );
9807
9930
  writeFileSync9(this.currentFile, "", { flag: "a", mode: 384 });
9808
- this.currentSize = statSync4(this.currentFile).size;
9931
+ this.currentSize = statSync3(this.currentFile).size;
9809
9932
  this.currentFileOpenedAtMs = nowMs;
9810
9933
  this.pruneOldFiles();
9811
9934
  }
9812
9935
  }
9813
9936
  pruneOldFiles() {
9814
- const files = readdirSync3(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
9937
+ const files = readdirSync2(this.traceDir).filter((name) => name.startsWith("daemon-trace-") && name.endsWith(".jsonl")).sort();
9815
9938
  const excess = files.length - this.maxFiles;
9816
9939
  if (excess <= 0) return;
9817
9940
  for (const file of files.slice(0, excess)) {
@@ -10246,7 +10369,7 @@ function readPositiveIntegerEnv2(name, fallback) {
10246
10369
 
10247
10370
  // src/core.ts
10248
10371
  var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
10249
- var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels"];
10372
+ var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
10250
10373
  var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
10251
10374
  var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
10252
10375
  var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
@@ -10947,8 +11070,8 @@ var DaemonCore = class {
10947
11070
  capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
10948
11071
  runtimes,
10949
11072
  runningAgents: runningAgentIds,
10950
- hostname: this.options.hostname ?? os8.hostname(),
10951
- os: this.options.osDescription ?? `${os8.platform()} ${os8.arch()}`,
11073
+ hostname: this.options.hostname ?? os7.hostname(),
11074
+ os: this.options.osDescription ?? `${os7.platform()} ${os7.arch()}`,
10952
11075
  daemonVersion: this.daemonVersion
10953
11076
  });
10954
11077
  this.recordDaemonTrace("daemon.ready.sent", {