@slock-ai/daemon 0.49.0 → 0.50.0-play.20260518104853

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.
@@ -7,11 +7,11 @@ import {
7
7
  } from "./chunk-KNMCE6WB.js";
8
8
 
9
9
  // src/core.ts
10
- import path16 from "path";
10
+ import path17 from "path";
11
11
  import os8 from "os";
12
12
  import { createRequire } from "module";
13
13
  import { accessSync } from "fs";
14
- import { fileURLToPath } from "url";
14
+ import { fileURLToPath as fileURLToPath2 } from "url";
15
15
 
16
16
  // ../shared/src/tracing/index.ts
17
17
  var DEFAULT_TRACE_FLAGS = "00";
@@ -723,6 +723,7 @@ var SERVER_CAPABILITY_MATRIX = {
723
723
  var RUNTIMES = [
724
724
  { id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
725
725
  { id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
726
+ { id: "pi", displayName: "Pi", binary: "pi", supported: true },
726
727
  { id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
727
728
  { id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
728
729
  { id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
@@ -764,10 +765,10 @@ var DISPLAY_PLAN_CONFIG = {
764
765
  };
765
766
 
766
767
  // src/agentProcessManager.ts
767
- import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
768
+ import { mkdirSync as mkdirSync5, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
768
769
  import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
769
770
  import { createHash as createHash2 } from "crypto";
770
- import path12 from "path";
771
+ import path13 from "path";
771
772
  import os6 from "os";
772
773
 
773
774
  // src/drivers/claude.ts
@@ -871,13 +872,15 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
871
872
  17. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
872
873
  18. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
873
874
  19. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--display-name <name>\`, and \`--description <text>\`. Values must be non-empty. Provide at least one flag per call; multiple flags can be combined.
874
- 20. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
875
- 21. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
876
- 22. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
877
- 23. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
878
- 24. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
879
- 25. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
880
- 26. **\`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\`).
875
+ 20. **\`slock integration list\`** \u2014 List registered third-party services and this agent's active Slock Agent Logins.
876
+ 21. **\`slock integration login\`** \u2014 Provision or reuse this agent's login for a registered third-party service.
877
+ 22. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
878
+ 23. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
879
+ 24. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
880
+ 25. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
881
+ 26. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
882
+ 27. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
883
+ 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\`).
881
884
 
882
885
  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:
883
886
  - failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
@@ -978,6 +981,11 @@ Each channel has a **name** and optionally a **description** that define its pur
978
981
  - **Reply in context** \u2014 always respond in the channel/thread the message came from.
979
982
  - **Stay on topic** \u2014 when proactively sharing results or updates, post in the channel most relevant to the work. Don't scatter messages across unrelated channels.
980
983
  - If unsure where something belongs, call ${serverInfoCmd} to review channel descriptions.`;
984
+ const thirdPartyIntegrationsSection = isCli ? `### Third-party integrations
985
+
986
+ 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. 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, 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
987
+
988
+ 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.`;
981
989
  const readingHistorySection = isCli ? `### Reading history
982
990
 
983
991
  \`slock message read --channel "#channel-name"\` or \`slock message read --channel dm:@peer-name\` or \`slock message read --channel "#channel:shortid"\`
@@ -1149,6 +1157,8 @@ ${discoverySection}
1149
1157
 
1150
1158
  ${channelAwarenessSection}
1151
1159
 
1160
+ ${thirdPartyIntegrationsSection}
1161
+
1152
1162
  ${readingHistorySection}
1153
1163
 
1154
1164
  ${historicalReferenceSection}
@@ -1180,6 +1190,17 @@ Keep the user informed. They cannot see your internal reasoning, so:
1180
1190
  - For multi-step work, send short progress updates (e.g. "Working on step 2/3\u2026").
1181
1191
  - When done, summarize the result.
1182
1192
  - Keep updates concise \u2014 one or two sentences. Don't flood the chat.
1193
+ - For long answers where users need the conclusion first but details still matter, put the conclusion and next action outside any collapse, then use sanitized HTML details blocks for optional depth:
1194
+
1195
+ \`\`\`html
1196
+ <details>
1197
+ <summary>Evidence, logs, or edge cases</summary>
1198
+
1199
+ Detailed notes go here.
1200
+ </details>
1201
+ \`\`\`
1202
+
1203
+ Do not hide the main recommendation, blocker, or required action inside \`<details>\`; only fold supporting evidence, logs, alternatives, or extended rationale.
1183
1204
 
1184
1205
  ### Conversation etiquette
1185
1206
 
@@ -1349,6 +1370,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
1349
1370
  return candidates.filter((candidate) => existsSync(candidate.path));
1350
1371
  }
1351
1372
 
1373
+ // src/authEnv.ts
1374
+ var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
1375
+ var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
1376
+ function scrubDaemonAuthEnv(env) {
1377
+ delete env[DAEMON_API_KEY_ENV];
1378
+ return env;
1379
+ }
1380
+ function scrubDaemonChildEnv(env) {
1381
+ delete env[DAEMON_API_KEY_ENV];
1382
+ delete env[SLOCK_AGENT_TOKEN_ENV];
1383
+ return env;
1384
+ }
1385
+
1352
1386
  // src/drivers/cliTransport.ts
1353
1387
  var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
1354
1388
  function runtimeContextEnv(config) {
@@ -1402,7 +1436,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)}
1402
1436
  SLOCK_AGENT_TOKEN_FILE: tokenFile,
1403
1437
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
1404
1438
  };
1405
- delete spawnEnv.SLOCK_AGENT_TOKEN;
1439
+ scrubDaemonChildEnv(spawnEnv);
1406
1440
  return {
1407
1441
  slockDir,
1408
1442
  tokenFile,
@@ -1439,7 +1473,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn) {
1439
1473
  }
1440
1474
  function resolveCommandOnPath(command, deps = {}) {
1441
1475
  const platform = deps.platform ?? process.platform;
1442
- const env = deps.env ?? process.env;
1476
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
1443
1477
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
1444
1478
  if (platform === "win32") {
1445
1479
  return resolveCommandOnWindows(command, env, execFileSyncFn);
@@ -1464,7 +1498,7 @@ function firstExistingPath(candidates, deps = {}) {
1464
1498
  return null;
1465
1499
  }
1466
1500
  function readCommandVersion(command, args = [], deps = {}) {
1467
- const env = deps.env ?? process.env;
1501
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
1468
1502
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
1469
1503
  try {
1470
1504
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -2741,7 +2775,7 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
2741
2775
  }
2742
2776
  function runCursorModelsCommand() {
2743
2777
  return spawnSync("cursor-agent", ["models"], {
2744
- env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
2778
+ env: scrubDaemonChildEnv({ ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" }),
2745
2779
  encoding: "utf8",
2746
2780
  timeout: 5e3
2747
2781
  });
@@ -2788,7 +2822,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
2788
2822
  }
2789
2823
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
2790
2824
  const existsSyncFn = deps.existsSyncFn ?? existsSync6;
2791
- const env = deps.env ?? process.env;
2825
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
2792
2826
  const winPath = path8.win32;
2793
2827
  let geminiEntry = null;
2794
2828
  try {
@@ -2962,13 +2996,16 @@ var GeminiDriver = class {
2962
2996
  // src/drivers/kimi.ts
2963
2997
  import { randomUUID } from "crypto";
2964
2998
  import { spawn as spawn6 } from "child_process";
2965
- import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
2999
+ import { chmodSync, existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
2966
3000
  import os4 from "os";
2967
3001
  import path9 from "path";
2968
3002
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
2969
3003
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
2970
3004
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
2971
3005
  var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
3006
+ var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
3007
+ var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
3008
+ var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
2972
3009
  function parseToolArguments(raw) {
2973
3010
  if (typeof raw !== "string") return raw;
2974
3011
  try {
@@ -2977,6 +3014,80 @@ function parseToolArguments(raw) {
2977
3014
  return raw;
2978
3015
  }
2979
3016
  }
3017
+ function readKimiConfigSource(home = os4.homedir(), env = process.env) {
3018
+ const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
3019
+ if (inlineConfig && inlineConfig.trim()) {
3020
+ return {
3021
+ raw: inlineConfig,
3022
+ explicitPath: null,
3023
+ sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
3024
+ };
3025
+ }
3026
+ const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
3027
+ const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
3028
+ try {
3029
+ return {
3030
+ raw: readFileSync3(configPath, "utf8"),
3031
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
3032
+ sourcePath: configPath
3033
+ };
3034
+ } catch {
3035
+ return {
3036
+ raw: null,
3037
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
3038
+ sourcePath: configPath
3039
+ };
3040
+ }
3041
+ }
3042
+ function buildKimiSpawnEnv(env = process.env) {
3043
+ const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
3044
+ delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
3045
+ delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
3046
+ return scrubDaemonChildEnv(spawnEnv);
3047
+ }
3048
+ function buildKimiEffectiveEnv(ctx, overrideEnv) {
3049
+ return {
3050
+ ...process.env,
3051
+ ...ctx.config.envVars || {},
3052
+ ...overrideEnv || {}
3053
+ };
3054
+ }
3055
+ function buildKimiLaunchOptions(ctx, opts = {}) {
3056
+ const env = buildKimiEffectiveEnv(ctx, opts.env);
3057
+ const source = readKimiConfigSource(opts.home ?? os4.homedir(), env);
3058
+ const args = [];
3059
+ let configFilePath = null;
3060
+ let configContent = null;
3061
+ if (source.explicitPath) {
3062
+ configFilePath = source.explicitPath;
3063
+ } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
3064
+ configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
3065
+ configContent = source.raw;
3066
+ if (opts.writeGeneratedConfig !== false) {
3067
+ writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
3068
+ chmodSync(configFilePath, 384);
3069
+ }
3070
+ }
3071
+ if (configFilePath) {
3072
+ args.push("--config-file", configFilePath);
3073
+ }
3074
+ if (ctx.config.model && ctx.config.model !== "default") {
3075
+ args.push("--model", ctx.config.model);
3076
+ }
3077
+ return {
3078
+ args,
3079
+ env: buildKimiSpawnEnv(env),
3080
+ configFilePath,
3081
+ configContent
3082
+ };
3083
+ }
3084
+ function resolveKimiSpawn(commandArgs, deps = {}) {
3085
+ return {
3086
+ command: resolveCommandOnPath("kimi", deps) ?? "kimi",
3087
+ args: commandArgs,
3088
+ shell: false
3089
+ };
3090
+ }
2980
3091
  var KimiDriver = class {
2981
3092
  id = "kimi";
2982
3093
  lifecycle = {
@@ -2993,7 +3104,25 @@ var KimiDriver = class {
2993
3104
  };
2994
3105
  model = {
2995
3106
  detectedModelsVerifiedAs: "launchable",
2996
- toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
3107
+ toLaunchSpec: (modelId, ctx, opts) => {
3108
+ if (!ctx) return { args: ["--model", modelId] };
3109
+ const launchCtx = {
3110
+ ...ctx,
3111
+ config: {
3112
+ ...ctx.config,
3113
+ model: modelId
3114
+ }
3115
+ };
3116
+ const launch = buildKimiLaunchOptions(launchCtx, {
3117
+ home: opts?.home,
3118
+ writeGeneratedConfig: false
3119
+ });
3120
+ return {
3121
+ args: launch.args,
3122
+ env: launch.env,
3123
+ configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
3124
+ };
3125
+ }
2997
3126
  };
2998
3127
  supportsStdinNotification = true;
2999
3128
  mcpToolPrefix = "";
@@ -3047,6 +3176,7 @@ var KimiDriver = class {
3047
3176
  }
3048
3177
  }
3049
3178
  }), "utf8");
3179
+ const launch = buildKimiLaunchOptions(ctx);
3050
3180
  const args = [
3051
3181
  "--wire",
3052
3182
  "--yolo",
@@ -3055,17 +3185,23 @@ var KimiDriver = class {
3055
3185
  "--mcp-config-file",
3056
3186
  mcpConfigPath,
3057
3187
  "--session",
3058
- this.sessionId
3188
+ this.sessionId,
3189
+ ...launch.args
3059
3190
  ];
3060
3191
  if (ctx.config.model && ctx.config.model !== "default") {
3061
3192
  args.push("--model", ctx.config.model);
3062
3193
  }
3063
3194
  const spawnEnv = prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
3064
- const proc = spawn6("kimi", args, {
3195
+ const spawnTarget = resolveKimiSpawn(args);
3196
+ const proc = spawn6(spawnTarget.command, spawnTarget.args, {
3065
3197
  cwd: ctx.workingDirectory,
3066
3198
  stdio: ["pipe", "pipe", "pipe"],
3067
3199
  env: spawnEnv,
3068
- shell: process.platform === "win32"
3200
+ // Windows cmd.exe defaults to the system code page (often CP936/GBK)
3201
+ // and has an 8191-character command-line limit. Kimi's official
3202
+ // installer/uv entrypoint is an executable, so launch it directly and
3203
+ // keep prompts on stdin / files instead of routing through cmd.exe.
3204
+ shell: spawnTarget.shell
3069
3205
  });
3070
3206
  proc.stdin?.write(JSON.stringify({
3071
3207
  jsonrpc: "2.0",
@@ -3181,14 +3317,9 @@ var KimiDriver = class {
3181
3317
  return detectKimiModels();
3182
3318
  }
3183
3319
  };
3184
- function detectKimiModels(home = os4.homedir()) {
3185
- const configPath = path9.join(home, ".kimi", "config.toml");
3186
- let raw;
3187
- try {
3188
- raw = readFileSync3(configPath, "utf8");
3189
- } catch {
3190
- return null;
3191
- }
3320
+ function detectKimiModels(home = os4.homedir(), opts = {}) {
3321
+ const raw = readKimiConfigSource(home, opts.env).raw;
3322
+ if (raw === null) return null;
3192
3323
  const models = [];
3193
3324
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
3194
3325
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -3285,6 +3416,18 @@ function isSupportedOpenCodeVersion(version) {
3285
3416
  }
3286
3417
  return true;
3287
3418
  }
3419
+ var AGENT_FLAG_CUTOFF_VERSION = "1.15.0";
3420
+ function requiresAgentCliFlag(version) {
3421
+ if (!version) return true;
3422
+ const actual = parseSemver(version);
3423
+ const cutoff = parseSemver(AGENT_FLAG_CUTOFF_VERSION);
3424
+ if (!actual || !cutoff) return true;
3425
+ for (let i = 0; i < 3; i += 1) {
3426
+ if (actual[i] > cutoff[i]) return false;
3427
+ if (actual[i] < cutoff[i]) return true;
3428
+ }
3429
+ return false;
3430
+ }
3288
3431
  function unsupportedOpenCodeVersionMessage(version) {
3289
3432
  if (!version || isSupportedOpenCodeVersion(version)) return null;
3290
3433
  return `OpenCode CLI ${version} is unsupported; requires OpenCode >= ${MIN_SUPPORTED_OPENCODE_VERSION}. Upgrade opencode before starting this runtime.`;
@@ -3332,7 +3475,7 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
3332
3475
  }
3333
3476
  };
3334
3477
  }
3335
- function buildOpenCodeLaunchOptions(ctx, home = os5.homedir()) {
3478
+ function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = null) {
3336
3479
  const slock = prepareCliTransport(ctx, { NO_COLOR: "1" });
3337
3480
  const config = buildOpenCodeConfig(ctx, home);
3338
3481
  const env = {
@@ -3351,7 +3494,9 @@ function buildOpenCodeLaunchOptions(ctx, home = os5.homedir()) {
3351
3494
  if (ctx.config.model && ctx.config.model !== "default") {
3352
3495
  args.push("--model", ctx.config.model);
3353
3496
  }
3354
- args.push("--agent", SLOCK_AGENT_NAME);
3497
+ if (requiresAgentCliFlag(version)) {
3498
+ args.push("--agent", SLOCK_AGENT_NAME);
3499
+ }
3355
3500
  if (ctx.config.sessionId) {
3356
3501
  args.push("--session", ctx.config.sessionId);
3357
3502
  }
@@ -3435,7 +3580,7 @@ function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeMode
3435
3580
  }
3436
3581
  function runOpenCodeModelsCommand(home) {
3437
3582
  const result = spawnSync2("opencode", ["models"], {
3438
- env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
3583
+ env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
3439
3584
  encoding: "utf8",
3440
3585
  timeout: 5e3
3441
3586
  });
@@ -3487,7 +3632,8 @@ var OpenCodeDriver = class {
3487
3632
  model: modelId
3488
3633
  }
3489
3634
  };
3490
- const launch = buildOpenCodeLaunchOptions(launchCtx, opts?.home);
3635
+ const version = readCommandVersion("opencode");
3636
+ const launch = buildOpenCodeLaunchOptions(launchCtx, opts?.home, version);
3491
3637
  return {
3492
3638
  args: launch.args,
3493
3639
  env: launch.env,
@@ -3524,11 +3670,12 @@ var OpenCodeDriver = class {
3524
3670
  spawn(ctx) {
3525
3671
  this.sessionId = ctx.config.sessionId || null;
3526
3672
  this.sessionAnnounced = false;
3527
- const unsupportedMessage = unsupportedOpenCodeVersionMessage(readCommandVersion("opencode"));
3673
+ const version = readCommandVersion("opencode");
3674
+ const unsupportedMessage = unsupportedOpenCodeVersionMessage(version);
3528
3675
  if (unsupportedMessage) {
3529
3676
  throw new Error(unsupportedMessage);
3530
3677
  }
3531
- const launch = buildOpenCodeLaunchOptions(ctx);
3678
+ const launch = buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
3532
3679
  const proc = spawn7("opencode", launch.args, {
3533
3680
  cwd: ctx.workingDirectory,
3534
3681
  stdio: ["pipe", "pipe", "pipe"],
@@ -3591,6 +3738,297 @@ var OpenCodeDriver = class {
3591
3738
  }
3592
3739
  };
3593
3740
 
3741
+ // src/drivers/pi.ts
3742
+ import { spawn as spawn8 } from "child_process";
3743
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
3744
+ import path11 from "path";
3745
+ import { fileURLToPath } from "url";
3746
+ import { getAgentDir, VERSION as PI_SDK_VERSION } from "@earendil-works/pi-coding-agent";
3747
+ var CHAT_MCP_TOOL_PREFIX2 = "chat_";
3748
+ var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
3749
+ var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
3750
+ var MIN_SUPPORTED_PI_VERSION = "0.74.0";
3751
+ function parseSemver2(version) {
3752
+ const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
3753
+ if (!match) return null;
3754
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
3755
+ }
3756
+ function isSupportedPiVersion(version) {
3757
+ if (!version) return true;
3758
+ const actual = parseSemver2(version);
3759
+ const minimum = parseSemver2(MIN_SUPPORTED_PI_VERSION);
3760
+ if (!actual || !minimum) return true;
3761
+ for (let i = 0; i < 3; i += 1) {
3762
+ if (actual[i] > minimum[i]) return true;
3763
+ if (actual[i] < minimum[i]) return false;
3764
+ }
3765
+ return true;
3766
+ }
3767
+ function unsupportedPiVersionMessage(version) {
3768
+ if (!version || isSupportedPiVersion(version)) return null;
3769
+ 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.`;
3770
+ }
3771
+ function probePi(version = PI_SDK_VERSION) {
3772
+ const unsupportedMessage = unsupportedPiVersionMessage(version);
3773
+ if (unsupportedMessage) {
3774
+ return {
3775
+ available: false,
3776
+ version: `${version} (requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION})`
3777
+ };
3778
+ }
3779
+ return { available: true, version };
3780
+ }
3781
+ function resolvePiSdkRunnerPath(moduleUrl = import.meta.url) {
3782
+ const moduleDir = path11.dirname(fileURLToPath(moduleUrl));
3783
+ const sourceSibling = path11.join(moduleDir, "piSdkRunner.ts");
3784
+ if (existsSync8(sourceSibling)) return sourceSibling;
3785
+ const bundledEntry = path11.join(moduleDir, "drivers", "piSdkRunner.js");
3786
+ if (existsSync8(bundledEntry)) return bundledEntry;
3787
+ return path11.join(moduleDir, "piSdkRunner.js");
3788
+ }
3789
+ function buildPiSdkNodeArgs(runnerPath = resolvePiSdkRunnerPath()) {
3790
+ if (runnerPath.endsWith(".ts")) {
3791
+ return [...process.execArgv, runnerPath];
3792
+ }
3793
+ return [runnerPath];
3794
+ }
3795
+ function buildPiLaunchOptions(ctx, opts = {}) {
3796
+ const command = opts.command ?? process.execPath;
3797
+ const piDir = path11.join(ctx.workingDirectory, ".slock", "pi");
3798
+ const sessionDir = path11.join(piDir, "sessions");
3799
+ mkdirSync4(sessionDir, { recursive: true });
3800
+ const slock = prepareCliTransport(ctx, { NO_COLOR: "1" });
3801
+ const runnerPath = opts.runnerPath ?? resolvePiSdkRunnerPath();
3802
+ const agentDir = opts.agentDir ?? getAgentDir();
3803
+ const runnerConfigPath = path11.join(
3804
+ piDir,
3805
+ `sdk-run-${(ctx.launchId || "launch").replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`
3806
+ );
3807
+ const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
3808
+ const runnerConfig = {
3809
+ cwd: ctx.workingDirectory,
3810
+ agentDir,
3811
+ sessionDir,
3812
+ sessionId: ctx.config.sessionId || null,
3813
+ standingPrompt: ctx.standingPrompt,
3814
+ prompt: turnPrompt,
3815
+ model: ctx.config.model && ctx.config.model !== "default" ? ctx.config.model : null
3816
+ };
3817
+ writeFileSync7(runnerConfigPath, `${JSON.stringify(runnerConfig)}
3818
+ `, { encoding: "utf8", mode: 384 });
3819
+ const args = [
3820
+ ...buildPiSdkNodeArgs(runnerPath),
3821
+ "--config",
3822
+ runnerConfigPath
3823
+ ];
3824
+ return {
3825
+ command,
3826
+ args,
3827
+ env: slock.spawnEnv,
3828
+ sessionDir,
3829
+ agentDir,
3830
+ runnerConfigPath,
3831
+ sdkVersion: PI_SDK_VERSION
3832
+ };
3833
+ }
3834
+ function isSystemFirstMessageTask2(message) {
3835
+ return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
3836
+ }
3837
+ function buildPiSystemPrompt(config) {
3838
+ return buildCliTransportSystemPrompt(config, {
3839
+ toolPrefix: CHAT_MCP_TOOL_PREFIX2,
3840
+ extraCriticalRules: [
3841
+ "- 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."
3842
+ ],
3843
+ postStartupNotes: [
3844
+ "**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."
3845
+ ],
3846
+ includeStdinNotificationSection: false,
3847
+ messageNotificationStyle: "poll"
3848
+ });
3849
+ }
3850
+ function contentText(content) {
3851
+ if (!content) return "";
3852
+ const chunks = [];
3853
+ for (const item of content) {
3854
+ if (item.type === "text" && typeof item.text === "string") {
3855
+ chunks.push(item.text);
3856
+ }
3857
+ }
3858
+ return chunks.join("");
3859
+ }
3860
+ function apiKeyErrorMessage(line) {
3861
+ const trimmed = line.trim();
3862
+ if (!trimmed) return null;
3863
+ if (/no api key found/i.test(trimmed)) return trimmed;
3864
+ if (/api key.+required/i.test(trimmed)) return trimmed;
3865
+ if (/no models available/i.test(trimmed)) return trimmed;
3866
+ return null;
3867
+ }
3868
+ var PiDriver = class {
3869
+ id = "pi";
3870
+ lifecycle = {
3871
+ kind: "per_turn",
3872
+ start: "defer_until_concrete_message",
3873
+ exit: "terminate_on_turn_end",
3874
+ inFlightWake: "coalesce_into_pending"
3875
+ };
3876
+ communication = {
3877
+ chat: "slock_cli",
3878
+ runtimeControl: "none"
3879
+ };
3880
+ session = {
3881
+ recovery: "resume_or_fresh"
3882
+ };
3883
+ model = {
3884
+ detectedModelsVerifiedAs: "launchable",
3885
+ toLaunchSpec: (modelId, ctx) => {
3886
+ if (!ctx) return modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] };
3887
+ const launchCtx = {
3888
+ ...ctx,
3889
+ config: {
3890
+ ...ctx.config,
3891
+ model: modelId
3892
+ }
3893
+ };
3894
+ const launch = buildPiLaunchOptions(launchCtx);
3895
+ return {
3896
+ args: launch.args,
3897
+ env: launch.env,
3898
+ configFiles: [launch.runnerConfigPath],
3899
+ params: {
3900
+ agentDir: launch.agentDir,
3901
+ sessionDir: launch.sessionDir,
3902
+ sdkVersion: launch.sdkVersion,
3903
+ resources: "extensions/skills/prompt-templates/themes/context-files disabled by Slock policy"
3904
+ }
3905
+ };
3906
+ }
3907
+ };
3908
+ supportsStdinNotification = false;
3909
+ mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
3910
+ busyDeliveryMode = "none";
3911
+ terminateProcessOnTurnEnd = true;
3912
+ deferSpawnUntilMessage = true;
3913
+ usesSlockCliForCommunication = true;
3914
+ sessionId = null;
3915
+ sessionAnnounced = false;
3916
+ apiKeyErrorAnnounced = false;
3917
+ turnEnded = false;
3918
+ assistantTextByMessageId = /* @__PURE__ */ new Map();
3919
+ shouldDeferWakeMessage(message) {
3920
+ return isSystemFirstMessageTask2(message);
3921
+ }
3922
+ probe() {
3923
+ return probePi();
3924
+ }
3925
+ async detectModels() {
3926
+ return null;
3927
+ }
3928
+ spawn(ctx) {
3929
+ this.sessionId = ctx.config.sessionId || null;
3930
+ this.sessionAnnounced = false;
3931
+ this.apiKeyErrorAnnounced = false;
3932
+ this.turnEnded = false;
3933
+ this.assistantTextByMessageId.clear();
3934
+ const unsupportedMessage = unsupportedPiVersionMessage(PI_SDK_VERSION);
3935
+ if (unsupportedMessage) throw new Error(unsupportedMessage);
3936
+ const launch = buildPiLaunchOptions(ctx);
3937
+ const proc = spawn8(launch.command, launch.args, {
3938
+ cwd: ctx.workingDirectory,
3939
+ stdio: ["pipe", "pipe", "pipe"],
3940
+ env: launch.env,
3941
+ shell: false
3942
+ });
3943
+ proc.stdin?.end();
3944
+ return { process: proc };
3945
+ }
3946
+ parseLine(line) {
3947
+ let event;
3948
+ try {
3949
+ event = JSON.parse(line);
3950
+ } catch {
3951
+ if (this.apiKeyErrorAnnounced) return [];
3952
+ const message = apiKeyErrorMessage(line);
3953
+ if (!message) return [];
3954
+ this.apiKeyErrorAnnounced = true;
3955
+ this.turnEnded = true;
3956
+ return [
3957
+ { kind: "error", message },
3958
+ { kind: "turn_end", sessionId: this.sessionId || void 0 }
3959
+ ];
3960
+ }
3961
+ const events = [];
3962
+ if (event.type === "session" && event.id) {
3963
+ this.sessionId = event.id;
3964
+ }
3965
+ if (!this.sessionAnnounced && this.sessionId) {
3966
+ events.push({ kind: "session_init", sessionId: this.sessionId });
3967
+ this.sessionAnnounced = true;
3968
+ }
3969
+ switch (event.type) {
3970
+ case "agent_start":
3971
+ case "turn_start":
3972
+ events.push({ kind: "thinking", text: "" });
3973
+ break;
3974
+ case "message_update":
3975
+ case "message_end":
3976
+ if (event.message?.role === "assistant") {
3977
+ const key = event.message.id || "current";
3978
+ const currentText = contentText(event.message.content);
3979
+ const previousText = this.assistantTextByMessageId.get(key) ?? "";
3980
+ if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
3981
+ events.push({ kind: "text", text: currentText.slice(previousText.length) });
3982
+ } else if (currentText && currentText !== previousText) {
3983
+ events.push({ kind: "text", text: currentText });
3984
+ }
3985
+ this.assistantTextByMessageId.set(key, currentText);
3986
+ if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
3987
+ events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
3988
+ }
3989
+ }
3990
+ break;
3991
+ case "tool_execution_start":
3992
+ events.push({
3993
+ kind: "tool_call",
3994
+ name: event.toolName || "unknown_tool",
3995
+ input: event.args
3996
+ });
3997
+ break;
3998
+ case "tool_execution_end":
3999
+ events.push({
4000
+ kind: "tool_output",
4001
+ name: event.toolName || "unknown_tool"
4002
+ });
4003
+ if (event.isError) {
4004
+ events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
4005
+ }
4006
+ break;
4007
+ case "compaction_start":
4008
+ events.push({ kind: "compaction_started" });
4009
+ break;
4010
+ case "compaction_end":
4011
+ events.push({ kind: "compaction_finished" });
4012
+ if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
4013
+ break;
4014
+ case "turn_end":
4015
+ case "agent_end":
4016
+ if (!this.turnEnded) {
4017
+ events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
4018
+ this.turnEnded = true;
4019
+ }
4020
+ break;
4021
+ }
4022
+ return events;
4023
+ }
4024
+ encodeStdinMessage(_text, _sessionId, _opts) {
4025
+ return null;
4026
+ }
4027
+ buildSystemPrompt(config, _agentId) {
4028
+ return buildPiSystemPrompt(config);
4029
+ }
4030
+ };
4031
+
3594
4032
  // src/drivers/index.ts
3595
4033
  var driverFactories = {
3596
4034
  claude: () => new ClaudeDriver(),
@@ -3599,7 +4037,8 @@ var driverFactories = {
3599
4037
  cursor: () => new CursorDriver(),
3600
4038
  gemini: () => new GeminiDriver(),
3601
4039
  kimi: () => new KimiDriver(),
3602
- opencode: () => new OpenCodeDriver()
4040
+ opencode: () => new OpenCodeDriver(),
4041
+ pi: () => new PiDriver()
3603
4042
  };
3604
4043
  function getDriver(runtimeId) {
3605
4044
  const createDriver = driverFactories[runtimeId];
@@ -3612,7 +4051,7 @@ function getDriver(runtimeId) {
3612
4051
 
3613
4052
  // src/workspaces.ts
3614
4053
  import { readdir, rm, stat } from "fs/promises";
3615
- import path11 from "path";
4054
+ import path12 from "path";
3616
4055
  function isValidWorkspaceDirectoryName(directoryName) {
3617
4056
  return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
3618
4057
  }
@@ -3620,7 +4059,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
3620
4059
  if (!isValidWorkspaceDirectoryName(directoryName)) {
3621
4060
  return null;
3622
4061
  }
3623
- return path11.join(dataDir, directoryName);
4062
+ return path12.join(dataDir, directoryName);
3624
4063
  }
3625
4064
  function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
3626
4065
  return {
@@ -3669,7 +4108,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
3669
4108
  return summary;
3670
4109
  }
3671
4110
  const childSummaries = await Promise.all(
3672
- entries.map((entry) => summarizeWorkspaceEntry(path11.join(dirPath, entry.name), entry))
4111
+ entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
3673
4112
  );
3674
4113
  for (const childSummary of childSummaries) {
3675
4114
  summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
@@ -3688,7 +4127,7 @@ async function scanWorkspaceDirectories(dataDir) {
3688
4127
  if (!entry.isDirectory()) {
3689
4128
  return null;
3690
4129
  }
3691
- const dirPath = path11.join(dataDir, entry.name);
4130
+ const dirPath = path12.join(dataDir, entry.name);
3692
4131
  try {
3693
4132
  const summary = await summarizeWorkspaceDirectory(dirPath);
3694
4133
  return {
@@ -3892,12 +4331,12 @@ function findSessionJsonl(root, predicate) {
3892
4331
  for (const entry of entries) {
3893
4332
  if (++visited > maxEntries) return null;
3894
4333
  if (!entry.isFile() || !predicate(entry.name)) continue;
3895
- return path12.join(dir, entry.name);
4334
+ return path13.join(dir, entry.name);
3896
4335
  }
3897
4336
  for (const entry of entries) {
3898
4337
  if (++visited > maxEntries) return null;
3899
4338
  if (!entry.isDirectory()) continue;
3900
- const found = visit(path12.join(dir, entry.name), depth - 1);
4339
+ const found = visit(path13.join(dir, entry.name), depth - 1);
3901
4340
  if (found) return found;
3902
4341
  }
3903
4342
  return null;
@@ -3910,10 +4349,10 @@ function safeSessionFilename(value) {
3910
4349
  }
3911
4350
  function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
3912
4351
  try {
3913
- const dir = path12.join(fallbackDir, ".slock", "runtime-sessions");
3914
- mkdirSync4(dir, { recursive: true });
3915
- const filePath = path12.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
3916
- writeFileSync7(filePath, JSON.stringify({
4352
+ const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
4353
+ mkdirSync5(dir, { recursive: true });
4354
+ const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
4355
+ writeFileSync8(filePath, JSON.stringify({
3917
4356
  type: "runtime_session_handoff",
3918
4357
  runtime,
3919
4358
  sessionId,
@@ -3932,7 +4371,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
3932
4371
  }
3933
4372
  }
3934
4373
  function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
3935
- const directPath = path12.isAbsolute(sessionId) ? sessionId : null;
4374
+ const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
3936
4375
  if (directPath) {
3937
4376
  try {
3938
4377
  if (statSync2(directPath).isFile()) {
@@ -3941,7 +4380,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), f
3941
4380
  } catch {
3942
4381
  }
3943
4382
  }
3944
- 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;
4383
+ 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;
3945
4384
  if (!resolvedPath && fallbackDir) {
3946
4385
  const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
3947
4386
  if (fallback) return fallback;
@@ -5075,26 +5514,26 @@ var AgentProcessManager = class _AgentProcessManager {
5075
5514
  this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
5076
5515
  try {
5077
5516
  const driver = this.driverResolver(config.runtime || "claude");
5078
- const agentDataDir = path12.join(this.dataDir, agentId);
5517
+ const agentDataDir = path13.join(this.dataDir, agentId);
5079
5518
  await mkdir(agentDataDir, { recursive: true });
5080
5519
  const runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
5081
- const memoryMdPath = path12.join(agentDataDir, "MEMORY.md");
5520
+ const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
5082
5521
  try {
5083
5522
  await access(memoryMdPath);
5084
5523
  } catch {
5085
5524
  const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
5086
5525
  await writeFile(memoryMdPath, initialMemoryMd);
5087
5526
  }
5088
- const notesDir = path12.join(agentDataDir, "notes");
5527
+ const notesDir = path13.join(agentDataDir, "notes");
5089
5528
  await mkdir(notesDir, { recursive: true });
5090
5529
  if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
5091
5530
  const seedFiles = buildOnboardingSeedFiles();
5092
5531
  for (const { relativePath, content } of seedFiles) {
5093
- const fullPath = path12.join(agentDataDir, relativePath);
5532
+ const fullPath = path13.join(agentDataDir, relativePath);
5094
5533
  try {
5095
5534
  await access(fullPath);
5096
5535
  } catch {
5097
- await mkdir(path12.dirname(fullPath), { recursive: true });
5536
+ await mkdir(path13.dirname(fullPath), { recursive: true });
5098
5537
  await writeFile(fullPath, content);
5099
5538
  }
5100
5539
  }
@@ -5350,11 +5789,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5350
5789
  ...runtimeTraceCounterAttrs(ap),
5351
5790
  ...this.finalizeRuntimeProfileTurnControl(agentId, ap, "process_exit")
5352
5791
  });
5353
- if (processEndedCleanly) {
5354
- this.finishCompactionIfActive(agentId, "Context compaction finished (inferred from process exit)");
5355
- } else {
5356
- this.clearCompactionWatchdog(ap);
5357
- }
5792
+ this.interruptCompactionIfActive(agentId);
5358
5793
  this.agents.delete(agentId);
5359
5794
  if (missingResumeSession) {
5360
5795
  const staleSessionId = ap.sessionId;
@@ -5739,6 +6174,37 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5739
6174
  }));
5740
6175
  return true;
5741
6176
  }
6177
+ if (ap.gatedSteering.compacting) {
6178
+ ap.pendingNotificationCount++;
6179
+ if (ap.notificationTimer) {
6180
+ clearTimeout(ap.notificationTimer);
6181
+ ap.notificationTimer = null;
6182
+ }
6183
+ if (ap.driver.busyDeliveryMode === "gated") {
6184
+ this.recordGatedSteeringEvent(agentId, ap, "buffer", {
6185
+ reason: "compaction_boundary",
6186
+ pendingMessages: ap.inbox.length
6187
+ });
6188
+ }
6189
+ this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.delivery_buffered", {
6190
+ pendingNotificationCount: ap.pendingNotificationCount,
6191
+ pendingMessages: ap.inbox.length,
6192
+ busyDeliveryMode: ap.driver.busyDeliveryMode
6193
+ });
6194
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
6195
+ outcome: "queued_compaction_boundary",
6196
+ accepted: true,
6197
+ process_present: true,
6198
+ runtime: ap.config.runtime,
6199
+ session_id_present: true,
6200
+ launchId: ap.launchId || void 0,
6201
+ inbox_count: ap.inbox.length,
6202
+ pending_notification_count: ap.pendingNotificationCount,
6203
+ busy_delivery_mode: ap.driver.busyDeliveryMode,
6204
+ notification_timer_present: false
6205
+ }));
6206
+ return true;
6207
+ }
5742
6208
  if (ap.driver.busyDeliveryMode === "gated") {
5743
6209
  ap.pendingNotificationCount++;
5744
6210
  this.recordGatedSteeringEvent(agentId, ap, "buffer", {
@@ -5776,7 +6242,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5776
6242
  return true;
5777
6243
  }
5778
6244
  async resetWorkspace(agentId) {
5779
- const agentDataDir = path12.join(this.dataDir, agentId);
6245
+ const agentDataDir = path13.join(this.dataDir, agentId);
5780
6246
  try {
5781
6247
  await rm2(agentDataDir, { recursive: true, force: true });
5782
6248
  logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
@@ -5813,7 +6279,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5813
6279
  return result;
5814
6280
  }
5815
6281
  buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
5816
- const workspacePath = path12.join(this.dataDir, agentId);
6282
+ const workspacePath = path13.join(this.dataDir, agentId);
5817
6283
  return {
5818
6284
  agentId,
5819
6285
  launchId,
@@ -6061,7 +6527,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6061
6527
  }
6062
6528
  // Workspace file browsing
6063
6529
  async getFileTree(agentId, dirPath) {
6064
- const agentDir = path12.join(this.dataDir, agentId);
6530
+ const agentDir = path13.join(this.dataDir, agentId);
6065
6531
  try {
6066
6532
  await stat2(agentDir);
6067
6533
  } catch {
@@ -6069,8 +6535,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6069
6535
  }
6070
6536
  let targetDir = agentDir;
6071
6537
  if (dirPath) {
6072
- const resolved = path12.resolve(agentDir, dirPath);
6073
- if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
6538
+ const resolved = path13.resolve(agentDir, dirPath);
6539
+ if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
6074
6540
  return [];
6075
6541
  }
6076
6542
  targetDir = resolved;
@@ -6078,14 +6544,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6078
6544
  return this.listDirectoryChildren(targetDir, agentDir);
6079
6545
  }
6080
6546
  async readFile(agentId, filePath) {
6081
- const agentDir = path12.join(this.dataDir, agentId);
6082
- const resolved = path12.resolve(agentDir, filePath);
6083
- if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
6547
+ const agentDir = path13.join(this.dataDir, agentId);
6548
+ const resolved = path13.resolve(agentDir, filePath);
6549
+ if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
6084
6550
  throw new Error("Access denied");
6085
6551
  }
6086
6552
  const info = await stat2(resolved);
6087
6553
  if (info.isDirectory()) throw new Error("Cannot read a directory");
6088
- const ext = path12.extname(resolved).toLowerCase();
6554
+ const ext = path13.extname(resolved).toLowerCase();
6089
6555
  if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
6090
6556
  if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
6091
6557
  const content = await readFile(resolved, "utf-8");
@@ -6120,13 +6586,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6120
6586
  const agent = this.agents.get(agentId);
6121
6587
  const runtime = runtimeHint || agent?.config.runtime || "claude";
6122
6588
  const home = os6.homedir();
6123
- const workspaceDir = path12.join(this.dataDir, agentId);
6589
+ const workspaceDir = path13.join(this.dataDir, agentId);
6124
6590
  const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
6125
6591
  const globalResults = await Promise.all(
6126
- paths.global.map((p) => this.scanSkillsDir(path12.join(home, p)))
6592
+ paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
6127
6593
  );
6128
6594
  const workspaceResults = await Promise.all(
6129
- paths.workspace.map((p) => this.scanSkillsDir(path12.join(workspaceDir, p)))
6595
+ paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
6130
6596
  );
6131
6597
  const dedup = (skills) => {
6132
6598
  const seen = /* @__PURE__ */ new Set();
@@ -6155,7 +6621,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6155
6621
  const skills = [];
6156
6622
  for (const entry of entries) {
6157
6623
  if (entry.isDirectory() || entry.isSymbolicLink()) {
6158
- const skillMd = path12.join(dir, entry.name, "SKILL.md");
6624
+ const skillMd = path13.join(dir, entry.name, "SKILL.md");
6159
6625
  try {
6160
6626
  const content = await readFile(skillMd, "utf-8");
6161
6627
  const skill = this.parseSkillMd(entry.name, content);
@@ -6166,7 +6632,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6166
6632
  } else if (entry.name.endsWith(".md")) {
6167
6633
  const cmdName = entry.name.replace(/\.md$/, "");
6168
6634
  try {
6169
- const content = await readFile(path12.join(dir, entry.name), "utf-8");
6635
+ const content = await readFile(path13.join(dir, entry.name), "utf-8");
6170
6636
  const skill = this.parseSkillMd(cmdName, content);
6171
6637
  skill.sourcePath = dir;
6172
6638
  skills.push(skill);
@@ -6407,11 +6873,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6407
6873
  ap.compactionWatchdog = null;
6408
6874
  this.broadcastActivity(agentId, "working", "Context compaction still running; no finish event observed");
6409
6875
  }
6410
- finishCompactionIfActive(agentId, detail = "Context compaction finished") {
6876
+ completeCompactionIfActive(agentId, detail = "Context compaction finished", options = {}) {
6411
6877
  const ap = this.agents.get(agentId);
6412
6878
  if (!ap || !ap.compactionStartedAt) return;
6413
6879
  this.clearCompactionWatchdog(ap);
6414
6880
  this.broadcastActivity(agentId, "working", detail, [{ kind: "compaction_finished" }]);
6881
+ ap.gatedSteering.compacting = false;
6882
+ this.setGatedSteeringPhase(agentId, ap, "assistant_continuation", { event: "compaction_finished", inferred: true });
6883
+ if (options.flushBoundaryMessages ?? true) {
6884
+ this.flushCompactionBoundaryMessages(agentId, ap);
6885
+ }
6886
+ ap.isIdle = false;
6887
+ }
6888
+ interruptCompactionIfActive(agentId) {
6889
+ const ap = this.agents.get(agentId);
6890
+ if (!ap || !ap.compactionStartedAt && !ap.gatedSteering.compacting) return;
6891
+ this.clearCompactionWatchdog(ap);
6892
+ ap.gatedSteering.compacting = false;
6415
6893
  }
6416
6894
  messagesTraceAttrs(messages) {
6417
6895
  if (!messages || messages.length === 0) return {};
@@ -6734,28 +7212,28 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6734
7212
  this.sendRuntimeProfileReport(agentId);
6735
7213
  break;
6736
7214
  case "thinking": {
6737
- this.finishCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
7215
+ if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
7216
+ this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
6738
7217
  this.queueTrajectoryText(agentId, "thinking", event.text);
6739
7218
  if (ap) ap.isIdle = false;
6740
- if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
6741
7219
  this.setGatedSteeringPhase(agentId, ap, "assistant_continuation", { event: "thinking" });
6742
7220
  break;
6743
7221
  }
6744
7222
  case "text": {
6745
- this.finishCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
7223
+ if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
7224
+ this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
6746
7225
  this.queueTrajectoryText(agentId, "text", event.text);
6747
7226
  if (ap) ap.isIdle = false;
6748
- if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
6749
7227
  this.setGatedSteeringPhase(agentId, ap, "assistant_continuation", { event: "text" });
6750
7228
  break;
6751
7229
  }
6752
7230
  case "tool_call": {
6753
- this.finishCompactionIfActive(agentId, "Context compaction finished (inferred from resumed tool use)");
7231
+ if (ap) this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
7232
+ this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed tool use)");
6754
7233
  this.flushPendingTrajectory(agentId);
6755
7234
  const invocation = normalizeToolDisplayInvocation(event.name, event.input);
6756
7235
  if (ap) {
6757
7236
  ap.gatedSteering.outstandingToolUses++;
6758
- this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
6759
7237
  this.noteRuntimeProfileToolCall(agentId, ap, invocation.toolName);
6760
7238
  this.recordRuntimeTraceEvent(agentId, ap, "tool.call.started", { tool: invocation.toolName });
6761
7239
  this.setGatedSteeringPhase(agentId, ap, "tool_wait", {
@@ -6816,7 +7294,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6816
7294
  break;
6817
7295
  case "turn_end":
6818
7296
  if (ap) this.recordRuntimeTraceEvent(agentId, ap, "runtime.turn.completed");
6819
- this.finishCompactionIfActive(agentId, "Context compaction finished (inferred from turn end)");
7297
+ this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from turn end)", {
7298
+ flushBoundaryMessages: false
7299
+ });
6820
7300
  this.flushPendingTrajectory(agentId);
6821
7301
  if (ap) {
6822
7302
  this.clearGatedInFlightBatch(agentId, ap, "turn_end");
@@ -6873,7 +7353,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6873
7353
  }
6874
7354
  break;
6875
7355
  case "error": {
6876
- this.finishCompactionIfActive(agentId, "Context compaction interrupted by runtime error");
7356
+ this.interruptCompactionIfActive(agentId);
6877
7357
  this.flushPendingTrajectory(agentId);
6878
7358
  if (ap) ap.lastRuntimeError = event.message;
6879
7359
  if (ap) {
@@ -7116,8 +7596,8 @@ ${RESPONSE_TARGET_HINT}`);
7116
7596
  const nodes = [];
7117
7597
  for (const entry of entries) {
7118
7598
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
7119
- const fullPath = path12.join(dir, entry.name);
7120
- const relativePath = path12.relative(rootDir, fullPath);
7599
+ const fullPath = path13.join(dir, entry.name);
7600
+ const relativePath = path13.relative(rootDir, fullPath);
7121
7601
  let info;
7122
7602
  try {
7123
7603
  info = await stat2(fullPath);
@@ -7420,9 +7900,9 @@ var ReminderCache = class {
7420
7900
 
7421
7901
  // src/machineLock.ts
7422
7902
  import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
7423
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync8 } from "fs";
7903
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
7424
7904
  import os7 from "os";
7425
- import path13 from "path";
7905
+ import path14 from "path";
7426
7906
  var INCOMPLETE_LOCK_STALE_MS = 3e4;
7427
7907
  var DaemonMachineLockConflictError = class extends Error {
7428
7908
  code = "DAEMON_MACHINE_LOCK_HELD";
@@ -7444,7 +7924,7 @@ function resolveDefaultMachineStateRoot() {
7444
7924
  return resolveSlockHomePath("machines");
7445
7925
  }
7446
7926
  function ownerPath(lockDir) {
7447
- return path13.join(lockDir, "owner.json");
7927
+ return path14.join(lockDir, "owner.json");
7448
7928
  }
7449
7929
  function readOwner(lockDir) {
7450
7930
  try {
@@ -7474,13 +7954,13 @@ function acquireDaemonMachineLock(options) {
7474
7954
  const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
7475
7955
  const fingerprint = apiKeyFingerprint(options.apiKey);
7476
7956
  const lockId = getDaemonMachineLockId(options.apiKey);
7477
- const machineDir = path13.join(rootDir, lockId);
7478
- const lockDir = path13.join(machineDir, "daemon.lock");
7957
+ const machineDir = path14.join(rootDir, lockId);
7958
+ const lockDir = path14.join(machineDir, "daemon.lock");
7479
7959
  const token = randomUUID2();
7480
- mkdirSync5(machineDir, { recursive: true });
7960
+ mkdirSync6(machineDir, { recursive: true });
7481
7961
  for (let attempt = 0; attempt < 2; attempt += 1) {
7482
7962
  try {
7483
- mkdirSync5(lockDir);
7963
+ mkdirSync6(lockDir);
7484
7964
  const owner = {
7485
7965
  pid: process.pid,
7486
7966
  token,
@@ -7490,7 +7970,7 @@ function acquireDaemonMachineLock(options) {
7490
7970
  apiKeyFingerprint: fingerprint.slice(0, 16)
7491
7971
  };
7492
7972
  try {
7493
- writeFileSync8(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
7973
+ writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
7494
7974
  `, { mode: 384 });
7495
7975
  } catch (err) {
7496
7976
  rmSync2(lockDir, { recursive: true, force: true });
@@ -7527,8 +8007,8 @@ function acquireDaemonMachineLock(options) {
7527
8007
  }
7528
8008
 
7529
8009
  // src/localTraceSink.ts
7530
- import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync3, rmSync as rmSync3, statSync as statSync4, writeFileSync as writeFileSync9 } from "fs";
7531
- import path14 from "path";
8010
+ import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync3, rmSync as rmSync3, statSync as statSync4, writeFileSync as writeFileSync10 } from "fs";
8011
+ import path15 from "path";
7532
8012
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
7533
8013
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
7534
8014
  var DEFAULT_MAX_FILES = 8;
@@ -7564,7 +8044,7 @@ var LocalRotatingTraceSink = class {
7564
8044
  currentSize = 0;
7565
8045
  sequence = 0;
7566
8046
  constructor(options) {
7567
- this.traceDir = path14.join(options.machineDir, "traces");
8047
+ this.traceDir = path15.join(options.machineDir, "traces");
7568
8048
  this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
7569
8049
  const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
7570
8050
  const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
@@ -7590,15 +8070,15 @@ var LocalRotatingTraceSink = class {
7590
8070
  return this.currentFile;
7591
8071
  }
7592
8072
  ensureFile(nextBytes) {
7593
- mkdirSync6(this.traceDir, { recursive: true, mode: 448 });
8073
+ mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
7594
8074
  const nowMs = this.nowMsProvider();
7595
8075
  const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
7596
8076
  if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
7597
- this.currentFile = path14.join(
8077
+ this.currentFile = path15.join(
7598
8078
  this.traceDir,
7599
8079
  `daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
7600
8080
  );
7601
- writeFileSync9(this.currentFile, "", { flag: "a", mode: 384 });
8081
+ writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
7602
8082
  this.currentSize = statSync4(this.currentFile).size;
7603
8083
  this.currentFileOpenedAtMs = nowMs;
7604
8084
  this.pruneOldFiles();
@@ -7609,7 +8089,7 @@ var LocalRotatingTraceSink = class {
7609
8089
  const excess = files.length - this.maxFiles;
7610
8090
  if (excess <= 0) return;
7611
8091
  for (const file of files.slice(0, excess)) {
7612
- rmSync3(path14.join(this.traceDir, file), { force: true });
8092
+ rmSync3(path15.join(this.traceDir, file), { force: true });
7613
8093
  }
7614
8094
  }
7615
8095
  };
@@ -7696,11 +8176,11 @@ function isDiagnosticErrorAttr(key) {
7696
8176
  import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
7697
8177
  import { gzipSync } from "zlib";
7698
8178
  import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
7699
- import path15 from "path";
8179
+ import path16 from "path";
7700
8180
 
7701
8181
  // src/directUploadCapability.ts
7702
- function joinUrl(base, path17) {
7703
- return `${base.replace(/\/+$/, "")}${path17}`;
8182
+ function joinUrl(base, path18) {
8183
+ return `${base.replace(/\/+$/, "")}${path18}`;
7704
8184
  }
7705
8185
  function jsonHeaders(apiKey) {
7706
8186
  return {
@@ -7919,7 +8399,7 @@ var DaemonTraceBundleUploader = class {
7919
8399
  }, nextMs);
7920
8400
  }
7921
8401
  async findUploadCandidates() {
7922
- const traceDir = path15.join(this.options.machineDir, "traces");
8402
+ const traceDir = path16.join(this.options.machineDir, "traces");
7923
8403
  let names;
7924
8404
  try {
7925
8405
  names = await readdir3(traceDir);
@@ -7931,8 +8411,8 @@ var DaemonTraceBundleUploader = class {
7931
8411
  const currentFile = this.options.currentFileProvider?.();
7932
8412
  const candidates = [];
7933
8413
  for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
7934
- const file = path15.join(traceDir, name);
7935
- if (currentFile && path15.resolve(file) === path15.resolve(currentFile)) continue;
8414
+ const file = path16.join(traceDir, name);
8415
+ if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
7936
8416
  if (await this.isUploaded(file)) continue;
7937
8417
  try {
7938
8418
  const info = await stat3(file);
@@ -8006,8 +8486,8 @@ var DaemonTraceBundleUploader = class {
8006
8486
  }
8007
8487
  }
8008
8488
  uploadStatePath(file) {
8009
- const stateDir = path15.join(this.options.machineDir, "trace-uploads");
8010
- return path15.join(stateDir, `${path15.basename(file)}.uploaded.json`);
8489
+ const stateDir = path16.join(this.options.machineDir, "trace-uploads");
8490
+ return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
8011
8491
  }
8012
8492
  async isUploaded(file) {
8013
8493
  try {
@@ -8019,9 +8499,9 @@ var DaemonTraceBundleUploader = class {
8019
8499
  }
8020
8500
  async markUploaded(file, metadata) {
8021
8501
  const stateFile = this.uploadStatePath(file);
8022
- await mkdir2(path15.dirname(stateFile), { recursive: true, mode: 448 });
8502
+ await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
8023
8503
  await writeFile2(stateFile, `${JSON.stringify({
8024
- file: path15.basename(file),
8504
+ file: path16.basename(file),
8025
8505
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
8026
8506
  ...metadata
8027
8507
  }, null, 2)}
@@ -8040,10 +8520,10 @@ function readPositiveIntegerEnv2(name, fallback) {
8040
8520
 
8041
8521
  // src/core.ts
8042
8522
  var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
8043
- var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
8044
- function parseDaemonCliArgs(args) {
8523
+ var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
8524
+ function parseDaemonCliArgs(args, env = {}) {
8045
8525
  let serverUrl = "";
8046
- let apiKey = "";
8526
+ let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
8047
8527
  for (let i = 0; i < args.length; i++) {
8048
8528
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
8049
8529
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -8060,23 +8540,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
8060
8540
  }
8061
8541
  }
8062
8542
  function resolveChatBridgePath(moduleUrl = import.meta.url) {
8063
- const dirname = path16.dirname(fileURLToPath(moduleUrl));
8064
- const jsPath = path16.resolve(dirname, "chat-bridge.js");
8543
+ const dirname = path17.dirname(fileURLToPath2(moduleUrl));
8544
+ const jsPath = path17.resolve(dirname, "chat-bridge.js");
8065
8545
  try {
8066
8546
  accessSync(jsPath);
8067
8547
  return jsPath;
8068
8548
  } catch {
8069
- return path16.resolve(dirname, "chat-bridge.ts");
8549
+ return path17.resolve(dirname, "chat-bridge.ts");
8070
8550
  }
8071
8551
  }
8072
8552
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
8073
- const thisDir = path16.dirname(fileURLToPath(moduleUrl));
8074
- const bundledDistPath = path16.resolve(thisDir, "cli", "index.js");
8553
+ const thisDir = path17.dirname(fileURLToPath2(moduleUrl));
8554
+ const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
8075
8555
  try {
8076
8556
  accessSync(bundledDistPath);
8077
8557
  return bundledDistPath;
8078
8558
  } catch {
8079
- const workspaceDistPath = path16.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
8559
+ const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
8080
8560
  accessSync(workspaceDistPath);
8081
8561
  return workspaceDistPath;
8082
8562
  }
@@ -8255,7 +8735,7 @@ var DaemonCore = class {
8255
8735
  }
8256
8736
  resolveMachineStateRoot() {
8257
8737
  if (this.options.machineStateDir) return this.options.machineStateDir;
8258
- if (this.options.dataDir) return path16.join(path16.dirname(this.options.dataDir), "machines");
8738
+ if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
8259
8739
  return resolveDefaultMachineStateRoot();
8260
8740
  }
8261
8741
  shouldEnableLocalTrace() {
@@ -8638,6 +9118,8 @@ var DaemonCore = class {
8638
9118
  };
8639
9119
 
8640
9120
  export {
9121
+ DAEMON_API_KEY_ENV,
9122
+ scrubDaemonAuthEnv,
8641
9123
  resolveWorkspaceDirectoryPath,
8642
9124
  scanWorkspaceDirectories,
8643
9125
  deleteWorkspaceDirectory,