@slock-ai/daemon 0.54.2-alpha.0 → 0.54.2-play.20260528162546

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