@slock-ai/daemon 0.54.2 → 0.55.0-play.20260529181703

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