@slock-ai/daemon 0.48.0-play.20260513145259 → 0.48.1

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.
@@ -169,16 +169,64 @@ function formatHistoryMessageLine(message) {
169
169
  }
170
170
 
171
171
  // src/chatBridgeSendRequest.ts
172
- import { randomUUID } from "crypto";
172
+ import { createHash, randomUUID } from "crypto";
173
+ var DEFAULT_SEND_RETRY_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
174
+ var ambiguousSendKeys = /* @__PURE__ */ new Map();
175
+ function pruneExpiredAmbiguousSendKeys(nowMs) {
176
+ for (const [fingerprint, entry] of ambiguousSendKeys) {
177
+ if (entry.expiresAt <= nowMs) {
178
+ ambiguousSendKeys.delete(fingerprint);
179
+ }
180
+ }
181
+ }
182
+ function resolveIdempotencyKey(opts) {
183
+ if (opts.idempotencyKey) return opts.idempotencyKey;
184
+ if (!opts.retryFingerprint) return randomUUID();
185
+ pruneExpiredAmbiguousSendKeys(opts.nowMs);
186
+ const existing = ambiguousSendKeys.get(opts.retryFingerprint);
187
+ if (existing && existing.expiresAt > opts.nowMs) {
188
+ return existing.idempotencyKey;
189
+ }
190
+ return randomUUID();
191
+ }
192
+ function rememberAmbiguousSendKey(retryFingerprint, idempotencyKey, nowMs, ttlMs) {
193
+ if (!retryFingerprint) return;
194
+ pruneExpiredAmbiguousSendKeys(nowMs);
195
+ ambiguousSendKeys.set(retryFingerprint, {
196
+ idempotencyKey,
197
+ expiresAt: nowMs + ttlMs
198
+ });
199
+ }
200
+ function clearAmbiguousSendKey(retryFingerprint) {
201
+ if (!retryFingerprint) return;
202
+ ambiguousSendKeys.delete(retryFingerprint);
203
+ }
204
+ function buildSendRetryFingerprint(input) {
205
+ const attachmentIds = input.attachmentIds ?? [];
206
+ const hash = createHash("sha256").update(JSON.stringify({
207
+ target: input.target,
208
+ content: input.content,
209
+ attachmentIds
210
+ })).digest("hex");
211
+ return `send_message:${hash}`;
212
+ }
173
213
  async function executeRetrySafeSendRequest(url, buildInit, {
174
214
  fetchImpl,
175
215
  target,
176
216
  timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS,
177
217
  now = () => Date.now(),
178
218
  warn = (message) => logger.warn(message),
179
- idempotencyKey = randomUUID()
219
+ idempotencyKey: explicitIdempotencyKey,
220
+ retryFingerprint,
221
+ retryFingerprintTtlMs = DEFAULT_SEND_RETRY_FINGERPRINT_TTL_MS
180
222
  }) {
181
223
  let lastError;
224
+ const idempotencyKey = resolveIdempotencyKey({
225
+ idempotencyKey: explicitIdempotencyKey,
226
+ retryFingerprint,
227
+ retryFingerprintTtlMs,
228
+ nowMs: now()
229
+ });
182
230
  for (let attempt = 1; attempt <= 2; attempt += 1) {
183
231
  try {
184
232
  const result = await executeJsonRequest(
@@ -193,6 +241,7 @@ async function executeRetrySafeSendRequest(url, buildInit, {
193
241
  warn
194
242
  }
195
243
  );
244
+ clearAmbiguousSendKey(retryFingerprint);
196
245
  return {
197
246
  ...result,
198
247
  idempotencyKey,
@@ -206,6 +255,7 @@ async function executeRetrySafeSendRequest(url, buildInit, {
206
255
  );
207
256
  }
208
257
  }
258
+ rememberAmbiguousSendKey(retryFingerprint, idempotencyKey, now(), retryFingerprintTtlMs);
209
259
  throw lastError;
210
260
  }
211
261
  function describeRetryReason(error) {
@@ -578,7 +628,12 @@ if (!deprecatedShimMode && !runtimeActionsOnlyMode) {
578
628
  }),
579
629
  {
580
630
  target,
581
- fetchImpl: bridgeFetch
631
+ fetchImpl: bridgeFetch,
632
+ retryFingerprint: buildSendRetryFingerprint({
633
+ target,
634
+ content,
635
+ attachmentIds: attachment_ids
636
+ })
582
637
  }
583
638
  );
584
639
  if (!res.ok) {
@@ -11,11 +11,11 @@ import {
11
11
  } from "./chunk-B7XIMLOT.js";
12
12
 
13
13
  // src/core.ts
14
- import path16 from "path";
14
+ import path15 from "path";
15
15
  import os7 from "os";
16
16
  import { createRequire } from "module";
17
17
  import { accessSync } from "fs";
18
- import { fileURLToPath as fileURLToPath2 } from "url";
18
+ import { fileURLToPath } from "url";
19
19
 
20
20
  // ../shared/src/tracing/index.ts
21
21
  var DEFAULT_TRACE_FLAGS = "00";
@@ -725,7 +725,6 @@ var SERVER_CAPABILITY_MATRIX = {
725
725
  var RUNTIMES = [
726
726
  { id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
727
727
  { id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
728
- { id: "pi", displayName: "Pi", binary: "pi", supported: true },
729
728
  { id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
730
729
  { id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
731
730
  { id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
@@ -767,10 +766,10 @@ var DISPLAY_PLAN_CONFIG = {
767
766
  };
768
767
 
769
768
  // src/agentProcessManager.ts
770
- import { mkdirSync as mkdirSync5, readdirSync as readdirSync3, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
769
+ import { mkdirSync as mkdirSync4, readdirSync as readdirSync3, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
771
770
  import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
772
771
  import { createHash as createHash2 } from "crypto";
773
- import path12 from "path";
772
+ import path11 from "path";
774
773
  import os5 from "os";
775
774
 
776
775
  // src/drivers/claude.ts
@@ -1177,17 +1176,6 @@ Keep the user informed. They cannot see your internal reasoning, so:
1177
1176
  - For multi-step work, send short progress updates (e.g. "Working on step 2/3\u2026").
1178
1177
  - When done, summarize the result.
1179
1178
  - Keep updates concise \u2014 one or two sentences. Don't flood the chat.
1180
- - 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:
1181
-
1182
- \`\`\`html
1183
- <details>
1184
- <summary>Evidence, logs, or edge cases</summary>
1185
-
1186
- Detailed notes go here.
1187
- </details>
1188
- \`\`\`
1189
-
1190
- Do not hide the main recommendation, blocker, or required action inside \`<details>\`; only fold supporting evidence, logs, alternatives, or extended rationale.
1191
1179
 
1192
1180
  ### Conversation etiquette
1193
1181
 
@@ -1321,19 +1309,6 @@ function buildMcpSystemPrompt(config, opts) {
1321
1309
  return buildPrompt(config, "mcp", opts);
1322
1310
  }
1323
1311
 
1324
- // src/authEnv.ts
1325
- var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
1326
- var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
1327
- function scrubDaemonAuthEnv(env) {
1328
- delete env[DAEMON_API_KEY_ENV];
1329
- return env;
1330
- }
1331
- function scrubDaemonChildEnv(env) {
1332
- delete env[DAEMON_API_KEY_ENV];
1333
- delete env[SLOCK_AGENT_TOKEN_ENV];
1334
- return env;
1335
- }
1336
-
1337
1312
  // src/drivers/cliTransport.ts
1338
1313
  var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
1339
1314
  function runtimeContextEnv(config) {
@@ -1387,7 +1362,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)}
1387
1362
  SLOCK_AGENT_TOKEN_FILE: tokenFile,
1388
1363
  PATH: `${slockDir}${path.delimiter}${process.env.PATH ?? ""}`
1389
1364
  };
1390
- scrubDaemonChildEnv(spawnEnv);
1365
+ delete spawnEnv.SLOCK_AGENT_TOKEN;
1391
1366
  return {
1392
1367
  slockDir,
1393
1368
  tokenFile,
@@ -1424,7 +1399,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn) {
1424
1399
  }
1425
1400
  function resolveCommandOnPath(command, deps = {}) {
1426
1401
  const platform = deps.platform ?? process.platform;
1427
- const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
1402
+ const env = deps.env ?? process.env;
1428
1403
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
1429
1404
  if (platform === "win32") {
1430
1405
  return resolveCommandOnWindows(command, env, execFileSyncFn);
@@ -1449,7 +1424,7 @@ function firstExistingPath(candidates, deps = {}) {
1449
1424
  return null;
1450
1425
  }
1451
1426
  function readCommandVersion(command, args = [], deps = {}) {
1452
- const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
1427
+ const env = deps.env ?? process.env;
1453
1428
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
1454
1429
  try {
1455
1430
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -1890,21 +1865,18 @@ function resolveCodexSpawn(commandArgs, deps = {}) {
1890
1865
  if ((deps.platform ?? process.platform) !== "win32") {
1891
1866
  return { command: resolveCodexCommand(deps) ?? "codex", args: commandArgs };
1892
1867
  }
1893
- const execSyncFn = deps.execSyncFn ?? execSync;
1894
- const existsSyncFn = deps.existsSyncFn ?? existsSync3;
1895
- const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
1896
1868
  let codexEntry = null;
1897
1869
  try {
1898
- const globalRoot = execSyncFn("npm root -g", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], env }).trim();
1870
+ const globalRoot = execSync("npm root -g", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
1899
1871
  const candidate = path4.join(globalRoot, "@openai", "codex", "bin", "codex.js");
1900
- if (existsSyncFn(candidate)) codexEntry = candidate;
1872
+ if (existsSync3(candidate)) codexEntry = candidate;
1901
1873
  } catch {
1902
1874
  }
1903
1875
  if (!codexEntry) {
1904
1876
  try {
1905
- const cmdPath = execSyncFn("where codex", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], env }).trim().split(/\r?\n/)[0];
1877
+ const cmdPath = execSync("where codex", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim().split(/\r?\n/)[0];
1906
1878
  const candidate = path4.join(path4.dirname(cmdPath), "node_modules", "@openai", "codex", "bin", "codex.js");
1907
- if (existsSyncFn(candidate)) codexEntry = candidate;
1879
+ if (existsSync3(candidate)) codexEntry = candidate;
1908
1880
  } catch {
1909
1881
  }
1910
1882
  }
@@ -2366,13 +2338,13 @@ import { spawn as spawn3 } from "child_process";
2366
2338
  import path5 from "path";
2367
2339
  import { writeFileSync as writeFileSync3 } from "fs";
2368
2340
  function buildCopilotSpawnEnv(ctx) {
2369
- return scrubDaemonChildEnv({
2341
+ return {
2370
2342
  ...process.env,
2371
2343
  FORCE_COLOR: "0",
2372
2344
  NO_COLOR: "1",
2373
2345
  ...ctx.config.envVars || {},
2374
2346
  [SLOCK_HOME_ENV]: resolveSlockHome()
2375
- });
2347
+ };
2376
2348
  }
2377
2349
  var CopilotDriver = class {
2378
2350
  id = "copilot";
@@ -2530,13 +2502,13 @@ import { spawn as spawn4, spawnSync } from "child_process";
2530
2502
  import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
2531
2503
  import path6 from "path";
2532
2504
  function buildCursorSpawnEnv(ctx) {
2533
- return scrubDaemonChildEnv({
2505
+ return {
2534
2506
  ...process.env,
2535
2507
  FORCE_COLOR: "0",
2536
2508
  NO_COLOR: "1",
2537
2509
  ...ctx.config.envVars || {},
2538
2510
  [SLOCK_HOME_ENV]: resolveSlockHome()
2539
- });
2511
+ };
2540
2512
  }
2541
2513
  var CursorDriver = class {
2542
2514
  id = "cursor";
@@ -2706,7 +2678,7 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
2706
2678
  }
2707
2679
  function runCursorModelsCommand() {
2708
2680
  return spawnSync("cursor-agent", ["models"], {
2709
- env: scrubDaemonChildEnv({ ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" }),
2681
+ env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
2710
2682
  encoding: "utf8",
2711
2683
  timeout: 5e3
2712
2684
  });
@@ -2753,7 +2725,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
2753
2725
  }
2754
2726
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
2755
2727
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
2756
- const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
2728
+ const env = deps.env ?? process.env;
2757
2729
  const winPath = path7.win32;
2758
2730
  let geminiEntry = null;
2759
2731
  try {
@@ -2927,16 +2899,13 @@ var GeminiDriver = class {
2927
2899
  // src/drivers/kimi.ts
2928
2900
  import { randomUUID } from "crypto";
2929
2901
  import { spawn as spawn6 } from "child_process";
2930
- import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
2902
+ import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
2931
2903
  import os3 from "os";
2932
2904
  import path8 from "path";
2933
2905
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
2934
2906
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
2935
2907
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
2936
2908
  var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
2937
- var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
2938
- var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
2939
- var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
2940
2909
  function parseToolArguments(raw) {
2941
2910
  if (typeof raw !== "string") return raw;
2942
2911
  try {
@@ -2945,73 +2914,6 @@ function parseToolArguments(raw) {
2945
2914
  return raw;
2946
2915
  }
2947
2916
  }
2948
- function readKimiConfigSource(home = os3.homedir(), env = process.env) {
2949
- const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
2950
- if (inlineConfig && inlineConfig.trim()) {
2951
- return {
2952
- raw: inlineConfig,
2953
- explicitPath: null,
2954
- sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
2955
- };
2956
- }
2957
- const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
2958
- const configPath = explicitPath && explicitPath.trim() ? explicitPath : path8.join(home, ".kimi", "config.toml");
2959
- try {
2960
- return {
2961
- raw: readFileSync3(configPath, "utf8"),
2962
- explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
2963
- sourcePath: configPath
2964
- };
2965
- } catch {
2966
- return {
2967
- raw: null,
2968
- explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
2969
- sourcePath: configPath
2970
- };
2971
- }
2972
- }
2973
- function buildKimiSpawnEnv(env = process.env) {
2974
- const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
2975
- delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
2976
- delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
2977
- return scrubDaemonChildEnv(spawnEnv);
2978
- }
2979
- function buildKimiEffectiveEnv(ctx, overrideEnv) {
2980
- return {
2981
- ...process.env,
2982
- ...ctx.config.envVars || {},
2983
- ...overrideEnv || {}
2984
- };
2985
- }
2986
- function buildKimiLaunchOptions(ctx, opts = {}) {
2987
- const env = buildKimiEffectiveEnv(ctx, opts.env);
2988
- const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
2989
- const args = [];
2990
- let configFilePath = null;
2991
- let configContent = null;
2992
- if (source.explicitPath) {
2993
- configFilePath = source.explicitPath;
2994
- } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
2995
- configFilePath = path8.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
2996
- configContent = source.raw;
2997
- if (opts.writeGeneratedConfig !== false) {
2998
- writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
2999
- chmodSync(configFilePath, 384);
3000
- }
3001
- }
3002
- if (configFilePath) {
3003
- args.push("--config-file", configFilePath);
3004
- }
3005
- if (ctx.config.model && ctx.config.model !== "default") {
3006
- args.push("--model", ctx.config.model);
3007
- }
3008
- return {
3009
- args,
3010
- env: buildKimiSpawnEnv(env),
3011
- configFilePath,
3012
- configContent
3013
- };
3014
- }
3015
2917
  var KimiDriver = class {
3016
2918
  id = "kimi";
3017
2919
  lifecycle = {
@@ -3028,25 +2930,7 @@ var KimiDriver = class {
3028
2930
  };
3029
2931
  model = {
3030
2932
  detectedModelsVerifiedAs: "launchable",
3031
- toLaunchSpec: (modelId, ctx, opts) => {
3032
- if (!ctx) return { args: ["--model", modelId] };
3033
- const launchCtx = {
3034
- ...ctx,
3035
- config: {
3036
- ...ctx.config,
3037
- model: modelId
3038
- }
3039
- };
3040
- const launch = buildKimiLaunchOptions(launchCtx, {
3041
- home: opts?.home,
3042
- writeGeneratedConfig: false
3043
- });
3044
- return {
3045
- args: launch.args,
3046
- env: launch.env,
3047
- configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
3048
- };
3049
- }
2933
+ toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
3050
2934
  };
3051
2935
  supportsStdinNotification = true;
3052
2936
  mcpToolPrefix = "";
@@ -3098,7 +2982,6 @@ var KimiDriver = class {
3098
2982
  }
3099
2983
  }
3100
2984
  }), "utf8");
3101
- const launch = buildKimiLaunchOptions(ctx);
3102
2985
  const args = [
3103
2986
  "--wire",
3104
2987
  "--yolo",
@@ -3107,13 +2990,16 @@ var KimiDriver = class {
3107
2990
  "--mcp-config-file",
3108
2991
  mcpConfigPath,
3109
2992
  "--session",
3110
- this.sessionId,
3111
- ...launch.args
2993
+ this.sessionId
3112
2994
  ];
2995
+ if (ctx.config.model && ctx.config.model !== "default") {
2996
+ args.push("--model", ctx.config.model);
2997
+ }
2998
+ const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" };
3113
2999
  const proc = spawn6("kimi", args, {
3114
3000
  cwd: ctx.workingDirectory,
3115
3001
  stdio: ["pipe", "pipe", "pipe"],
3116
- env: launch.env,
3002
+ env: spawnEnv,
3117
3003
  shell: process.platform === "win32"
3118
3004
  });
3119
3005
  proc.stdin?.write(JSON.stringify({
@@ -3230,9 +3116,14 @@ var KimiDriver = class {
3230
3116
  return detectKimiModels();
3231
3117
  }
3232
3118
  };
3233
- function detectKimiModels(home = os3.homedir(), opts = {}) {
3234
- const raw = readKimiConfigSource(home, opts.env).raw;
3235
- if (raw === null) return null;
3119
+ function detectKimiModels(home = os3.homedir()) {
3120
+ const configPath = path8.join(home, ".kimi", "config.toml");
3121
+ let raw;
3122
+ try {
3123
+ raw = readFileSync3(configPath, "utf8");
3124
+ } catch {
3125
+ return null;
3126
+ }
3236
3127
  const models = [];
3237
3128
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
3238
3129
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -3479,7 +3370,7 @@ function detectOpenCodeModels(home = os4.homedir(), runCommand = runOpenCodeMode
3479
3370
  }
3480
3371
  function runOpenCodeModelsCommand(home) {
3481
3372
  const result = spawnSync2("opencode", ["models"], {
3482
- env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
3373
+ env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
3483
3374
  encoding: "utf8",
3484
3375
  timeout: 5e3
3485
3376
  });
@@ -3635,305 +3526,6 @@ var OpenCodeDriver = class {
3635
3526
  }
3636
3527
  };
3637
3528
 
3638
- // src/drivers/pi.ts
3639
- import { spawn as spawn8, spawnSync as spawnSync3 } from "child_process";
3640
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
3641
- import { fileURLToPath } from "url";
3642
- import path10 from "path";
3643
- var CHAT_MCP_TOOL_PREFIX2 = "chat_";
3644
- var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
3645
- var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
3646
- var MIN_BUNDLED_PI_VERSION = "0.74.0";
3647
- function defaultPackageResolver(specifier) {
3648
- return import.meta.resolve(specifier);
3649
- }
3650
- function parseSemver2(version) {
3651
- const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
3652
- if (!match) return null;
3653
- return [Number(match[1]), Number(match[2]), Number(match[3])];
3654
- }
3655
- function isSupportedPiVersion(version) {
3656
- if (!version) return true;
3657
- const actual = parseSemver2(version);
3658
- const minimum = parseSemver2(MIN_BUNDLED_PI_VERSION);
3659
- if (!actual || !minimum) return true;
3660
- for (let i = 0; i < 3; i += 1) {
3661
- if (actual[i] > minimum[i]) return true;
3662
- if (actual[i] < minimum[i]) return false;
3663
- }
3664
- return true;
3665
- }
3666
- function resolveBundledPiCliPath(resolver = defaultPackageResolver) {
3667
- try {
3668
- const mainPath = fileURLToPath(resolver("@earendil-works/pi-coding-agent"));
3669
- const cliPath = path10.join(path10.dirname(mainPath), "cli.js");
3670
- return existsSync7(cliPath) ? cliPath : null;
3671
- } catch {
3672
- return null;
3673
- }
3674
- }
3675
- function readBundledPiVersion(cliPath) {
3676
- const result = spawnSync3(process.execPath, [cliPath, "--version"], {
3677
- env: scrubDaemonChildEnv({ ...process.env }),
3678
- encoding: "utf8",
3679
- timeout: 5e3
3680
- });
3681
- if (result.error || result.status !== 0) return null;
3682
- return String(result.stdout || result.stderr || "").trim() || null;
3683
- }
3684
- function unsupportedPiVersionMessage(version) {
3685
- if (!version || isSupportedPiVersion(version)) return null;
3686
- return `Bundled Pi CLI ${version} is unsupported; requires Pi >= ${MIN_BUNDLED_PI_VERSION}.`;
3687
- }
3688
- function buildPiLaunchOptions(ctx, opts = {}) {
3689
- const cliPath = opts.cliPath ?? resolveBundledPiCliPath();
3690
- if (!cliPath) {
3691
- throw new Error("Bundled Pi CLI not found in @earendil-works/pi-coding-agent");
3692
- }
3693
- const piDir = path10.join(ctx.workingDirectory, ".slock", "pi");
3694
- const sessionDir = path10.join(piDir, "sessions");
3695
- mkdirSync4(sessionDir, { recursive: true });
3696
- const slock = prepareCliTransport(ctx, {
3697
- NO_COLOR: "1",
3698
- PI_CODING_AGENT_DIR: piDir,
3699
- PI_CODING_AGENT_SESSION_DIR: sessionDir
3700
- });
3701
- const systemPromptPath = path10.join(slock.slockDir, "pi-system-prompt.md");
3702
- writeFileSync7(systemPromptPath, ctx.standingPrompt, { encoding: "utf8", mode: 384 });
3703
- const args = [
3704
- cliPath,
3705
- "--mode",
3706
- "json",
3707
- "--session-dir",
3708
- sessionDir,
3709
- "--append-system-prompt",
3710
- systemPromptPath,
3711
- "--no-context-files",
3712
- "--no-extensions",
3713
- "--no-skills",
3714
- "--no-prompt-templates",
3715
- "--no-themes"
3716
- ];
3717
- if (ctx.config.model && ctx.config.model !== "default") {
3718
- args.push("--model", ctx.config.model);
3719
- }
3720
- if (ctx.config.sessionId) {
3721
- args.push("--session", ctx.config.sessionId);
3722
- }
3723
- const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
3724
- args.push(turnPrompt);
3725
- return {
3726
- command: process.execPath,
3727
- args,
3728
- env: slock.spawnEnv,
3729
- sessionDir,
3730
- systemPromptPath
3731
- };
3732
- }
3733
- function isSystemFirstMessageTask2(message) {
3734
- return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
3735
- }
3736
- function buildPiSystemPrompt(config) {
3737
- return buildCliTransportSystemPrompt(config, {
3738
- toolPrefix: CHAT_MCP_TOOL_PREFIX2,
3739
- extraCriticalRules: [
3740
- "- 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."
3741
- ],
3742
- postStartupNotes: [
3743
- "**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."
3744
- ],
3745
- includeStdinNotificationSection: false,
3746
- messageNotificationStyle: "poll"
3747
- });
3748
- }
3749
- function contentText(content) {
3750
- if (!content) return "";
3751
- const chunks = [];
3752
- for (const item of content) {
3753
- if (item.type === "text" && typeof item.text === "string") {
3754
- chunks.push(item.text);
3755
- }
3756
- }
3757
- return chunks.join("");
3758
- }
3759
- function apiKeyErrorMessage(line) {
3760
- const trimmed = line.trim();
3761
- if (!trimmed) return null;
3762
- if (/no api key found/i.test(trimmed)) return trimmed;
3763
- if (/api key.+required/i.test(trimmed)) return trimmed;
3764
- if (/no models available/i.test(trimmed)) return trimmed;
3765
- return null;
3766
- }
3767
- var PiDriver = class {
3768
- id = "pi";
3769
- lifecycle = {
3770
- kind: "per_turn",
3771
- start: "defer_until_concrete_message",
3772
- exit: "terminate_on_turn_end",
3773
- inFlightWake: "coalesce_into_pending"
3774
- };
3775
- communication = {
3776
- chat: "slock_cli",
3777
- runtimeControl: "none"
3778
- };
3779
- session = {
3780
- recovery: "resume_or_fresh"
3781
- };
3782
- model = {
3783
- detectedModelsVerifiedAs: "launchable",
3784
- toLaunchSpec: (modelId, ctx) => {
3785
- if (!ctx) return { args: ["--model", modelId] };
3786
- const launchCtx = {
3787
- ...ctx,
3788
- config: {
3789
- ...ctx.config,
3790
- model: modelId
3791
- }
3792
- };
3793
- const launch = buildPiLaunchOptions(launchCtx);
3794
- return {
3795
- args: launch.args,
3796
- env: launch.env,
3797
- configFiles: [launch.systemPromptPath]
3798
- };
3799
- }
3800
- };
3801
- supportsStdinNotification = false;
3802
- mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
3803
- busyDeliveryMode = "none";
3804
- terminateProcessOnTurnEnd = true;
3805
- deferSpawnUntilMessage = true;
3806
- usesSlockCliForCommunication = true;
3807
- sessionId = null;
3808
- sessionAnnounced = false;
3809
- apiKeyErrorAnnounced = false;
3810
- turnEnded = false;
3811
- assistantTextByMessageId = /* @__PURE__ */ new Map();
3812
- shouldDeferWakeMessage(message) {
3813
- return isSystemFirstMessageTask2(message);
3814
- }
3815
- probe() {
3816
- const cliPath = resolveBundledPiCliPath();
3817
- if (!cliPath) return { available: false };
3818
- const version = readBundledPiVersion(cliPath) || void 0;
3819
- const unsupportedMessage = unsupportedPiVersionMessage(version);
3820
- if (unsupportedMessage) {
3821
- return {
3822
- available: false,
3823
- version: `${version} (requires >= ${MIN_BUNDLED_PI_VERSION})`
3824
- };
3825
- }
3826
- return { available: true, version };
3827
- }
3828
- async detectModels() {
3829
- return null;
3830
- }
3831
- spawn(ctx) {
3832
- this.sessionId = ctx.config.sessionId || null;
3833
- this.sessionAnnounced = false;
3834
- this.apiKeyErrorAnnounced = false;
3835
- this.turnEnded = false;
3836
- this.assistantTextByMessageId.clear();
3837
- const cliPath = resolveBundledPiCliPath();
3838
- if (!cliPath) throw new Error("Bundled Pi CLI not found in @earendil-works/pi-coding-agent");
3839
- const unsupportedMessage = unsupportedPiVersionMessage(readBundledPiVersion(cliPath));
3840
- if (unsupportedMessage) throw new Error(unsupportedMessage);
3841
- const launch = buildPiLaunchOptions(ctx, { cliPath });
3842
- const proc = spawn8(launch.command, launch.args, {
3843
- cwd: ctx.workingDirectory,
3844
- stdio: ["pipe", "pipe", "pipe"],
3845
- env: launch.env,
3846
- shell: process.platform === "win32"
3847
- });
3848
- proc.stdin?.end();
3849
- return { process: proc };
3850
- }
3851
- parseLine(line) {
3852
- let event;
3853
- try {
3854
- event = JSON.parse(line);
3855
- } catch {
3856
- if (this.apiKeyErrorAnnounced) return [];
3857
- const message = apiKeyErrorMessage(line);
3858
- if (!message) return [];
3859
- this.apiKeyErrorAnnounced = true;
3860
- this.turnEnded = true;
3861
- return [
3862
- { kind: "error", message },
3863
- { kind: "turn_end", sessionId: this.sessionId || void 0 }
3864
- ];
3865
- }
3866
- const events = [];
3867
- if (event.type === "session" && event.id) {
3868
- this.sessionId = event.id;
3869
- }
3870
- if (!this.sessionAnnounced && this.sessionId) {
3871
- events.push({ kind: "session_init", sessionId: this.sessionId });
3872
- this.sessionAnnounced = true;
3873
- }
3874
- switch (event.type) {
3875
- case "agent_start":
3876
- case "turn_start":
3877
- events.push({ kind: "thinking", text: "" });
3878
- break;
3879
- case "message_update":
3880
- case "message_end":
3881
- if (event.message?.role === "assistant") {
3882
- const key = event.message.id || "current";
3883
- const currentText = contentText(event.message.content);
3884
- const previousText = this.assistantTextByMessageId.get(key) ?? "";
3885
- if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
3886
- events.push({ kind: "text", text: currentText.slice(previousText.length) });
3887
- } else if (currentText && currentText !== previousText) {
3888
- events.push({ kind: "text", text: currentText });
3889
- }
3890
- this.assistantTextByMessageId.set(key, currentText);
3891
- if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
3892
- events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
3893
- }
3894
- }
3895
- break;
3896
- case "tool_execution_start":
3897
- events.push({
3898
- kind: "tool_call",
3899
- name: event.toolName || "unknown_tool",
3900
- input: event.args
3901
- });
3902
- break;
3903
- case "tool_execution_end":
3904
- events.push({
3905
- kind: "tool_output",
3906
- name: event.toolName || "unknown_tool"
3907
- });
3908
- if (event.isError) {
3909
- events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
3910
- }
3911
- break;
3912
- case "compaction_start":
3913
- events.push({ kind: "compaction_started" });
3914
- break;
3915
- case "compaction_end":
3916
- events.push({ kind: "compaction_finished" });
3917
- if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
3918
- break;
3919
- case "turn_end":
3920
- case "agent_end":
3921
- if (!this.turnEnded) {
3922
- events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
3923
- this.turnEnded = true;
3924
- }
3925
- break;
3926
- }
3927
- return events;
3928
- }
3929
- encodeStdinMessage(_text, _sessionId, _opts) {
3930
- return null;
3931
- }
3932
- buildSystemPrompt(config, _agentId) {
3933
- return buildPiSystemPrompt(config);
3934
- }
3935
- };
3936
-
3937
3529
  // src/drivers/index.ts
3938
3530
  var driverFactories = {
3939
3531
  claude: () => new ClaudeDriver(),
@@ -3942,8 +3534,7 @@ var driverFactories = {
3942
3534
  cursor: () => new CursorDriver(),
3943
3535
  gemini: () => new GeminiDriver(),
3944
3536
  kimi: () => new KimiDriver(),
3945
- opencode: () => new OpenCodeDriver(),
3946
- pi: () => new PiDriver()
3537
+ opencode: () => new OpenCodeDriver()
3947
3538
  };
3948
3539
  function getDriver(runtimeId) {
3949
3540
  const createDriver = driverFactories[runtimeId];
@@ -3956,7 +3547,7 @@ function getDriver(runtimeId) {
3956
3547
 
3957
3548
  // src/workspaces.ts
3958
3549
  import { readdir, rm, stat } from "fs/promises";
3959
- import path11 from "path";
3550
+ import path10 from "path";
3960
3551
  function isValidWorkspaceDirectoryName(directoryName) {
3961
3552
  return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
3962
3553
  }
@@ -3964,7 +3555,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
3964
3555
  if (!isValidWorkspaceDirectoryName(directoryName)) {
3965
3556
  return null;
3966
3557
  }
3967
- return path11.join(dataDir, directoryName);
3558
+ return path10.join(dataDir, directoryName);
3968
3559
  }
3969
3560
  function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
3970
3561
  return {
@@ -4013,7 +3604,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
4013
3604
  return summary;
4014
3605
  }
4015
3606
  const childSummaries = await Promise.all(
4016
- entries.map((entry) => summarizeWorkspaceEntry(path11.join(dirPath, entry.name), entry))
3607
+ entries.map((entry) => summarizeWorkspaceEntry(path10.join(dirPath, entry.name), entry))
4017
3608
  );
4018
3609
  for (const childSummary of childSummaries) {
4019
3610
  summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
@@ -4032,7 +3623,7 @@ async function scanWorkspaceDirectories(dataDir) {
4032
3623
  if (!entry.isDirectory()) {
4033
3624
  return null;
4034
3625
  }
4035
- const dirPath = path11.join(dataDir, entry.name);
3626
+ const dirPath = path10.join(dataDir, entry.name);
4036
3627
  try {
4037
3628
  const summary = await summarizeWorkspaceDirectory(dirPath);
4038
3629
  return {
@@ -4230,12 +3821,12 @@ function findSessionJsonl(root, predicate) {
4230
3821
  for (const entry of entries) {
4231
3822
  if (++visited > maxEntries) return null;
4232
3823
  if (!entry.isFile() || !predicate(entry.name)) continue;
4233
- return path12.join(dir, entry.name);
3824
+ return path11.join(dir, entry.name);
4234
3825
  }
4235
3826
  for (const entry of entries) {
4236
3827
  if (++visited > maxEntries) return null;
4237
3828
  if (!entry.isDirectory()) continue;
4238
- const found = visit(path12.join(dir, entry.name), depth - 1);
3829
+ const found = visit(path11.join(dir, entry.name), depth - 1);
4239
3830
  if (found) return found;
4240
3831
  }
4241
3832
  return null;
@@ -4248,10 +3839,10 @@ function safeSessionFilename(value) {
4248
3839
  }
4249
3840
  function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
4250
3841
  try {
4251
- const dir = path12.join(fallbackDir, ".slock", "runtime-sessions");
4252
- mkdirSync5(dir, { recursive: true });
4253
- const filePath = path12.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
4254
- writeFileSync8(filePath, JSON.stringify({
3842
+ const dir = path11.join(fallbackDir, ".slock", "runtime-sessions");
3843
+ mkdirSync4(dir, { recursive: true });
3844
+ const filePath = path11.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
3845
+ writeFileSync7(filePath, JSON.stringify({
4255
3846
  type: "runtime_session_handoff",
4256
3847
  runtime,
4257
3848
  sessionId,
@@ -4270,7 +3861,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
4270
3861
  }
4271
3862
  }
4272
3863
  function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
4273
- const directPath = path12.isAbsolute(sessionId) ? sessionId : null;
3864
+ const directPath = path11.isAbsolute(sessionId) ? sessionId : null;
4274
3865
  if (directPath) {
4275
3866
  try {
4276
3867
  if (statSync2(directPath).isFile()) {
@@ -4279,7 +3870,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), f
4279
3870
  } catch {
4280
3871
  }
4281
3872
  }
4282
- 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;
3873
+ const resolvedPath = runtime === "claude" ? findSessionJsonl(path11.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path11.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
4283
3874
  if (!resolvedPath && fallbackDir) {
4284
3875
  const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
4285
3876
  if (fallback) return fallback;
@@ -5412,26 +5003,26 @@ var AgentProcessManager = class _AgentProcessManager {
5412
5003
  this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
5413
5004
  try {
5414
5005
  const driver = this.driverResolver(config.runtime || "claude");
5415
- const agentDataDir = path12.join(this.dataDir, agentId);
5006
+ const agentDataDir = path11.join(this.dataDir, agentId);
5416
5007
  await mkdir(agentDataDir, { recursive: true });
5417
5008
  const runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
5418
- const memoryMdPath = path12.join(agentDataDir, "MEMORY.md");
5009
+ const memoryMdPath = path11.join(agentDataDir, "MEMORY.md");
5419
5010
  try {
5420
5011
  await access(memoryMdPath);
5421
5012
  } catch {
5422
5013
  const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
5423
5014
  await writeFile(memoryMdPath, initialMemoryMd);
5424
5015
  }
5425
- const notesDir = path12.join(agentDataDir, "notes");
5016
+ const notesDir = path11.join(agentDataDir, "notes");
5426
5017
  await mkdir(notesDir, { recursive: true });
5427
5018
  if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
5428
5019
  const seedFiles = buildOnboardingSeedFiles();
5429
5020
  for (const { relativePath, content } of seedFiles) {
5430
- const fullPath = path12.join(agentDataDir, relativePath);
5021
+ const fullPath = path11.join(agentDataDir, relativePath);
5431
5022
  try {
5432
5023
  await access(fullPath);
5433
5024
  } catch {
5434
- await mkdir(path12.dirname(fullPath), { recursive: true });
5025
+ await mkdir(path11.dirname(fullPath), { recursive: true });
5435
5026
  await writeFile(fullPath, content);
5436
5027
  }
5437
5028
  }
@@ -6110,7 +5701,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6110
5701
  return true;
6111
5702
  }
6112
5703
  async resetWorkspace(agentId) {
6113
- const agentDataDir = path12.join(this.dataDir, agentId);
5704
+ const agentDataDir = path11.join(this.dataDir, agentId);
6114
5705
  try {
6115
5706
  await rm2(agentDataDir, { recursive: true, force: true });
6116
5707
  logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
@@ -6147,7 +5738,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6147
5738
  return result;
6148
5739
  }
6149
5740
  buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
6150
- const workspacePath = path12.join(this.dataDir, agentId);
5741
+ const workspacePath = path11.join(this.dataDir, agentId);
6151
5742
  return {
6152
5743
  agentId,
6153
5744
  launchId,
@@ -6395,7 +5986,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6395
5986
  }
6396
5987
  // Workspace file browsing
6397
5988
  async getFileTree(agentId, dirPath) {
6398
- const agentDir = path12.join(this.dataDir, agentId);
5989
+ const agentDir = path11.join(this.dataDir, agentId);
6399
5990
  try {
6400
5991
  await stat2(agentDir);
6401
5992
  } catch {
@@ -6403,8 +5994,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6403
5994
  }
6404
5995
  let targetDir = agentDir;
6405
5996
  if (dirPath) {
6406
- const resolved = path12.resolve(agentDir, dirPath);
6407
- if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
5997
+ const resolved = path11.resolve(agentDir, dirPath);
5998
+ if (!resolved.startsWith(agentDir + path11.sep) && resolved !== agentDir) {
6408
5999
  return [];
6409
6000
  }
6410
6001
  targetDir = resolved;
@@ -6412,14 +6003,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6412
6003
  return this.listDirectoryChildren(targetDir, agentDir);
6413
6004
  }
6414
6005
  async readFile(agentId, filePath) {
6415
- const agentDir = path12.join(this.dataDir, agentId);
6416
- const resolved = path12.resolve(agentDir, filePath);
6417
- if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
6006
+ const agentDir = path11.join(this.dataDir, agentId);
6007
+ const resolved = path11.resolve(agentDir, filePath);
6008
+ if (!resolved.startsWith(agentDir + path11.sep) && resolved !== agentDir) {
6418
6009
  throw new Error("Access denied");
6419
6010
  }
6420
6011
  const info = await stat2(resolved);
6421
6012
  if (info.isDirectory()) throw new Error("Cannot read a directory");
6422
- const ext = path12.extname(resolved).toLowerCase();
6013
+ const ext = path11.extname(resolved).toLowerCase();
6423
6014
  if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
6424
6015
  if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
6425
6016
  const content = await readFile(resolved, "utf-8");
@@ -6454,13 +6045,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6454
6045
  const agent = this.agents.get(agentId);
6455
6046
  const runtime = runtimeHint || agent?.config.runtime || "claude";
6456
6047
  const home = os5.homedir();
6457
- const workspaceDir = path12.join(this.dataDir, agentId);
6048
+ const workspaceDir = path11.join(this.dataDir, agentId);
6458
6049
  const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
6459
6050
  const globalResults = await Promise.all(
6460
- paths.global.map((p) => this.scanSkillsDir(path12.join(home, p)))
6051
+ paths.global.map((p) => this.scanSkillsDir(path11.join(home, p)))
6461
6052
  );
6462
6053
  const workspaceResults = await Promise.all(
6463
- paths.workspace.map((p) => this.scanSkillsDir(path12.join(workspaceDir, p)))
6054
+ paths.workspace.map((p) => this.scanSkillsDir(path11.join(workspaceDir, p)))
6464
6055
  );
6465
6056
  const dedup = (skills) => {
6466
6057
  const seen = /* @__PURE__ */ new Set();
@@ -6489,7 +6080,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6489
6080
  const skills = [];
6490
6081
  for (const entry of entries) {
6491
6082
  if (entry.isDirectory() || entry.isSymbolicLink()) {
6492
- const skillMd = path12.join(dir, entry.name, "SKILL.md");
6083
+ const skillMd = path11.join(dir, entry.name, "SKILL.md");
6493
6084
  try {
6494
6085
  const content = await readFile(skillMd, "utf-8");
6495
6086
  const skill = this.parseSkillMd(entry.name, content);
@@ -6500,7 +6091,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6500
6091
  } else if (entry.name.endsWith(".md")) {
6501
6092
  const cmdName = entry.name.replace(/\.md$/, "");
6502
6093
  try {
6503
- const content = await readFile(path12.join(dir, entry.name), "utf-8");
6094
+ const content = await readFile(path11.join(dir, entry.name), "utf-8");
6504
6095
  const skill = this.parseSkillMd(cmdName, content);
6505
6096
  skill.sourcePath = dir;
6506
6097
  skills.push(skill);
@@ -7343,8 +6934,8 @@ ${RESPONSE_TARGET_HINT}`);
7343
6934
  const nodes = [];
7344
6935
  for (const entry of entries) {
7345
6936
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
7346
- const fullPath = path12.join(dir, entry.name);
7347
- const relativePath = path12.relative(rootDir, fullPath);
6937
+ const fullPath = path11.join(dir, entry.name);
6938
+ const relativePath = path11.relative(rootDir, fullPath);
7348
6939
  let info;
7349
6940
  try {
7350
6941
  info = await stat2(fullPath);
@@ -7647,9 +7238,9 @@ var ReminderCache = class {
7647
7238
 
7648
7239
  // src/machineLock.ts
7649
7240
  import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
7650
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
7241
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync8 } from "fs";
7651
7242
  import os6 from "os";
7652
- import path13 from "path";
7243
+ import path12 from "path";
7653
7244
  var INCOMPLETE_LOCK_STALE_MS = 3e4;
7654
7245
  var DaemonMachineLockConflictError = class extends Error {
7655
7246
  code = "DAEMON_MACHINE_LOCK_HELD";
@@ -7671,7 +7262,7 @@ function resolveDefaultMachineStateRoot() {
7671
7262
  return resolveSlockHomePath("machines");
7672
7263
  }
7673
7264
  function ownerPath(lockDir) {
7674
- return path13.join(lockDir, "owner.json");
7265
+ return path12.join(lockDir, "owner.json");
7675
7266
  }
7676
7267
  function readOwner(lockDir) {
7677
7268
  try {
@@ -7701,13 +7292,13 @@ function acquireDaemonMachineLock(options) {
7701
7292
  const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
7702
7293
  const fingerprint = apiKeyFingerprint(options.apiKey);
7703
7294
  const lockId = getDaemonMachineLockId(options.apiKey);
7704
- const machineDir = path13.join(rootDir, lockId);
7705
- const lockDir = path13.join(machineDir, "daemon.lock");
7295
+ const machineDir = path12.join(rootDir, lockId);
7296
+ const lockDir = path12.join(machineDir, "daemon.lock");
7706
7297
  const token = randomUUID2();
7707
- mkdirSync6(machineDir, { recursive: true });
7298
+ mkdirSync5(machineDir, { recursive: true });
7708
7299
  for (let attempt = 0; attempt < 2; attempt += 1) {
7709
7300
  try {
7710
- mkdirSync6(lockDir);
7301
+ mkdirSync5(lockDir);
7711
7302
  const owner = {
7712
7303
  pid: process.pid,
7713
7304
  token,
@@ -7717,7 +7308,7 @@ function acquireDaemonMachineLock(options) {
7717
7308
  apiKeyFingerprint: fingerprint.slice(0, 16)
7718
7309
  };
7719
7310
  try {
7720
- writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
7311
+ writeFileSync8(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
7721
7312
  `, { mode: 384 });
7722
7313
  } catch (err) {
7723
7314
  rmSync2(lockDir, { recursive: true, force: true });
@@ -7754,8 +7345,8 @@ function acquireDaemonMachineLock(options) {
7754
7345
  }
7755
7346
 
7756
7347
  // src/localTraceSink.ts
7757
- import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync4, rmSync as rmSync3, statSync as statSync4, writeFileSync as writeFileSync10 } from "fs";
7758
- import path14 from "path";
7348
+ import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync4, rmSync as rmSync3, statSync as statSync4, writeFileSync as writeFileSync9 } from "fs";
7349
+ import path13 from "path";
7759
7350
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
7760
7351
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
7761
7352
  var DEFAULT_MAX_FILES = 8;
@@ -7791,7 +7382,7 @@ var LocalRotatingTraceSink = class {
7791
7382
  currentSize = 0;
7792
7383
  sequence = 0;
7793
7384
  constructor(options) {
7794
- this.traceDir = path14.join(options.machineDir, "traces");
7385
+ this.traceDir = path13.join(options.machineDir, "traces");
7795
7386
  this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
7796
7387
  const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
7797
7388
  const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
@@ -7817,15 +7408,15 @@ var LocalRotatingTraceSink = class {
7817
7408
  return this.currentFile;
7818
7409
  }
7819
7410
  ensureFile(nextBytes) {
7820
- mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
7411
+ mkdirSync6(this.traceDir, { recursive: true, mode: 448 });
7821
7412
  const nowMs = this.nowMsProvider();
7822
7413
  const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
7823
7414
  if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
7824
- this.currentFile = path14.join(
7415
+ this.currentFile = path13.join(
7825
7416
  this.traceDir,
7826
7417
  `daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
7827
7418
  );
7828
- writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
7419
+ writeFileSync9(this.currentFile, "", { flag: "a", mode: 384 });
7829
7420
  this.currentSize = statSync4(this.currentFile).size;
7830
7421
  this.currentFileOpenedAtMs = nowMs;
7831
7422
  this.pruneOldFiles();
@@ -7836,7 +7427,7 @@ var LocalRotatingTraceSink = class {
7836
7427
  const excess = files.length - this.maxFiles;
7837
7428
  if (excess <= 0) return;
7838
7429
  for (const file of files.slice(0, excess)) {
7839
- rmSync3(path14.join(this.traceDir, file), { force: true });
7430
+ rmSync3(path13.join(this.traceDir, file), { force: true });
7840
7431
  }
7841
7432
  }
7842
7433
  };
@@ -7923,11 +7514,11 @@ function isDiagnosticErrorAttr(key) {
7923
7514
  import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
7924
7515
  import { gzipSync } from "zlib";
7925
7516
  import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
7926
- import path15 from "path";
7517
+ import path14 from "path";
7927
7518
 
7928
7519
  // src/directUploadCapability.ts
7929
- function joinUrl(base, path17) {
7930
- return `${base.replace(/\/+$/, "")}${path17}`;
7520
+ function joinUrl(base, path16) {
7521
+ return `${base.replace(/\/+$/, "")}${path16}`;
7931
7522
  }
7932
7523
  function jsonHeaders(apiKey) {
7933
7524
  return {
@@ -8146,7 +7737,7 @@ var DaemonTraceBundleUploader = class {
8146
7737
  }, nextMs);
8147
7738
  }
8148
7739
  async findUploadCandidates() {
8149
- const traceDir = path15.join(this.options.machineDir, "traces");
7740
+ const traceDir = path14.join(this.options.machineDir, "traces");
8150
7741
  let names;
8151
7742
  try {
8152
7743
  names = await readdir3(traceDir);
@@ -8158,8 +7749,8 @@ var DaemonTraceBundleUploader = class {
8158
7749
  const currentFile = this.options.currentFileProvider?.();
8159
7750
  const candidates = [];
8160
7751
  for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
8161
- const file = path15.join(traceDir, name);
8162
- if (currentFile && path15.resolve(file) === path15.resolve(currentFile)) continue;
7752
+ const file = path14.join(traceDir, name);
7753
+ if (currentFile && path14.resolve(file) === path14.resolve(currentFile)) continue;
8163
7754
  if (await this.isUploaded(file)) continue;
8164
7755
  try {
8165
7756
  const info = await stat3(file);
@@ -8233,8 +7824,8 @@ var DaemonTraceBundleUploader = class {
8233
7824
  }
8234
7825
  }
8235
7826
  uploadStatePath(file) {
8236
- const stateDir = path15.join(this.options.machineDir, "trace-uploads");
8237
- return path15.join(stateDir, `${path15.basename(file)}.uploaded.json`);
7827
+ const stateDir = path14.join(this.options.machineDir, "trace-uploads");
7828
+ return path14.join(stateDir, `${path14.basename(file)}.uploaded.json`);
8238
7829
  }
8239
7830
  async isUploaded(file) {
8240
7831
  try {
@@ -8246,9 +7837,9 @@ var DaemonTraceBundleUploader = class {
8246
7837
  }
8247
7838
  async markUploaded(file, metadata) {
8248
7839
  const stateFile = this.uploadStatePath(file);
8249
- await mkdir2(path15.dirname(stateFile), { recursive: true, mode: 448 });
7840
+ await mkdir2(path14.dirname(stateFile), { recursive: true, mode: 448 });
8250
7841
  await writeFile2(stateFile, `${JSON.stringify({
8251
- file: path15.basename(file),
7842
+ file: path14.basename(file),
8252
7843
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
8253
7844
  ...metadata
8254
7845
  }, null, 2)}
@@ -8267,10 +7858,10 @@ function readPositiveIntegerEnv2(name, fallback) {
8267
7858
 
8268
7859
  // src/core.ts
8269
7860
  var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
8270
- var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
8271
- function parseDaemonCliArgs(args, env = {}) {
7861
+ var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
7862
+ function parseDaemonCliArgs(args) {
8272
7863
  let serverUrl = "";
8273
- let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
7864
+ let apiKey = "";
8274
7865
  for (let i = 0; i < args.length; i++) {
8275
7866
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
8276
7867
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -8287,23 +7878,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
8287
7878
  }
8288
7879
  }
8289
7880
  function resolveChatBridgePath(moduleUrl = import.meta.url) {
8290
- const dirname = path16.dirname(fileURLToPath2(moduleUrl));
8291
- const jsPath = path16.resolve(dirname, "chat-bridge.js");
7881
+ const dirname = path15.dirname(fileURLToPath(moduleUrl));
7882
+ const jsPath = path15.resolve(dirname, "chat-bridge.js");
8292
7883
  try {
8293
7884
  accessSync(jsPath);
8294
7885
  return jsPath;
8295
7886
  } catch {
8296
- return path16.resolve(dirname, "chat-bridge.ts");
7887
+ return path15.resolve(dirname, "chat-bridge.ts");
8297
7888
  }
8298
7889
  }
8299
7890
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
8300
- const thisDir = path16.dirname(fileURLToPath2(moduleUrl));
8301
- const bundledDistPath = path16.resolve(thisDir, "cli", "index.js");
7891
+ const thisDir = path15.dirname(fileURLToPath(moduleUrl));
7892
+ const bundledDistPath = path15.resolve(thisDir, "cli", "index.js");
8302
7893
  try {
8303
7894
  accessSync(bundledDistPath);
8304
7895
  return bundledDistPath;
8305
7896
  } catch {
8306
- const workspaceDistPath = path16.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
7897
+ const workspaceDistPath = path15.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
8307
7898
  accessSync(workspaceDistPath);
8308
7899
  return workspaceDistPath;
8309
7900
  }
@@ -8482,7 +8073,7 @@ var DaemonCore = class {
8482
8073
  }
8483
8074
  resolveMachineStateRoot() {
8484
8075
  if (this.options.machineStateDir) return this.options.machineStateDir;
8485
- if (this.options.dataDir) return path16.join(path16.dirname(this.options.dataDir), "machines");
8076
+ if (this.options.dataDir) return path15.join(path15.dirname(this.options.dataDir), "machines");
8486
8077
  return resolveDefaultMachineStateRoot();
8487
8078
  }
8488
8079
  shouldEnableLocalTrace() {
@@ -8865,8 +8456,6 @@ var DaemonCore = class {
8865
8456
  };
8866
8457
 
8867
8458
  export {
8868
- DAEMON_API_KEY_ENV,
8869
- scrubDaemonAuthEnv,
8870
8459
  resolveWorkspaceDirectoryPath,
8871
8460
  scanWorkspaceDirectories,
8872
8461
  deleteWorkspaceDirectory,
package/dist/cli/index.js CHANGED
@@ -14101,7 +14101,6 @@ var SERVER_CAPABILITY_MATRIX = {
14101
14101
  var RUNTIMES = [
14102
14102
  { id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
14103
14103
  { id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
14104
- { id: "pi", displayName: "Pi", binary: "pi", supported: true },
14105
14104
  { id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
14106
14105
  { id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
14107
14106
  { id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
package/dist/core.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import {
2
- DAEMON_API_KEY_ENV,
3
2
  DAEMON_CLI_USAGE,
4
3
  DaemonCore,
5
4
  deleteWorkspaceDirectory,
@@ -9,14 +8,12 @@ import {
9
8
  resolveChatBridgePath,
10
9
  resolveSlockCliPath,
11
10
  resolveWorkspaceDirectoryPath,
12
- scanWorkspaceDirectories,
13
- scrubDaemonAuthEnv
14
- } from "./chunk-DOUUVDEA.js";
11
+ scanWorkspaceDirectories
12
+ } from "./chunk-RDRY2MW2.js";
15
13
  import {
16
14
  subscribeDaemonLogs
17
15
  } from "./chunk-B7XIMLOT.js";
18
16
  export {
19
- DAEMON_API_KEY_ENV,
20
17
  DAEMON_CLI_USAGE,
21
18
  DaemonCore,
22
19
  deleteWorkspaceDirectory,
@@ -27,6 +24,5 @@ export {
27
24
  resolveSlockCliPath,
28
25
  resolveWorkspaceDirectoryPath,
29
26
  scanWorkspaceDirectories,
30
- scrubDaemonAuthEnv,
31
27
  subscribeDaemonLogs
32
28
  };
package/dist/index.js CHANGED
@@ -2,14 +2,12 @@
2
2
  import {
3
3
  DAEMON_CLI_USAGE,
4
4
  DaemonCore,
5
- parseDaemonCliArgs,
6
- scrubDaemonAuthEnv
7
- } from "./chunk-DOUUVDEA.js";
5
+ parseDaemonCliArgs
6
+ } from "./chunk-RDRY2MW2.js";
8
7
  import "./chunk-B7XIMLOT.js";
9
8
 
10
9
  // src/index.ts
11
- var parsedArgs = parseDaemonCliArgs(process.argv.slice(2), process.env);
12
- scrubDaemonAuthEnv(process.env);
10
+ var parsedArgs = parseDaemonCliArgs(process.argv.slice(2));
13
11
  if (!parsedArgs) {
14
12
  console.error(DAEMON_CLI_USAGE);
15
13
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slock-ai/daemon",
3
- "version": "0.48.0-play.20260513145259",
3
+ "version": "0.48.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "slock-daemon": "dist/index.js"
@@ -36,7 +36,6 @@
36
36
  "release:alpha": "npm version prerelease --preid=alpha --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags"
37
37
  },
38
38
  "dependencies": {
39
- "@earendil-works/pi-coding-agent": "0.74.0",
40
39
  "@modelcontextprotocol/sdk": "^1.29.0",
41
40
  "commander": "^12.1.0",
42
41
  "https-proxy-agent": "^7.0.6",