@botiverse/raft-daemon 0.62.0 → 0.63.1-play.20260619141001

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.
@@ -1,6 +1,6 @@
1
1
  // src/core.ts
2
- import path17 from "path";
3
- import os8 from "os";
2
+ import path18 from "path";
3
+ import os9 from "os";
4
4
  import { createRequire as createRequire3 } from "module";
5
5
  import { accessSync } from "fs";
6
6
  import { fileURLToPath } from "url";
@@ -1602,9 +1602,9 @@ var FREE_MONTHLY_FILE_UPLOAD_LIMIT_BYTES = 100 * 1024 * 1024;
1602
1602
  import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync4 } from "fs";
1603
1603
  import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2, lstat, realpath, open } from "fs/promises";
1604
1604
  import { createHash as createHash3, randomUUID as randomUUID5 } from "crypto";
1605
- import path13 from "path";
1605
+ import path14 from "path";
1606
1606
  import { gzipSync } from "zlib";
1607
- import os6 from "os";
1607
+ import os7 from "os";
1608
1608
 
1609
1609
  // src/proxy.ts
1610
1610
  import { HttpsProxyAgent } from "https-proxy-agent";
@@ -1873,8 +1873,8 @@ async function executeResponseRequest(url, init, {
1873
1873
  }
1874
1874
 
1875
1875
  // src/directUploadCapability.ts
1876
- function joinUrl(base, path18) {
1877
- return `${base.replace(/\/+$/, "")}${path18}`;
1876
+ function joinUrl(base, path19) {
1877
+ return `${base.replace(/\/+$/, "")}${path19}`;
1878
1878
  }
1879
1879
  function jsonHeaders(apiKey) {
1880
1880
  return {
@@ -2582,6 +2582,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
2582
2582
  return candidates.filter((candidate) => existsSync(candidate.path));
2583
2583
  }
2584
2584
 
2585
+ // src/authEnv.ts
2586
+ var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
2587
+ var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
2588
+ function scrubDaemonAuthEnv(env) {
2589
+ delete env[DAEMON_API_KEY_ENV];
2590
+ return env;
2591
+ }
2592
+ function scrubDaemonChildEnv(env) {
2593
+ delete env[DAEMON_API_KEY_ENV];
2594
+ delete env[SLOCK_AGENT_TOKEN_ENV];
2595
+ return env;
2596
+ }
2597
+
2585
2598
  // src/agentCredentialProxy.ts
2586
2599
  import { randomBytes } from "crypto";
2587
2600
  import http from "http";
@@ -3024,7 +3037,7 @@ function projectApmRuntimeStallDiagnostic(input) {
3024
3037
  gatedPhase: input.runtimeDescriptorBusyDelivery === "gated" ? input.gatedPhase : void 0,
3025
3038
  outstandingToolUses: input.outstandingToolUses,
3026
3039
  compacting: input.compacting,
3027
- reviewing: input.reviewing === true ? true : void 0,
3040
+ ...input.reviewing === true ? { reviewing: true } : {},
3028
3041
  recentStderrCount: input.recentStderrCount,
3029
3042
  recentStdoutCount: input.recentStdoutCount,
3030
3043
  ...input.runtimeTraceCounterAttrs ?? {}
@@ -4148,7 +4161,20 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
4148
4161
  var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
4149
4162
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
4150
4163
  var RAW_CREDENTIAL_ENV_DENYLIST = [
4151
- "SLOCK_AGENT_CREDENTIAL_KEY"
4164
+ "SLOCK_AGENT_TOKEN",
4165
+ "SLOCK_AGENT_CREDENTIAL_KEY",
4166
+ "SLOCK_AGENT_CREDENTIAL_KEY_FILE"
4167
+ ];
4168
+ var WORKSPACE_CLI_TRANSPORT_FILENAMES = [
4169
+ "agent-token",
4170
+ "slock",
4171
+ "slock.cmd",
4172
+ "slock.ps1",
4173
+ "raft",
4174
+ "raft.cmd",
4175
+ "raft.ps1",
4176
+ "opencli",
4177
+ "opencli.cmd"
4152
4178
  ];
4153
4179
  function deriveCliFallbackCandidates(cliPath) {
4154
4180
  if (!cliPath || cliPath === "__cli") return [];
@@ -4210,6 +4236,18 @@ function resolveOpencliBinPath() {
4210
4236
  return null;
4211
4237
  }
4212
4238
  }
4239
+ function buildCliTransportDir(slockHome, agentId, launchId) {
4240
+ return path2.join(slockHome, "cli-transport", safePathPart(agentId), buildCliTransportLaunchPart(launchId));
4241
+ }
4242
+ function buildCliTransportLaunchPart(launchId) {
4243
+ return safePathPart(launchId || `pid-${process.pid}`);
4244
+ }
4245
+ function cleanupWorkspaceCliTransportFiles(workingDirectory) {
4246
+ const legacySlockDir = path2.join(workingDirectory, ".slock");
4247
+ for (const filename of WORKSPACE_CLI_TRANSPORT_FILENAMES) {
4248
+ rmSync(path2.join(legacySlockDir, filename), { force: true });
4249
+ }
4250
+ }
4213
4251
  function writeOpencliWrapper(slockDir, opencliBinPath, platform = process.platform) {
4214
4252
  const fallbacks = deriveOpencliFallbackCandidates(opencliBinPath);
4215
4253
  let binPath = opencliBinPath;
@@ -4340,9 +4378,10 @@ async function prepareCliTransport(ctx, extraEnv = {}, platform = process.platfo
4340
4378
  );
4341
4379
  }
4342
4380
  }
4343
- const slockDir = path2.join(ctx.workingDirectory, ".slock");
4381
+ const slockHome = ctx.slockHome ? path2.resolve(ctx.slockHome) : resolveSlockHome();
4382
+ cleanupWorkspaceCliTransportFiles(ctx.workingDirectory);
4383
+ const slockDir = buildCliTransportDir(slockHome, ctx.agentId, ctx.launchId);
4344
4384
  mkdirSync(slockDir, { recursive: true });
4345
- const slockHome = resolveSlockHome();
4346
4385
  const tokenFile = path2.join(slockDir, "agent-token");
4347
4386
  const agentCredentialKey = ctx.config.agentCredentialKey;
4348
4387
  let agentCredentialProxy = null;
@@ -4357,7 +4396,7 @@ async function prepareCliTransport(ctx, extraEnv = {}, platform = process.platfo
4357
4396
  activeCapabilities: DEFAULT_ACTIVE_CAPABILITIES,
4358
4397
  inboxCoordinator: ctx.agentCredentialProxyInboxCoordinator
4359
4398
  });
4360
- const launchPart = safePathPart(ctx.launchId || `pid-${process.pid}`);
4399
+ const launchPart = buildCliTransportLaunchPart(ctx.launchId);
4361
4400
  const proxyTokenDir = path2.join(slockHome, "agent-proxy-tokens", safePathPart(ctx.agentId));
4362
4401
  mkdirSync(proxyTokenDir, { recursive: true, mode: 448 });
4363
4402
  agentCredentialProxyTokenFile = path2.join(proxyTokenDir, `${launchPart}.token`);
@@ -4474,10 +4513,9 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
4474
4513
  ...ctx.launchId ? { SLOCK_AGENT_LAUNCH_ID: ctx.launchId } : {},
4475
4514
  ...ctx.cliTransportTraceDir ? { [CLI_TRANSPORT_TRACE_DIR_ENV]: ctx.cliTransportTraceDir } : {},
4476
4515
  SLOCK_SERVER_URL: ctx.config.serverUrl,
4477
- ...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
4478
4516
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
4479
4517
  };
4480
- delete spawnEnv.SLOCK_AGENT_TOKEN;
4518
+ scrubDaemonChildEnv(spawnEnv);
4481
4519
  for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
4482
4520
  delete spawnEnv[key];
4483
4521
  }
@@ -4485,9 +4523,7 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
4485
4523
  delete spawnEnv.SLOCK_AGENT_PROXY_TOKEN;
4486
4524
  delete spawnEnv.SLOCK_AGENT_PROXY_TOKEN_FILE;
4487
4525
  delete spawnEnv.SLOCK_AGENT_ACTIVE_CAPABILITIES;
4488
- if (agentCredentialProxy) {
4489
- delete spawnEnv.SLOCK_AGENT_TOKEN_FILE;
4490
- }
4526
+ delete spawnEnv.SLOCK_AGENT_TOKEN_FILE;
4491
4527
  return {
4492
4528
  slockDir,
4493
4529
  tokenFile,
@@ -4912,7 +4948,7 @@ function requiresWindowsShell(command, platform = process.platform) {
4912
4948
  }
4913
4949
  function resolveCommandOnPath(command, deps = {}) {
4914
4950
  const platform = deps.platform ?? process.platform;
4915
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
4951
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
4916
4952
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
4917
4953
  const existsSyncFn = deps.existsSyncFn ?? existsSync3;
4918
4954
  if (platform === "win32") {
@@ -4938,7 +4974,7 @@ function firstExistingPath(candidates, deps = {}) {
4938
4974
  return null;
4939
4975
  }
4940
4976
  function readCommandVersion(command, args = [], deps = {}) {
4941
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
4977
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
4942
4978
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
4943
4979
  try {
4944
4980
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -5177,8 +5213,47 @@ var ClaudeDriver = class {
5177
5213
  // src/drivers/codex.ts
5178
5214
  import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
5179
5215
  import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
5216
+ import os4 from "os";
5217
+ import path7 from "path";
5218
+
5219
+ // src/drivers/codexHome.ts
5180
5220
  import os3 from "os";
5181
5221
  import path6 from "path";
5222
+ function readConfiguredCodexHome(env) {
5223
+ const raw = env.CODEX_HOME;
5224
+ return typeof raw === "string" && raw.trim().length > 0 ? raw : null;
5225
+ }
5226
+ function resolveCodexHomeRootFromEnv(env = process.env, opts = {}) {
5227
+ const raw = readConfiguredCodexHome(env);
5228
+ if (raw) {
5229
+ return path6.resolve(opts.cwd ?? process.cwd(), raw);
5230
+ }
5231
+ return path6.join(opts.defaultHomeDir ?? os3.homedir(), ".codex");
5232
+ }
5233
+ function hasConfiguredCodexHome(config, baseEnv = process.env) {
5234
+ const launchRuntimeFields = config ? runtimeConfigToLaunchFields(config) : null;
5235
+ return Boolean(readConfiguredCodexHome({
5236
+ ...baseEnv,
5237
+ ...launchRuntimeFields?.envVars || {}
5238
+ }));
5239
+ }
5240
+ function resolveCodexHomeRootFromConfig(config, defaultHomeDir, cwd, baseEnv = process.env, _opts = {}) {
5241
+ const launchRuntimeFields = runtimeConfigToLaunchFields(config);
5242
+ const env = {
5243
+ ...baseEnv,
5244
+ ...launchRuntimeFields.envVars || {}
5245
+ };
5246
+ return resolveCodexHomeRootFromEnv(env, { defaultHomeDir, cwd });
5247
+ }
5248
+ function codexStateRootCandidates(homeDirOrCodexRoot) {
5249
+ return [
5250
+ homeDirOrCodexRoot,
5251
+ path6.join(homeDirOrCodexRoot, ".codex")
5252
+ ];
5253
+ }
5254
+ function codexSessionRootCandidates(homeDirOrCodexRoot) {
5255
+ return codexStateRootCandidates(homeDirOrCodexRoot).map((root) => path6.join(root, "sessions"));
5256
+ }
5182
5257
 
5183
5258
  // src/runtimeTurnState.ts
5184
5259
  var RuntimeTurnState = class {
@@ -5359,7 +5434,7 @@ function codexNotificationDiagnosticEvent(message) {
5359
5434
  return null;
5360
5435
  }
5361
5436
  const sessionId = codexMessageThreadId(message);
5362
- const path18 = boundedString(params.path);
5437
+ const path19 = boundedString(params.path);
5363
5438
  return {
5364
5439
  kind: "runtime_diagnostic",
5365
5440
  severity: "warning",
@@ -5367,12 +5442,42 @@ function codexNotificationDiagnosticEvent(message) {
5367
5442
  itemType: message.method,
5368
5443
  message: diagnosticMessage,
5369
5444
  ...details ? { details } : {},
5370
- ...path18 ? { path: path18 } : {},
5445
+ ...path19 ? { path: path19 } : {},
5371
5446
  ...params.range !== void 0 ? { range: params.range } : {},
5372
5447
  payloadBytes: payloadBytes(params),
5373
5448
  ...sessionId ? { sessionId } : {}
5374
5449
  };
5375
5450
  }
5451
+ function codexThreadStatusChangedEvent(message) {
5452
+ if (message.method !== "thread/status/changed") return null;
5453
+ const params = message.params ?? {};
5454
+ const status = params.status ?? params.thread?.status;
5455
+ if (!status || typeof status !== "object") return null;
5456
+ const statusType = nonEmptyString2(status.type);
5457
+ const sessionId = codexMessageThreadId(message);
5458
+ if (statusType === "systemError") {
5459
+ const statusMessage = boundedString(status.message) ?? boundedString(status.error?.message) ?? getCodexNotificationErrorMessage(params) ?? "Codex thread entered system error state";
5460
+ return {
5461
+ kind: "error",
5462
+ message: statusMessage
5463
+ };
5464
+ }
5465
+ if (statusType !== "active" || !Array.isArray(status.activeFlags)) return null;
5466
+ const activeFlags = status.activeFlags.filter((flag) => typeof flag === "string");
5467
+ const waitFlags = activeFlags.filter((flag) => flag === "waitingOnApproval" || flag === "waitingOnUserInput");
5468
+ if (waitFlags.length === 0) return null;
5469
+ const waitLabel = waitFlags.includes("waitingOnApproval") && waitFlags.includes("waitingOnUserInput") ? "approval and user input" : waitFlags.includes("waitingOnApproval") ? "approval" : "user input";
5470
+ return {
5471
+ kind: "runtime_diagnostic",
5472
+ severity: "warning",
5473
+ source: "codex_app_server_notification",
5474
+ itemType: "thread/status/changed",
5475
+ message: `Codex thread is waiting on ${waitLabel}`,
5476
+ details: `Active flags: ${waitFlags.join(", ")}`,
5477
+ payloadBytes: payloadBytes(params),
5478
+ ...sessionId ? { sessionId } : {}
5479
+ };
5480
+ }
5376
5481
  function joinReasoningSummaryText(item) {
5377
5482
  const summary = Array.isArray(item.summary) ? item.summary.filter((entry) => typeof entry === "string") : [];
5378
5483
  return summary.join("\n").trim();
@@ -5400,10 +5505,27 @@ function codexMcpToolName(item) {
5400
5505
  function codexMessageThreadId(message) {
5401
5506
  return nonEmptyString2(message.params?.threadId) ?? nonEmptyString2(message.params?.thread?.id) ?? nonEmptyString2(message.params?.sessionId);
5402
5507
  }
5508
+ function codexAgentMessagePhase(value) {
5509
+ return typeof value === "string" && value.length > 0 ? value : null;
5510
+ }
5511
+ function codexAgentMessageDeltaPhase(message) {
5512
+ return codexAgentMessagePhase(message.params?.phase) ?? codexAgentMessagePhase(message.params?.item?.phase);
5513
+ }
5514
+ function isUserVisibleAgentMessagePhase(phase) {
5515
+ return phase === null || phase === "final_answer";
5516
+ }
5517
+ function codexSuppressedAgentMessageEvent(itemType, phase, text, itemId) {
5518
+ return codexNotificationProgressEvent(itemType, {
5519
+ ...typeof itemId === "string" ? { itemId } : {},
5520
+ ...phase ? { phase } : {},
5521
+ bytes: Buffer.byteLength(text, "utf8")
5522
+ });
5523
+ }
5403
5524
  var CodexEventNormalizer = class {
5404
5525
  currentThreadId = null;
5405
5526
  sessionAnnounced = false;
5406
5527
  streamedAgentMessageIds = /* @__PURE__ */ new Set();
5528
+ agentMessagePhases = /* @__PURE__ */ new Map();
5407
5529
  streamedReasoningIds = /* @__PURE__ */ new Set();
5408
5530
  fileChangeToolCallCounts = /* @__PURE__ */ new Map();
5409
5531
  turnState = new RuntimeTurnState();
@@ -5412,6 +5534,7 @@ var CodexEventNormalizer = class {
5412
5534
  this.turnState.reset();
5413
5535
  this.sessionAnnounced = false;
5414
5536
  this.streamedAgentMessageIds.clear();
5537
+ this.agentMessagePhases.clear();
5415
5538
  this.streamedReasoningIds.clear();
5416
5539
  this.fileChangeToolCallCounts.clear();
5417
5540
  }
@@ -5489,12 +5612,17 @@ var CodexEventNormalizer = class {
5489
5612
  case "item/agentMessage/delta": {
5490
5613
  const delta = message.params?.delta;
5491
5614
  const itemId = message.params?.itemId;
5615
+ const phase = codexAgentMessageDeltaPhase(message) ?? (typeof itemId === "string" ? this.agentMessagePhases.get(itemId) ?? null : null);
5492
5616
  if (typeof itemId === "string") {
5493
5617
  this.streamedAgentMessageIds.add(itemId);
5494
5618
  }
5495
5619
  if (typeof delta === "string" && delta.length > 0) {
5496
5620
  this.turnState.markProgress();
5497
- events.push({ kind: "text", text: delta });
5621
+ if (isUserVisibleAgentMessagePhase(phase)) {
5622
+ events.push({ kind: "text", text: delta });
5623
+ } else {
5624
+ events.push(codexSuppressedAgentMessageEvent("agent_message_non_final_delta", phase, delta, itemId));
5625
+ }
5498
5626
  }
5499
5627
  break;
5500
5628
  }
@@ -5545,6 +5673,13 @@ var CodexEventNormalizer = class {
5545
5673
  }
5546
5674
  break;
5547
5675
  }
5676
+ case "thread/status/changed": {
5677
+ const event = codexThreadStatusChangedEvent(message);
5678
+ if (event) {
5679
+ events.push(event);
5680
+ }
5681
+ break;
5682
+ }
5548
5683
  case "item/started":
5549
5684
  case "item/completed": {
5550
5685
  const item = message.params?.item;
@@ -5566,12 +5701,21 @@ var CodexEventNormalizer = class {
5566
5701
  }
5567
5702
  break;
5568
5703
  case "agentMessage":
5704
+ if ((isStarted || isCompleted) && typeof item.id === "string") {
5705
+ this.agentMessagePhases.set(item.id, codexAgentMessagePhase(item.phase));
5706
+ }
5569
5707
  if (isCompleted && typeof item.id === "string" && !this.streamedAgentMessageIds.has(item.id) && typeof item.text === "string" && item.text.length > 0) {
5708
+ const phase = codexAgentMessagePhase(item.phase);
5570
5709
  this.turnState.markProgress();
5571
- events.push({ kind: "text", text: item.text });
5710
+ if (isUserVisibleAgentMessagePhase(phase)) {
5711
+ events.push({ kind: "text", text: item.text });
5712
+ } else {
5713
+ events.push(codexSuppressedAgentMessageEvent("agent_message_non_final_completed", phase, item.text, item.id));
5714
+ }
5572
5715
  }
5573
5716
  if (isCompleted && typeof item.id === "string") {
5574
5717
  this.streamedAgentMessageIds.delete(item.id);
5718
+ this.agentMessagePhases.delete(item.id);
5575
5719
  }
5576
5720
  break;
5577
5721
  case "commandExecution":
@@ -5710,14 +5854,15 @@ var CodexEventNormalizer = class {
5710
5854
 
5711
5855
  // src/drivers/codex.ts
5712
5856
  var CODEX_DESKTOP_BUNDLE_PATH = "/Applications/Codex.app/Contents/Resources/codex";
5857
+ var CODEX_APP_SERVER_PROBE_ARGS = ["app-server", "--help"];
5713
5858
  function isWindowsSandboxRunner(commandPath) {
5714
- return path6.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
5859
+ return path7.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
5715
5860
  }
5716
5861
  function resolveWindowsNpmCodexEntry(deps = {}) {
5717
5862
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
5718
5863
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
5719
5864
  const env = deps.env ?? process.env;
5720
- const winPath = path6.win32;
5865
+ const winPath = path7.win32;
5721
5866
  try {
5722
5867
  const globalRoot = String(execFileSyncFn("npm", ["root", "-g"], {
5723
5868
  encoding: "utf8",
@@ -5739,7 +5884,7 @@ function resolveWindowsCodexDesktopEntry(deps = {}) {
5739
5884
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
5740
5885
  const env = deps.env ?? process.env;
5741
5886
  const homeDir = deps.homeDir;
5742
- const winPath = path6.win32;
5887
+ const winPath = path7.win32;
5743
5888
  const candidates = [
5744
5889
  env.LOCALAPPDATA ? winPath.join(env.LOCALAPPDATA, "OpenAI", "Codex", "bin", "codex.exe") : null,
5745
5890
  env.USERPROFILE ? winPath.join(env.USERPROFILE, "AppData", "Local", "OpenAI", "Codex", "bin", "codex.exe") : null,
@@ -5750,69 +5895,158 @@ function resolveWindowsCodexDesktopEntry(deps = {}) {
5750
5895
  }
5751
5896
  return null;
5752
5897
  }
5753
- function resolveCodexCommand(deps = {}) {
5898
+ function codexSpawnCandidates(deps = {}) {
5754
5899
  const platform = deps.platform ?? process.platform;
5755
5900
  if (platform === "win32") {
5901
+ const candidates2 = [];
5756
5902
  const npmEntry = resolveWindowsNpmCodexEntry(deps);
5757
- if (npmEntry) return npmEntry;
5903
+ if (npmEntry) {
5904
+ candidates2.push({
5905
+ source: "npm_global",
5906
+ command: process.execPath,
5907
+ argsPrefix: [npmEntry],
5908
+ shell: false
5909
+ });
5910
+ }
5758
5911
  const command = resolveCommandOnPath("codex", deps);
5759
- if (command && !isWindowsSandboxRunner(command)) return command;
5760
- return resolveWindowsCodexDesktopEntry(deps);
5912
+ if (command && !isWindowsSandboxRunner(command)) {
5913
+ candidates2.push({
5914
+ source: "path",
5915
+ command,
5916
+ argsPrefix: [],
5917
+ shell: requiresWindowsShell(command, platform)
5918
+ });
5919
+ }
5920
+ const desktopEntry = resolveWindowsCodexDesktopEntry(deps);
5921
+ if (desktopEntry && !candidates2.some((candidate) => candidate.command === desktopEntry)) {
5922
+ candidates2.push({
5923
+ source: "desktop_install",
5924
+ command: desktopEntry,
5925
+ argsPrefix: [],
5926
+ shell: false
5927
+ });
5928
+ }
5929
+ return candidates2;
5761
5930
  }
5931
+ const candidates = [];
5762
5932
  const pathCommand = resolveCommandOnPath("codex", deps);
5763
- if (pathCommand) return pathCommand;
5933
+ if (pathCommand) {
5934
+ candidates.push({
5935
+ source: "path",
5936
+ command: pathCommand,
5937
+ argsPrefix: [],
5938
+ shell: false
5939
+ });
5940
+ }
5764
5941
  if (platform === "darwin") {
5765
- return firstExistingPath([CODEX_DESKTOP_BUNDLE_PATH], deps);
5942
+ const bundleCommand = firstExistingPath([CODEX_DESKTOP_BUNDLE_PATH], deps);
5943
+ if (bundleCommand && !candidates.some((candidate) => candidate.command === bundleCommand)) {
5944
+ candidates.push({
5945
+ source: "desktop_bundle",
5946
+ command: bundleCommand,
5947
+ argsPrefix: [],
5948
+ shell: false
5949
+ });
5950
+ }
5766
5951
  }
5767
- return null;
5952
+ return candidates;
5953
+ }
5954
+ function formatCodexCandidate(candidate) {
5955
+ return [candidate.command, ...candidate.argsPrefix].join(" ");
5956
+ }
5957
+ function describeCodexProbeFailure(error) {
5958
+ if (error && typeof error === "object") {
5959
+ const candidate = error;
5960
+ if (typeof candidate.status === "number") return `exit status ${candidate.status}`;
5961
+ if (typeof candidate.signal === "string") return `terminated by ${candidate.signal}`;
5962
+ if (typeof candidate.code === "string") return candidate.code;
5963
+ }
5964
+ return "probe failed";
5965
+ }
5966
+ function validateCodexAppServer(candidate, deps = {}) {
5967
+ const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
5968
+ const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
5969
+ try {
5970
+ execFileSyncFn(candidate.command, [...candidate.argsPrefix, ...CODEX_APP_SERVER_PROBE_ARGS], {
5971
+ stdio: ["ignore", "pipe", "pipe"],
5972
+ env,
5973
+ timeout: 5e3,
5974
+ shell: candidate.shell
5975
+ });
5976
+ return null;
5977
+ } catch (error) {
5978
+ return describeCodexProbeFailure(error);
5979
+ }
5980
+ }
5981
+ function readCodexCandidateVersion(candidate, deps = {}) {
5982
+ const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
5983
+ const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
5984
+ try {
5985
+ const output = execFileSyncFn(candidate.command, [...candidate.argsPrefix, "--version"], {
5986
+ stdio: ["ignore", "pipe", "pipe"],
5987
+ env,
5988
+ timeout: 5e3,
5989
+ shell: candidate.shell
5990
+ });
5991
+ return (Buffer.isBuffer(output) ? output.toString("utf8") : String(output ?? "")).trim().split(/\r?\n/)[0] || null;
5992
+ } catch {
5993
+ return null;
5994
+ }
5995
+ }
5996
+ function resolveCompatibleCodexCandidate(deps = {}) {
5997
+ const rejected = [];
5998
+ for (const candidate of codexSpawnCandidates(deps)) {
5999
+ const failure = validateCodexAppServer(candidate, deps);
6000
+ if (!failure) return { candidate, rejected };
6001
+ rejected.push(`${candidate.source} ${formatCodexCandidate(candidate)} rejected: app-server probe ${failure}`);
6002
+ }
6003
+ return { candidate: null, rejected };
5768
6004
  }
5769
6005
  function probeCodex(deps = {}) {
5770
- if ((deps.platform ?? process.platform) === "win32") {
5771
- try {
5772
- const resolved = resolveCodexSpawn([], deps);
5773
- return {
5774
- available: true,
5775
- version: readCommandVersion(resolved.command, resolved.args, deps) ?? void 0
5776
- };
5777
- } catch {
5778
- return { available: false };
5779
- }
6006
+ const { candidate, rejected } = resolveCompatibleCodexCandidate(deps);
6007
+ const diagnostic = rejected.length > 0 ? rejected.join("; ") : void 0;
6008
+ if (!candidate) {
6009
+ return {
6010
+ available: false,
6011
+ diagnostic: diagnostic ?? "No Codex CLI app-server candidate was found."
6012
+ };
5780
6013
  }
5781
- const command = resolveCodexCommand(deps);
5782
- if (!command) return { available: false };
5783
6014
  return {
5784
6015
  available: true,
5785
- version: readCommandVersion(command, [], deps) ?? void 0
6016
+ version: readCodexCandidateVersion(candidate, deps) ?? void 0,
6017
+ ...diagnostic ? { diagnostic } : {}
5786
6018
  };
5787
6019
  }
5788
6020
  function resolveCodexSpawn(commandArgs, deps = {}) {
5789
- if ((deps.platform ?? process.platform) !== "win32") {
5790
- return { command: resolveCodexCommand(deps) ?? "codex", args: commandArgs, shell: false };
5791
- }
5792
- const codexEntry = resolveWindowsNpmCodexEntry(deps);
5793
- if (codexEntry) {
5794
- return {
5795
- command: process.execPath,
5796
- args: [codexEntry, ...commandArgs],
5797
- shell: false
5798
- };
5799
- }
5800
- const command = resolveCommandOnPath("codex", deps);
5801
- if (command && !isWindowsSandboxRunner(command)) {
5802
- return { command, args: commandArgs, shell: requiresWindowsShell(command, deps.platform) };
6021
+ const { candidate, rejected } = resolveCompatibleCodexCandidate(deps);
6022
+ if (candidate) {
6023
+ return { command: candidate.command, args: [...candidate.argsPrefix, ...commandArgs], shell: candidate.shell };
5803
6024
  }
5804
- const desktopEntry = resolveWindowsCodexDesktopEntry(deps);
5805
- if (desktopEntry) {
5806
- return { command: desktopEntry, args: commandArgs, shell: false };
6025
+ const diagnostic = rejected.length > 0 ? ` Rejected candidates: ${rejected.join("; ")}.` : "";
6026
+ if ((deps.platform ?? process.platform) === "win32") {
6027
+ throw new Error(
6028
+ "Cannot resolve a compatible Codex CLI app-server entry point on Windows. Install Codex Desktop or install @openai/codex globally via npm (npm i -g @openai/codex). Ignoring .codex/.sandbox-bin/codex-command-runner because it is a sandbox helper, not the Codex CLI." + diagnostic
6029
+ );
5807
6030
  }
5808
- throw new Error(
5809
- "Cannot resolve Codex CLI entry point on Windows. Install Codex Desktop or install @openai/codex globally via npm (npm i -g @openai/codex). Ignoring .codex/.sandbox-bin/codex-command-runner because it is a sandbox helper, not the Codex CLI."
5810
- );
6031
+ throw new Error(`Cannot resolve a compatible Codex CLI app-server entry point.${diagnostic}`);
5811
6032
  }
5812
6033
  function isCodexMissingRolloutError(message) {
5813
6034
  return /\bno\s+rollout\s+found\b/i.test(message) || /\bmissing\s+rollout\b/i.test(message) || /\brollout\b.*\b(not found|missing)\b/i.test(message) || /\bthread\b.*\b(not found|missing)\b/i.test(message) || /\bmissing\s+thread\b/i.test(message);
5814
6035
  }
5815
- function classifyCodexResumeError(message) {
6036
+ function codexMissingRolloutRecoveryMessage() {
6037
+ return "Codex could not resume its previous thread; Slock started a fresh Codex thread.";
6038
+ }
6039
+ function codexMissingRolloutRecoveryDetails() {
6040
+ return "Use Slock conversation history and local MEMORY.md/notes as the recovery point; do not assume prior Codex thread context is loaded.";
6041
+ }
6042
+ function prependCodexRecoveryNotice(prompt, recovery) {
6043
+ return `${recovery.message}
6044
+
6045
+ ${recovery.details}
6046
+
6047
+ ${prompt}`;
6048
+ }
6049
+ function classifyCodexResumeError(message, requestedSessionId) {
5816
6050
  if (isCodexMissingRolloutError(message)) {
5817
6051
  return {
5818
6052
  kind: "missing_rollout",
@@ -5826,6 +6060,15 @@ function classifyCodexResumeError(message) {
5826
6060
  resume_error_class: "missing_rollout",
5827
6061
  recovery_action: "fallback_fresh_thread"
5828
6062
  }
6063
+ },
6064
+ recovery: {
6065
+ kind: "runtime_recovery",
6066
+ source: "codex_resume_missing_rollout",
6067
+ resumeErrorClass: "missing_rollout",
6068
+ recoveryAction: "fallback_fresh_thread",
6069
+ message: codexMissingRolloutRecoveryMessage(),
6070
+ details: codexMissingRolloutRecoveryDetails(),
6071
+ ...requestedSessionId ? { requestedSessionId } : {}
5829
6072
  }
5830
6073
  };
5831
6074
  }
@@ -5849,6 +6092,26 @@ function isJsonRpcResponse(message) {
5849
6092
  function isCodexServerRequest(message) {
5850
6093
  return message.id !== void 0 && typeof message.method === "string" && !isJsonRpcResponse(message);
5851
6094
  }
6095
+ function payloadBytes2(value) {
6096
+ try {
6097
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
6098
+ } catch {
6099
+ return void 0;
6100
+ }
6101
+ }
6102
+ function extractCodexHome(result) {
6103
+ if (!result || typeof result !== "object") return null;
6104
+ const candidate = result.codexHome;
6105
+ return typeof candidate === "string" && candidate.trim().length > 0 ? candidate : null;
6106
+ }
6107
+ function isCompatibleInitializeResult(result) {
6108
+ if (!result || typeof result !== "object" || Array.isArray(result)) return false;
6109
+ const userAgent = result.userAgent;
6110
+ return typeof userAgent === "string" && userAgent.trim().length > 0;
6111
+ }
6112
+ function unsupportedInitializeResultMessage() {
6113
+ return "Codex app-server initialize response is missing the expected userAgent handshake field; upgrade Codex CLI to a compatible app-server build.";
6114
+ }
5852
6115
  var CodexDriver = class {
5853
6116
  id = "codex";
5854
6117
  lifecycle = {
@@ -5919,9 +6182,16 @@ var CodexDriver = class {
5919
6182
  pendingThreadRequestId = null;
5920
6183
  pendingThreadRequestMethod = null;
5921
6184
  pendingResumeFallbackParams = null;
6185
+ pendingResumeThreadId = null;
5922
6186
  pendingInitialTurnRequestId = null;
6187
+ pendingDeliveryRequests = /* @__PURE__ */ new Map();
5923
6188
  initialTurnStarted = false;
5924
6189
  normalizer = new CodexEventNormalizer();
6190
+ codexHomeRoot = null;
6191
+ spawnWorkingDirectory = null;
6192
+ get currentRuntimeHomeDir() {
6193
+ return this.codexHomeRoot;
6194
+ }
5925
6195
  async spawn(ctx) {
5926
6196
  const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
5927
6197
  this.process = null;
@@ -5932,9 +6202,16 @@ var CodexDriver = class {
5932
6202
  this.pendingThreadRequestId = null;
5933
6203
  this.pendingThreadRequestMethod = null;
5934
6204
  this.pendingResumeFallbackParams = null;
6205
+ this.pendingResumeThreadId = null;
5935
6206
  this.pendingInitialTurnRequestId = null;
6207
+ this.pendingDeliveryRequests.clear();
5936
6208
  this.initialTurnStarted = false;
5937
6209
  this.normalizer.reset();
6210
+ this.spawnWorkingDirectory = ctx.workingDirectory;
6211
+ this.codexHomeRoot = resolveCodexHomeRootFromEnv(spawnEnv, {
6212
+ defaultHomeDir: os4.homedir(),
6213
+ cwd: ctx.workingDirectory
6214
+ });
5938
6215
  const args = ["app-server", "--listen", "stdio://"];
5939
6216
  const { command, args: spawnArgs, shell } = resolveCodexSpawn(args);
5940
6217
  const proc = spawn2(command, spawnArgs, {
@@ -5969,6 +6246,24 @@ var CodexDriver = class {
5969
6246
  const isResponse = isJsonRpcResponse(message);
5970
6247
  if (isResponse && hasJsonRpcField(message, "result")) {
5971
6248
  if (message.id === this.initializeRequestId) {
6249
+ if (!isCompatibleInitializeResult(message.result)) {
6250
+ this.initializeRequestId = null;
6251
+ this.pendingThreadRequest = null;
6252
+ this.pendingThreadRequestId = null;
6253
+ this.pendingThreadRequestMethod = null;
6254
+ this.pendingResumeFallbackParams = null;
6255
+ this.pendingResumeThreadId = null;
6256
+ events.push({
6257
+ kind: "error",
6258
+ message: unsupportedInitializeResultMessage(),
6259
+ startupRequestMethod: "initialize"
6260
+ });
6261
+ return events;
6262
+ }
6263
+ const codexHome = extractCodexHome(message.result);
6264
+ if (codexHome) {
6265
+ this.codexHomeRoot = path7.resolve(this.spawnWorkingDirectory ?? process.cwd(), codexHome);
6266
+ }
5972
6267
  this.initializeRequestId = null;
5973
6268
  this.sendNotification("initialized", {});
5974
6269
  if (this.pendingThreadRequest) {
@@ -5984,6 +6279,7 @@ var CodexDriver = class {
5984
6279
  this.pendingThreadRequestId = null;
5985
6280
  this.pendingThreadRequestMethod = null;
5986
6281
  this.pendingResumeFallbackParams = null;
6282
+ this.pendingResumeThreadId = null;
5987
6283
  events.push({
5988
6284
  kind: "error",
5989
6285
  message: message.error?.message || "Codex app-server request failed",
@@ -5995,9 +6291,16 @@ var CodexDriver = class {
5995
6291
  if (hasJsonRpcField(message, "error")) {
5996
6292
  const errorMessage = message.error?.message || "Codex app-server request failed";
5997
6293
  const requestMethod = this.pendingThreadRequestMethod;
5998
- const resumeErrorClassification = requestMethod === "thread/resume" ? classifyCodexResumeError(errorMessage) : null;
6294
+ const resumeErrorClassification = requestMethod === "thread/resume" ? classifyCodexResumeError(errorMessage, this.pendingResumeThreadId || void 0) : null;
5999
6295
  if (this.pendingResumeFallbackParams && resumeErrorClassification?.kind === "missing_rollout") {
6000
6296
  events.push(resumeErrorClassification.telemetry);
6297
+ events.push(resumeErrorClassification.recovery);
6298
+ if (this.pendingInitialPrompt) {
6299
+ this.pendingInitialPrompt = prependCodexRecoveryNotice(
6300
+ this.pendingInitialPrompt,
6301
+ resumeErrorClassification.recovery
6302
+ );
6303
+ }
6001
6304
  this.sendThreadRequest("thread/start", this.pendingResumeFallbackParams);
6002
6305
  this.pendingResumeFallbackParams = null;
6003
6306
  return events;
@@ -6005,12 +6308,14 @@ var CodexDriver = class {
6005
6308
  this.pendingThreadRequestId = null;
6006
6309
  this.pendingThreadRequestMethod = null;
6007
6310
  this.pendingResumeFallbackParams = null;
6311
+ this.pendingResumeThreadId = null;
6008
6312
  events.push(requestMethod ? { kind: "error", message: errorMessage, startupRequestMethod: requestMethod } : { kind: "error", message: errorMessage });
6009
6313
  return events;
6010
6314
  }
6011
6315
  this.pendingThreadRequestId = null;
6012
6316
  this.pendingThreadRequestMethod = null;
6013
6317
  this.pendingResumeFallbackParams = null;
6318
+ this.pendingResumeThreadId = null;
6014
6319
  }
6015
6320
  if (isResponse && message.id === this.pendingInitialTurnRequestId) {
6016
6321
  this.pendingInitialTurnRequestId = null;
@@ -6025,6 +6330,21 @@ var CodexDriver = class {
6025
6330
  }
6026
6331
  return events;
6027
6332
  }
6333
+ if (isResponse && message.id !== void 0 && this.pendingDeliveryRequests.has(message.id)) {
6334
+ const requestMethod = this.pendingDeliveryRequests.get(message.id);
6335
+ this.pendingDeliveryRequests.delete(message.id);
6336
+ if (hasJsonRpcField(message, "error")) {
6337
+ const params = message.error ?? {};
6338
+ events.push({
6339
+ kind: "delivery_error",
6340
+ message: message.error?.message || "Codex app-server request failed",
6341
+ requestMethod,
6342
+ source: "codex_app_server_response",
6343
+ payloadBytes: payloadBytes2(params)
6344
+ });
6345
+ }
6346
+ return events;
6347
+ }
6028
6348
  const result = this.normalizer.normalizeMessage(message);
6029
6349
  if (result.turnStarted) {
6030
6350
  this.pendingInitialTurnRequestId = null;
@@ -6043,9 +6363,11 @@ var CodexDriver = class {
6043
6363
  const mode = opts?.mode || "busy";
6044
6364
  if (mode === "busy") {
6045
6365
  if (!this.normalizer.canSteerBusy) return null;
6366
+ const id2 = this.nextRequestId();
6367
+ this.pendingDeliveryRequests.set(id2, "turn/steer");
6046
6368
  return JSON.stringify({
6047
6369
  jsonrpc: "2.0",
6048
- id: this.nextRequestId(),
6370
+ id: id2,
6049
6371
  method: "turn/steer",
6050
6372
  params: {
6051
6373
  threadId: this.normalizer.threadId,
@@ -6054,9 +6376,11 @@ var CodexDriver = class {
6054
6376
  }
6055
6377
  });
6056
6378
  }
6379
+ const id = this.nextRequestId();
6380
+ this.pendingDeliveryRequests.set(id, "turn/start");
6057
6381
  return JSON.stringify({
6058
6382
  jsonrpc: "2.0",
6059
- id: this.nextRequestId(),
6383
+ id,
6060
6384
  method: "turn/start",
6061
6385
  params: {
6062
6386
  threadId: this.normalizer.threadId,
@@ -6112,9 +6436,11 @@ var CodexDriver = class {
6112
6436
  this.pendingThreadRequestId = id;
6113
6437
  this.pendingThreadRequestMethod = method;
6114
6438
  this.pendingResumeFallbackParams = null;
6439
+ this.pendingResumeThreadId = null;
6115
6440
  if (method === "thread/resume") {
6116
6441
  const { threadId: _threadId, ...freshParams } = params;
6117
6442
  this.pendingResumeFallbackParams = freshParams;
6443
+ this.pendingResumeThreadId = typeof _threadId === "string" && _threadId.trim() ? _threadId.trim() : null;
6118
6444
  }
6119
6445
  return id;
6120
6446
  }
@@ -6126,12 +6452,194 @@ var CodexDriver = class {
6126
6452
  }) + "\n");
6127
6453
  }
6128
6454
  async detectModels() {
6129
- return detectCodexModels();
6455
+ return await detectCodexModelsFromAppServer() ?? detectCodexModels(resolveCodexHomeRootFromEnv());
6130
6456
  }
6131
6457
  };
6132
- function detectCodexModels(home = os3.homedir()) {
6133
- const cachePath = path6.join(home, ".codex", "models_cache.json");
6134
- const configPath = path6.join(home, ".codex", "config.toml");
6458
+ function asNonEmptyString(value) {
6459
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
6460
+ }
6461
+ function modelListPage(result) {
6462
+ if (!result || typeof result !== "object") return null;
6463
+ const object = result;
6464
+ const entries = Array.isArray(object.data) ? object.data : Array.isArray(object.models) ? object.models : null;
6465
+ if (!entries) return null;
6466
+ return {
6467
+ entries,
6468
+ nextCursor: asNonEmptyString(object.nextCursor)
6469
+ };
6470
+ }
6471
+ function modelListRequestParams(cursor) {
6472
+ return cursor ? { cursor } : {};
6473
+ }
6474
+ function stringArray(values, read) {
6475
+ const result = [];
6476
+ const seen = /* @__PURE__ */ new Set();
6477
+ for (const value of values) {
6478
+ const item = read(value);
6479
+ if (!item || seen.has(item)) continue;
6480
+ seen.add(item);
6481
+ result.push(item);
6482
+ }
6483
+ return result;
6484
+ }
6485
+ function codexReasoningEfforts(value) {
6486
+ if (!Array.isArray(value)) return [];
6487
+ return stringArray(value, (entry) => {
6488
+ if (typeof entry === "string") return asNonEmptyString(entry);
6489
+ if (!entry || typeof entry !== "object") return null;
6490
+ const object = entry;
6491
+ return asNonEmptyString(object.reasoningEffort) ?? asNonEmptyString(object.id);
6492
+ });
6493
+ }
6494
+ function codexServiceTierIds(value) {
6495
+ if (!Array.isArray(value)) return [];
6496
+ return stringArray(value, (entry) => {
6497
+ if (typeof entry === "string") return asNonEmptyString(entry);
6498
+ if (!entry || typeof entry !== "object") return null;
6499
+ const object = entry;
6500
+ return asNonEmptyString(object.id) ?? asNonEmptyString(object.serviceTier);
6501
+ });
6502
+ }
6503
+ function codexModelInfoFromAppServer(entry) {
6504
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
6505
+ const object = entry;
6506
+ if (object.hidden === true) return null;
6507
+ const id = asNonEmptyString(object.id) ?? asNonEmptyString(object.model) ?? asNonEmptyString(object.slug);
6508
+ if (!id) return null;
6509
+ const label = asNonEmptyString(object.displayName) ?? asNonEmptyString(object.display_name) ?? asNonEmptyString(object.label) ?? id;
6510
+ const supportedReasoningEfforts = codexReasoningEfforts(object.supportedReasoningEfforts);
6511
+ const serviceTiers = codexServiceTierIds(object.serviceTiers);
6512
+ const additionalSpeedTiers = codexServiceTierIds(object.additionalSpeedTiers);
6513
+ const runtimeServiceTiers = serviceTiers.length > 0 ? serviceTiers : additionalSpeedTiers;
6514
+ const defaultReasoningEffort = asNonEmptyString(object.defaultReasoningEffort);
6515
+ const defaultServiceTier = asNonEmptyString(object.defaultServiceTier);
6516
+ return {
6517
+ id,
6518
+ label,
6519
+ verified: "launchable",
6520
+ ...supportedReasoningEfforts.length > 0 ? { supportedReasoningEfforts } : {},
6521
+ ...defaultReasoningEffort ? { defaultReasoningEffort } : {},
6522
+ ...runtimeServiceTiers.length > 0 ? { serviceTiers: runtimeServiceTiers } : {},
6523
+ ...defaultServiceTier ? { defaultServiceTier } : {}
6524
+ };
6525
+ }
6526
+ function codexModelSetFromAppServerEntries(entries) {
6527
+ const models = [];
6528
+ let defaultModel;
6529
+ for (const entry of entries) {
6530
+ const model = codexModelInfoFromAppServer(entry);
6531
+ if (!model) continue;
6532
+ models.push(model);
6533
+ if (!defaultModel && typeof entry === "object" && entry && entry.isDefault === true) {
6534
+ defaultModel = model.id;
6535
+ }
6536
+ }
6537
+ return models.length > 0 ? { models, default: defaultModel } : null;
6538
+ }
6539
+ async function detectCodexModelsFromAppServer(options = {}) {
6540
+ const env = options.env ?? process.env;
6541
+ let launch;
6542
+ try {
6543
+ launch = resolveCodexSpawn(["app-server", "--listen", "stdio://"], { env });
6544
+ } catch {
6545
+ return null;
6546
+ }
6547
+ return await new Promise((resolve) => {
6548
+ const timeoutMs = options.timeoutMs ?? 5e3;
6549
+ const proc = spawn2(launch.command, launch.args, {
6550
+ cwd: options.cwd ?? process.cwd(),
6551
+ stdio: ["pipe", "pipe", "ignore"],
6552
+ env,
6553
+ shell: launch.shell
6554
+ });
6555
+ let settled = false;
6556
+ let buffer = "";
6557
+ let requestId = 0;
6558
+ let initializeRequestId = null;
6559
+ let modelListRequestId = null;
6560
+ let pageCount = 0;
6561
+ const entries = [];
6562
+ const finish = (result) => {
6563
+ if (settled) return;
6564
+ settled = true;
6565
+ clearTimeout(timer);
6566
+ proc.kill();
6567
+ resolve(result);
6568
+ };
6569
+ const timer = setTimeout(() => finish(null), timeoutMs);
6570
+ const sendRequest = (method, params) => {
6571
+ requestId += 1;
6572
+ const id = requestId;
6573
+ proc.stdin?.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n");
6574
+ return id;
6575
+ };
6576
+ const sendNotification = (method, params) => {
6577
+ proc.stdin?.write(JSON.stringify({ jsonrpc: "2.0", method, params }) + "\n");
6578
+ };
6579
+ const requestModelPage = (cursor) => {
6580
+ pageCount += 1;
6581
+ modelListRequestId = sendRequest("model/list", modelListRequestParams(cursor));
6582
+ };
6583
+ proc.once("error", () => finish(null));
6584
+ proc.once("exit", () => finish(null));
6585
+ proc.stdout?.on("data", (chunk) => {
6586
+ if (settled) return;
6587
+ buffer += chunk.toString();
6588
+ for (; ; ) {
6589
+ const newline = buffer.indexOf("\n");
6590
+ if (newline === -1) break;
6591
+ const line = buffer.slice(0, newline).trim();
6592
+ buffer = buffer.slice(newline + 1);
6593
+ if (!line) continue;
6594
+ const message = parseCodexJsonRpcLine(line);
6595
+ if (!message || !isJsonRpcResponse(message)) continue;
6596
+ if (message.id === initializeRequestId) {
6597
+ if (!hasJsonRpcField(message, "result") || !isCompatibleInitializeResult(message.result)) {
6598
+ finish(null);
6599
+ return;
6600
+ }
6601
+ sendNotification("initialized", {});
6602
+ requestModelPage(null);
6603
+ continue;
6604
+ }
6605
+ if (message.id === modelListRequestId) {
6606
+ if (!hasJsonRpcField(message, "result")) {
6607
+ finish(null);
6608
+ return;
6609
+ }
6610
+ const page = modelListPage(message.result);
6611
+ if (!page) {
6612
+ finish(null);
6613
+ return;
6614
+ }
6615
+ entries.push(...page.entries);
6616
+ if (page.nextCursor && pageCount < 5) {
6617
+ requestModelPage(page.nextCursor);
6618
+ continue;
6619
+ }
6620
+ finish(codexModelSetFromAppServerEntries(entries));
6621
+ return;
6622
+ }
6623
+ }
6624
+ });
6625
+ initializeRequestId = sendRequest("initialize", {
6626
+ clientInfo: { name: "slock-daemon", version: "1.0.0" },
6627
+ capabilities: { experimentalApi: true }
6628
+ });
6629
+ });
6630
+ }
6631
+ function detectCodexModels(home = resolveCodexHomeRootFromEnv()) {
6632
+ let cachePath = null;
6633
+ let configPath = null;
6634
+ for (const root of codexStateRootCandidates(home)) {
6635
+ const candidate = path7.join(root, "models_cache.json");
6636
+ if (existsSync5(candidate)) {
6637
+ cachePath = candidate;
6638
+ configPath = path7.join(root, "config.toml");
6639
+ break;
6640
+ }
6641
+ }
6642
+ if (!cachePath || !configPath) return null;
6135
6643
  let models = [];
6136
6644
  try {
6137
6645
  const raw = readFileSync2(cachePath, "utf8");
@@ -6584,11 +7092,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
6584
7092
  return parseCursorModelsOutput(String(result.stdout || ""));
6585
7093
  }
6586
7094
  function buildCursorModelProbeEnv(deps = {}) {
6587
- return withWindowsUserEnvironment({
7095
+ return scrubDaemonChildEnv(withWindowsUserEnvironment({
6588
7096
  ...deps.env ?? process.env,
6589
7097
  FORCE_COLOR: "0",
6590
7098
  NO_COLOR: "1"
6591
- }, deps);
7099
+ }, deps));
6592
7100
  }
6593
7101
  function runCursorModelsCommand() {
6594
7102
  return spawnSync("cursor-agent", ["models"], {
@@ -6601,7 +7109,7 @@ function runCursorModelsCommand() {
6601
7109
  // src/drivers/gemini.ts
6602
7110
  import { execFileSync as execFileSync3, spawn as spawn6 } from "child_process";
6603
7111
  import { existsSync as existsSync6 } from "fs";
6604
- import path7 from "path";
7112
+ import path8 from "path";
6605
7113
  async function buildGeminiSpawnEnv(ctx, platform = process.platform) {
6606
7114
  const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
6607
7115
  const launchEnvVars = runtimeConfigToLaunchFields(ctx.config).envVars;
@@ -6644,8 +7152,8 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
6644
7152
  }
6645
7153
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
6646
7154
  const existsSyncFn = deps.existsSyncFn ?? existsSync6;
6647
- const env = deps.env ?? process.env;
6648
- const winPath = path7.win32;
7155
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
7156
+ const winPath = path8.win32;
6649
7157
  let geminiEntry = null;
6650
7158
  try {
6651
7159
  const globalRoot = normalizeExecOutput2(execFileSyncFn("npm", ["root", "-g"], {
@@ -6781,12 +7289,15 @@ var GeminiDriver = class {
6781
7289
  // src/drivers/kimi.ts
6782
7290
  import { randomUUID as randomUUID2 } from "crypto";
6783
7291
  import { spawn as spawn7 } from "child_process";
6784
- import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
6785
- import os4 from "os";
6786
- import path8 from "path";
7292
+ import { chmodSync, existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
7293
+ import os5 from "os";
7294
+ import path9 from "path";
6787
7295
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
6788
7296
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
6789
7297
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
7298
+ var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
7299
+ var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
7300
+ var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
6790
7301
  function parseToolArguments(raw) {
6791
7302
  if (typeof raw !== "string") return raw;
6792
7303
  try {
@@ -6795,6 +7306,73 @@ function parseToolArguments(raw) {
6795
7306
  return raw;
6796
7307
  }
6797
7308
  }
7309
+ function readKimiConfigSource(home = os5.homedir(), env = process.env) {
7310
+ const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
7311
+ if (inlineConfig && inlineConfig.trim()) {
7312
+ return {
7313
+ raw: inlineConfig,
7314
+ explicitPath: null,
7315
+ sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
7316
+ };
7317
+ }
7318
+ const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
7319
+ const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
7320
+ try {
7321
+ return {
7322
+ raw: readFileSync3(configPath, "utf8"),
7323
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
7324
+ sourcePath: configPath
7325
+ };
7326
+ } catch {
7327
+ return {
7328
+ raw: null,
7329
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
7330
+ sourcePath: configPath
7331
+ };
7332
+ }
7333
+ }
7334
+ function buildKimiSpawnEnv(env = process.env) {
7335
+ const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
7336
+ delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
7337
+ delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
7338
+ return scrubDaemonChildEnv(spawnEnv);
7339
+ }
7340
+ function buildKimiEffectiveEnv(ctx, overrideEnv) {
7341
+ return {
7342
+ ...process.env,
7343
+ ...ctx.config.envVars || {},
7344
+ ...overrideEnv || {}
7345
+ };
7346
+ }
7347
+ function buildKimiLaunchOptions(ctx, opts = {}) {
7348
+ const env = buildKimiEffectiveEnv(ctx, opts.env);
7349
+ const source = readKimiConfigSource(opts.home ?? os5.homedir(), env);
7350
+ const args = [];
7351
+ let configFilePath = null;
7352
+ let configContent = null;
7353
+ if (source.explicitPath) {
7354
+ configFilePath = source.explicitPath;
7355
+ } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
7356
+ configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
7357
+ configContent = source.raw;
7358
+ if (opts.writeGeneratedConfig !== false) {
7359
+ writeFileSync3(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
7360
+ chmodSync(configFilePath, 384);
7361
+ }
7362
+ }
7363
+ if (configFilePath) {
7364
+ args.push("--config-file", configFilePath);
7365
+ }
7366
+ if (ctx.config.model && ctx.config.model !== "default") {
7367
+ args.push("--model", ctx.config.model);
7368
+ }
7369
+ return {
7370
+ args,
7371
+ env: buildKimiSpawnEnv(env),
7372
+ configFilePath,
7373
+ configContent
7374
+ };
7375
+ }
6798
7376
  function resolveKimiSpawn(commandArgs, deps = {}) {
6799
7377
  return {
6800
7378
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -6818,7 +7396,25 @@ var KimiDriver = class {
6818
7396
  };
6819
7397
  model = {
6820
7398
  detectedModelsVerifiedAs: "launchable",
6821
- toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
7399
+ toLaunchSpec: (modelId, ctx, opts) => {
7400
+ if (!ctx) return { args: ["--model", modelId] };
7401
+ const launchCtx = {
7402
+ ...ctx,
7403
+ config: {
7404
+ ...ctx.config,
7405
+ model: modelId
7406
+ }
7407
+ };
7408
+ const launch = buildKimiLaunchOptions(launchCtx, {
7409
+ home: opts?.home,
7410
+ writeGeneratedConfig: false
7411
+ });
7412
+ return {
7413
+ args: launch.args,
7414
+ env: launch.env,
7415
+ configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
7416
+ };
7417
+ }
6822
7418
  };
6823
7419
  supportsStdinNotification = true;
6824
7420
  busyDeliveryMode = "direct";
@@ -6830,8 +7426,8 @@ var KimiDriver = class {
6830
7426
  this.sessionId = ctx.config.sessionId || randomUUID2();
6831
7427
  this.sessionAnnounced = false;
6832
7428
  this.promptRequestId = randomUUID2();
6833
- const systemPromptPath = path8.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
6834
- const agentFilePath = path8.join(ctx.workingDirectory, KIMI_AGENT_FILE);
7429
+ const systemPromptPath = path9.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
7430
+ const agentFilePath = path9.join(ctx.workingDirectory, KIMI_AGENT_FILE);
6835
7431
  if (!isResume || !existsSync7(systemPromptPath)) {
6836
7432
  writeFileSync3(systemPromptPath, ctx.prompt, "utf8");
6837
7433
  }
@@ -6842,21 +7438,23 @@ var KimiDriver = class {
6842
7438
  ` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
6843
7439
  ""
6844
7440
  ].join("\n"), "utf8");
7441
+ const launch = buildKimiLaunchOptions(ctx);
6845
7442
  const args = [
6846
7443
  "--wire",
6847
7444
  "--yolo",
6848
7445
  "--agent-file",
6849
7446
  agentFilePath,
6850
7447
  "--session",
6851
- this.sessionId
7448
+ this.sessionId,
7449
+ ...launch.args
6852
7450
  ];
6853
7451
  const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
6854
7452
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
6855
7453
  args.push("--model", launchRuntimeFields.model);
6856
7454
  }
6857
7455
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
6858
- const launch = resolveKimiSpawn(args);
6859
- const proc = spawn7(launch.command, launch.args, {
7456
+ const spawnTarget = resolveKimiSpawn(args);
7457
+ const proc = spawn7(spawnTarget.command, spawnTarget.args, {
6860
7458
  cwd: ctx.workingDirectory,
6861
7459
  stdio: ["pipe", "pipe", "pipe"],
6862
7460
  env: spawnEnv,
@@ -6864,7 +7462,7 @@ var KimiDriver = class {
6864
7462
  // and has an 8191-character command-line limit. Kimi's official
6865
7463
  // installer/uv entrypoint is an executable, so launch it directly and
6866
7464
  // keep prompts on stdin / files instead of routing through cmd.exe.
6867
- shell: launch.shell
7465
+ shell: spawnTarget.shell
6868
7466
  });
6869
7467
  proc.stdin?.write(JSON.stringify({
6870
7468
  jsonrpc: "2.0",
@@ -6977,14 +7575,9 @@ var KimiDriver = class {
6977
7575
  return detectKimiModels();
6978
7576
  }
6979
7577
  };
6980
- function detectKimiModels(home = os4.homedir()) {
6981
- const configPath = path8.join(home, ".kimi", "config.toml");
6982
- let raw;
6983
- try {
6984
- raw = readFileSync3(configPath, "utf8");
6985
- } catch {
6986
- return null;
6987
- }
7578
+ function detectKimiModels(home = os5.homedir(), opts = {}) {
7579
+ const raw = readKimiConfigSource(home, opts.env).raw;
7580
+ if (raw === null) return null;
6988
7581
  const models = [];
6989
7582
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
6990
7583
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -7007,7 +7600,7 @@ function detectKimiModels(home = os4.homedir()) {
7007
7600
  import { randomUUID as randomUUID3 } from "crypto";
7008
7601
  import { EventEmitter } from "events";
7009
7602
  import { mkdirSync as mkdirSync3, readFileSync as readFileSync4 } from "fs";
7010
- import path9 from "path";
7603
+ import path10 from "path";
7011
7604
  import { createRequire as createRequire2 } from "module";
7012
7605
  import {
7013
7606
  createKimiHarness,
@@ -7032,7 +7625,7 @@ function createKimiSdkEventMappingState(sessionId = null) {
7032
7625
  };
7033
7626
  }
7034
7627
  function buildKimiSessionDir(workingDirectory) {
7035
- return path9.join(workingDirectory, KIMI_SESSION_DIR);
7628
+ return path10.join(workingDirectory, KIMI_SESSION_DIR);
7036
7629
  }
7037
7630
  function kimiErrorMessage(error) {
7038
7631
  if (typeof error === "string" && error.trim()) return error.trim();
@@ -7159,7 +7752,7 @@ async function createKimiAgentSessionForContext(ctx, sessionId) {
7159
7752
  const cliTransport = await prepareCliTransport(ctx, { NO_COLOR: "1" });
7160
7753
  const spawnEnv = cliTransport.spawnEnv;
7161
7754
  const wrapperPath = cliTransport.wrapperPath;
7162
- const homeDir = spawnEnv.KIMI_HOME || (process.env.HOME ? path9.join(process.env.HOME, ".kimi") : path9.join(ctx.workingDirectory, ".kimi"));
7755
+ const homeDir = spawnEnv.KIMI_HOME || (process.env.HOME ? path10.join(process.env.HOME, ".kimi") : path10.join(ctx.workingDirectory, ".kimi"));
7163
7756
  mkdirSync3(homeDir, { recursive: true });
7164
7757
  const harness = createKimiHarness({
7165
7758
  homeDir,
@@ -7384,7 +7977,7 @@ So instead of \`raft message send ...\`, run \`${this.wrapperPath} message send
7384
7977
  };
7385
7978
  function detectKimiSdkModels(home = resolveKimiHome(), ctx = {}) {
7386
7979
  const span = ctx.span;
7387
- const configPath = path9.join(home, "config.toml");
7980
+ const configPath = path10.join(home, "config.toml");
7388
7981
  const homeFromEnv = Boolean(process.env.KIMI_CODE_HOME);
7389
7982
  const emit2 = (outcome, extra = {}) => {
7390
7983
  span?.addEvent("daemon.kimi_sdk.models.config", {
@@ -7492,8 +8085,8 @@ var KimiSdkDriver = class {
7492
8085
  // src/drivers/opencode.ts
7493
8086
  import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
7494
8087
  import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
7495
- import os5 from "os";
7496
- import path10 from "path";
8088
+ import os6 from "os";
8089
+ import path11 from "path";
7497
8090
  var SLOCK_AGENT_NAME = "slock";
7498
8091
  var NO_MESSAGE_PROMPT = "No new messages are pending. Stop now.";
7499
8092
  var FIRST_MESSAGE_TASK_PREFIX = "First message task (system-triggered):";
@@ -7521,8 +8114,8 @@ function parseUserOpenCodeConfig(ctx) {
7521
8114
  const raw = runtimeConfigToLaunchFields(ctx.config).envVars?.OPENCODE_CONFIG_CONTENT;
7522
8115
  return parseOpenCodeConfigContent(raw);
7523
8116
  }
7524
- function readLocalOpenCodeConfig(home = os5.homedir()) {
7525
- const configPath = path10.join(home, ".config", "opencode", "opencode.json");
8117
+ function readLocalOpenCodeConfig(home = os6.homedir()) {
8118
+ const configPath = path11.join(home, ".config", "opencode", "opencode.json");
7526
8119
  try {
7527
8120
  return parseOpenCodeConfigContent(readFileSync5(configPath, "utf8"));
7528
8121
  } catch {
@@ -7582,7 +8175,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
7582
8175
  }
7583
8176
  };
7584
8177
  }
7585
- function buildOpenCodeConfig(ctx, home = os5.homedir()) {
8178
+ function buildOpenCodeConfig(ctx, home = os6.homedir()) {
7586
8179
  const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
7587
8180
  const userAgents = recordField(userConfig.agent);
7588
8181
  const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
@@ -7600,7 +8193,7 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
7600
8193
  mcp: recordField(userConfig.mcp)
7601
8194
  };
7602
8195
  }
7603
- async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = null) {
8196
+ async function buildOpenCodeLaunchOptions(ctx, home = os6.homedir(), version = null) {
7604
8197
  const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
7605
8198
  const config = buildOpenCodeConfig(ctx, home);
7606
8199
  const env = {
@@ -7699,7 +8292,7 @@ function formatOpenCodeLabelToken(token) {
7699
8292
  if (/^\d/.test(token)) return token;
7700
8293
  return normalized.charAt(0).toUpperCase() + normalized.slice(1);
7701
8294
  }
7702
- function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeModelsCommand) {
8295
+ function detectOpenCodeModels(home = os6.homedir(), runCommand = runOpenCodeModelsCommand) {
7703
8296
  const commandResult = runCommand(home);
7704
8297
  if (commandResult.error || commandResult.status !== 0) return null;
7705
8298
  return parseOpenCodeModelsOutput(commandResult.stdout);
@@ -7708,7 +8301,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
7708
8301
  const platform = deps.platform ?? process.platform;
7709
8302
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
7710
8303
  const result = spawnSyncFn("opencode", ["models"], {
7711
- env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
8304
+ env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
7712
8305
  encoding: "utf8",
7713
8306
  timeout: 5e3,
7714
8307
  shell: platform === "win32"
@@ -7720,11 +8313,11 @@ function runOpenCodeModelsCommand(home, deps = {}) {
7720
8313
  };
7721
8314
  }
7722
8315
  function isWindowsCommandShim(commandPath) {
7723
- const ext = path10.win32.extname(commandPath).toLowerCase();
8316
+ const ext = path11.win32.extname(commandPath).toLowerCase();
7724
8317
  return ext === ".cmd" || ext === ".bat";
7725
8318
  }
7726
8319
  function opencodePackageEntryCandidates(packageRoot) {
7727
- const winPath = path10.win32;
8320
+ const winPath = path11.win32;
7728
8321
  return [
7729
8322
  winPath.join(packageRoot, "bin", "opencode.exe"),
7730
8323
  winPath.join(packageRoot, "bin", "opencode.js"),
@@ -7733,7 +8326,7 @@ function opencodePackageEntryCandidates(packageRoot) {
7733
8326
  ];
7734
8327
  }
7735
8328
  function openCodeSpecForEntry(entry, commandArgs) {
7736
- if (path10.win32.extname(entry).toLowerCase() === ".exe") {
8329
+ if (path11.win32.extname(entry).toLowerCase() === ".exe") {
7737
8330
  return { command: entry, args: commandArgs, shell: false };
7738
8331
  }
7739
8332
  return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
@@ -7742,7 +8335,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
7742
8335
  const existsSyncFn = deps.existsSyncFn ?? existsSync8;
7743
8336
  const execFileSyncFn = deps.execFileSyncFn;
7744
8337
  const env = deps.env ?? process.env;
7745
- const winPath = path10.win32;
8338
+ const winPath = path11.win32;
7746
8339
  const candidates = [];
7747
8340
  if (execFileSyncFn) {
7748
8341
  try {
@@ -7770,7 +8363,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
7770
8363
  function extractWindowsShimTargets(commandPath, deps = {}) {
7771
8364
  if (!isWindowsCommandShim(commandPath)) return [];
7772
8365
  const readFileSyncFn = deps.readFileSyncFn ?? readFileSync5;
7773
- const commandDir = path10.win32.dirname(commandPath);
8366
+ const commandDir = path11.win32.dirname(commandPath);
7774
8367
  let raw;
7775
8368
  try {
7776
8369
  raw = String(readFileSyncFn(commandPath, "utf8"));
@@ -7781,7 +8374,7 @@ function extractWindowsShimTargets(commandPath, deps = {}) {
7781
8374
  const dp0Pattern = /%~dp0\\?([^"\r\n]*?opencode\.(?:exe|js|mjs|cjs))/gi;
7782
8375
  for (const match of raw.matchAll(dp0Pattern)) {
7783
8376
  const relative = match[1]?.replace(/^\\+/, "");
7784
- if (relative) candidates.push(path10.win32.normalize(path10.win32.join(commandDir, relative)));
8377
+ if (relative) candidates.push(path11.win32.normalize(path11.win32.join(commandDir, relative)));
7785
8378
  }
7786
8379
  return candidates;
7787
8380
  }
@@ -7795,7 +8388,7 @@ function resolveOpenCodeSpawn(commandArgs, deps = {}) {
7795
8388
  };
7796
8389
  }
7797
8390
  const command = resolveCommandOnPath("opencode", deps);
7798
- if (command && path10.win32.extname(command).toLowerCase() === ".exe") {
8391
+ if (command && path11.win32.extname(command).toLowerCase() === ".exe") {
7799
8392
  return { command, args: commandArgs, shell: false };
7800
8393
  }
7801
8394
  const packageEntry = resolveWindowsOpenCodePackageEntry(command, deps);
@@ -7900,7 +8493,7 @@ var OpenCodeDriver = class {
7900
8493
  if (unsupportedMessage) {
7901
8494
  throw new Error(unsupportedMessage);
7902
8495
  }
7903
- const launch = await buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
8496
+ const launch = await buildOpenCodeLaunchOptions(ctx, os6.homedir(), version);
7904
8497
  const spawnSpec = resolveOpenCodeSpawn(launch.args);
7905
8498
  const proc = spawn8(spawnSpec.command, spawnSpec.args, {
7906
8499
  cwd: ctx.workingDirectory,
@@ -7968,7 +8561,7 @@ var OpenCodeDriver = class {
7968
8561
  import { randomUUID as randomUUID4 } from "crypto";
7969
8562
  import { EventEmitter as EventEmitter2 } from "events";
7970
8563
  import { mkdirSync as mkdirSync4, readdirSync as readdirSync2 } from "fs";
7971
- import path11 from "path";
8564
+ import path12 from "path";
7972
8565
  import {
7973
8566
  AuthStorage,
7974
8567
  createBashTool,
@@ -7995,7 +8588,7 @@ function createPiSdkEventMappingState(sessionId = null) {
7995
8588
  };
7996
8589
  }
7997
8590
  function buildPiSessionDir(workingDirectory) {
7998
- return path11.join(workingDirectory, PI_SESSION_DIR);
8591
+ return path12.join(workingDirectory, PI_SESSION_DIR);
7999
8592
  }
8000
8593
  async function buildPiSpawnEnv(ctx) {
8001
8594
  return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
@@ -8017,7 +8610,7 @@ function findPiSessionFile(sessionDir, sessionId) {
8017
8610
  }
8018
8611
  const suffix = `_${sessionId}.jsonl`;
8019
8612
  const match = entries.find((entry) => entry.endsWith(suffix));
8020
- return match ? path11.join(sessionDir, match) : null;
8613
+ return match ? path12.join(sessionDir, match) : null;
8021
8614
  }
8022
8615
  function detectPiModelsFromRegistry(modelRegistry) {
8023
8616
  const models = [];
@@ -8253,7 +8846,7 @@ async function createPiAgentSessionForContext(ctx, sessionId) {
8253
8846
  try {
8254
8847
  const spawnEnv = await buildPiSpawnEnv(ctx);
8255
8848
  const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
8256
- const authStorage = AuthStorage.create(path11.join(agentDir, "auth.json"));
8849
+ const authStorage = AuthStorage.create(path12.join(agentDir, "auth.json"));
8257
8850
  const settingsManager = SettingsManager.create(ctx.workingDirectory, agentDir);
8258
8851
  const services = await createAgentSessionServices({
8259
8852
  cwd: ctx.workingDirectory,
@@ -8652,6 +9245,9 @@ var ChildProcessRuntimeSession = class {
8652
9245
  get currentSessionId() {
8653
9246
  return this.driver.currentSessionId;
8654
9247
  }
9248
+ get currentRuntimeHomeDir() {
9249
+ return this.driver.currentRuntimeHomeDir;
9250
+ }
8655
9251
  get exitCode() {
8656
9252
  return this.process?.exitCode ?? null;
8657
9253
  }
@@ -8775,7 +9371,7 @@ function getDriver(runtimeId) {
8775
9371
 
8776
9372
  // src/workspaces.ts
8777
9373
  import { readdir, rm, stat } from "fs/promises";
8778
- import path12 from "path";
9374
+ import path13 from "path";
8779
9375
  function isValidWorkspaceDirectoryName(directoryName) {
8780
9376
  return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
8781
9377
  }
@@ -8783,7 +9379,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
8783
9379
  if (!isValidWorkspaceDirectoryName(directoryName)) {
8784
9380
  return null;
8785
9381
  }
8786
- return path12.join(dataDir, directoryName);
9382
+ return path13.join(dataDir, directoryName);
8787
9383
  }
8788
9384
  function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
8789
9385
  return {
@@ -8832,7 +9428,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
8832
9428
  return summary;
8833
9429
  }
8834
9430
  const childSummaries = await Promise.all(
8835
- entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
9431
+ entries.map((entry) => summarizeWorkspaceEntry(path13.join(dirPath, entry.name), entry))
8836
9432
  );
8837
9433
  for (const childSummary of childSummaries) {
8838
9434
  summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
@@ -8851,7 +9447,7 @@ async function scanWorkspaceDirectories(dataDir) {
8851
9447
  if (!entry.isDirectory()) {
8852
9448
  return null;
8853
9449
  }
8854
- const dirPath = path12.join(dataDir, entry.name);
9450
+ const dirPath = path13.join(dataDir, entry.name);
8855
9451
  try {
8856
9452
  const summary = await summarizeWorkspaceDirectory(dirPath);
8857
9453
  return {
@@ -9400,17 +9996,17 @@ function allowedTranscriptRootsForRuntime(runtime, homeDir, workspaceDir) {
9400
9996
  const roots = [workspaceDir];
9401
9997
  switch (runtime) {
9402
9998
  case "claude":
9403
- roots.push(path13.join(homeDir, ".claude"));
9999
+ roots.push(path14.join(homeDir, ".claude"));
9404
10000
  break;
9405
10001
  case "codex":
9406
- roots.push(path13.join(homeDir, ".codex"));
10002
+ roots.push(homeDir, path14.join(homeDir, ".codex"));
9407
10003
  break;
9408
10004
  case "kimi":
9409
10005
  case "kimi-sdk":
9410
- roots.push(path13.join(homeDir, ".kimi"));
10006
+ roots.push(path14.join(homeDir, ".kimi"));
9411
10007
  break;
9412
10008
  case "pi":
9413
- roots.push(path13.join(homeDir, ".pi"), path13.join(homeDir, ".pi", "agent"));
10009
+ roots.push(path14.join(homeDir, ".pi"), path14.join(homeDir, ".pi", "agent"));
9414
10010
  break;
9415
10011
  }
9416
10012
  return roots;
@@ -9421,8 +10017,8 @@ async function isPathWithinAllowedRoots(filePath, roots) {
9421
10017
  for (const root of roots) {
9422
10018
  const realRoot = await realpath(root).catch(() => null);
9423
10019
  if (!realRoot) continue;
9424
- const rel = path13.relative(realRoot, real);
9425
- if (!rel.startsWith("..") && !path13.isAbsolute(rel)) return true;
10020
+ const rel = path14.relative(realRoot, real);
10021
+ if (!rel.startsWith("..") && !path14.isAbsolute(rel)) return true;
9426
10022
  }
9427
10023
  return false;
9428
10024
  }
@@ -9469,12 +10065,12 @@ function findSessionJsonl(root, predicate) {
9469
10065
  for (const entry of entries) {
9470
10066
  if (++visited > maxEntries) return null;
9471
10067
  if (!entry.isFile() || !predicate(entry.name)) continue;
9472
- return path13.join(dir, entry.name);
10068
+ return path14.join(dir, entry.name);
9473
10069
  }
9474
10070
  for (const entry of entries) {
9475
10071
  if (++visited > maxEntries) return null;
9476
10072
  if (!entry.isDirectory()) continue;
9477
- const found = visit(path13.join(dir, entry.name), depth - 1);
10073
+ const found = visit(path14.join(dir, entry.name), depth - 1);
9478
10074
  if (found) return found;
9479
10075
  }
9480
10076
  return null;
@@ -9482,7 +10078,7 @@ function findSessionJsonl(root, predicate) {
9482
10078
  return visit(root, maxDepth);
9483
10079
  }
9484
10080
  function findKimiSdkSessionDir(sessionId, agentId, homeDir) {
9485
- const indexPath = path13.join(homeDir, ".kimi", "session_index.jsonl");
10081
+ const indexPath = path14.join(homeDir, ".kimi", "session_index.jsonl");
9486
10082
  try {
9487
10083
  const index = readFileSync6(indexPath, "utf8");
9488
10084
  for (const line of index.split("\n")) {
@@ -9497,12 +10093,12 @@ function findKimiSdkSessionDir(sessionId, agentId, homeDir) {
9497
10093
  }
9498
10094
  } catch {
9499
10095
  }
9500
- const sessionsRoot = path13.join(homeDir, ".kimi", "sessions");
10096
+ const sessionsRoot = path14.join(homeDir, ".kimi", "sessions");
9501
10097
  try {
9502
10098
  const prefix = agentId ? `wd_${agentId}_` : `wd_`;
9503
10099
  for (const entry of readdirSync3(sessionsRoot, { withFileTypes: true })) {
9504
10100
  if (!entry.isDirectory() || !entry.name.startsWith(prefix)) continue;
9505
- const candidate = path13.join(sessionsRoot, entry.name, `session_${sessionId}`);
10101
+ const candidate = path14.join(sessionsRoot, entry.name, `session_${sessionId}`);
9506
10102
  if (existsSync9(candidate)) return candidate;
9507
10103
  }
9508
10104
  } catch {
@@ -9511,16 +10107,16 @@ function findKimiSdkSessionDir(sessionId, agentId, homeDir) {
9511
10107
  }
9512
10108
  function findPiSessionFile2(sessionId, workingDirectory, homeDir) {
9513
10109
  if (workingDirectory) {
9514
- const piSessionsDir = path13.join(workingDirectory, ".pi-sessions");
10110
+ const piSessionsDir = path14.join(workingDirectory, ".pi-sessions");
9515
10111
  try {
9516
- const files = readdirSync3(piSessionsDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => ({ name: e.name, path: path13.join(piSessionsDir, e.name), stat: statSync(path13.join(piSessionsDir, e.name)) })).filter((f) => f.stat.isFile() && f.name.includes(sessionId)).sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs);
10112
+ const files = readdirSync3(piSessionsDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => ({ name: e.name, path: path14.join(piSessionsDir, e.name), stat: statSync(path14.join(piSessionsDir, e.name)) })).filter((f) => f.stat.isFile() && f.name.includes(sessionId)).sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs);
9517
10113
  if (files[0]) return files[0].path;
9518
10114
  } catch {
9519
10115
  }
9520
10116
  }
9521
10117
  const legacyRoots = [
9522
- path13.join(homeDir, ".pi", "agent"),
9523
- path13.join(homeDir, ".pi")
10118
+ path14.join(homeDir, ".pi", "agent"),
10119
+ path14.join(homeDir, ".pi")
9524
10120
  ];
9525
10121
  for (const root of legacyRoots) {
9526
10122
  const found = findSessionJsonl(root, (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
@@ -9534,9 +10130,9 @@ function safeSessionFilename(value) {
9534
10130
  }
9535
10131
  function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
9536
10132
  try {
9537
- const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
10133
+ const dir = path14.join(fallbackDir, ".slock", "runtime-sessions");
9538
10134
  mkdirSync5(dir, { recursive: true });
9539
- const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
10135
+ const filePath = path14.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
9540
10136
  writeFileSync4(filePath, JSON.stringify({
9541
10137
  type: "runtime_session_handoff",
9542
10138
  runtime,
@@ -9555,27 +10151,40 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
9555
10151
  return null;
9556
10152
  }
9557
10153
  }
9558
- function resolveRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
10154
+ function resolveRuntimeHomeDir(config, defaultHomeDir, workspacePath, opts = {}) {
9559
10155
  if (isClaudeCustomProviderConfig(config)) {
9560
10156
  return getClaudeProviderStatePaths(workspacePath).home;
9561
10157
  }
10158
+ if (config.runtime === "codex") {
10159
+ return resolveCodexHomeRootFromConfig(config, defaultHomeDir, workspacePath, process.env, opts);
10160
+ }
9562
10161
  return defaultHomeDir;
9563
10162
  }
9564
- function ensureRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
10163
+ function ensureRuntimeHomeDir(config, defaultHomeDir, workspacePath, opts = {}) {
9565
10164
  if (isClaudeCustomProviderConfig(config)) {
9566
10165
  return ensureClaudeProviderStatePaths(workspacePath).home;
9567
10166
  }
10167
+ if (config.runtime === "codex") {
10168
+ const home = resolveCodexHomeRootFromConfig(config, defaultHomeDir, workspacePath, process.env, opts);
10169
+ if (opts.agentId) {
10170
+ mkdirSync5(home, { recursive: true });
10171
+ }
10172
+ return home;
10173
+ }
9568
10174
  return defaultHomeDir;
9569
10175
  }
9570
- function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir, opts) {
10176
+ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os7.homedir(), fallbackDir, opts) {
9571
10177
  let resolvedPath = null;
9572
10178
  let lookupMethod = "none";
9573
10179
  if (runtime === "claude") {
9574
10180
  lookupMethod = "claude_jsonl";
9575
- resolvedPath = findSessionJsonl(path13.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`);
10181
+ resolvedPath = findSessionJsonl(path14.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`);
9576
10182
  } else if (runtime === "codex") {
9577
10183
  lookupMethod = "codex_jsonl";
9578
- resolvedPath = findSessionJsonl(path13.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
10184
+ for (const root of codexSessionRootCandidates(homeDir)) {
10185
+ resolvedPath = findSessionJsonl(root, (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
10186
+ if (resolvedPath) break;
10187
+ }
9579
10188
  } else if (runtime === "kimi-sdk" || runtime === "kimi") {
9580
10189
  lookupMethod = "kimi_sdk_index";
9581
10190
  resolvedPath = findKimiSdkSessionDir(sessionId, opts?.agentId, homeDir);
@@ -10312,6 +10921,28 @@ function runtimeDiagnosticTraceAttrs(event) {
10312
10921
  session_id_present: Boolean(event.sessionId)
10313
10922
  };
10314
10923
  }
10924
+ function runtimeRecoveryTrajectoryEntry(event) {
10925
+ const lines = [event.message];
10926
+ if (event.details && event.details !== event.message) {
10927
+ lines.push(event.details);
10928
+ }
10929
+ return {
10930
+ kind: "system",
10931
+ title: "Codex resume recovery",
10932
+ text: lines.join("\n")
10933
+ };
10934
+ }
10935
+ function runtimeRecoveryTraceAttrs(event) {
10936
+ return {
10937
+ kind: event.kind,
10938
+ source: event.source,
10939
+ resume_error_class: event.resumeErrorClass,
10940
+ recovery_action: event.recoveryAction,
10941
+ requested_session_id_present: Boolean(event.requestedSessionId),
10942
+ message_present: Boolean(event.message),
10943
+ details_present: Boolean(event.details)
10944
+ };
10945
+ }
10315
10946
  function currentErrorCandidates(ap) {
10316
10947
  return [
10317
10948
  ap.runtimeErrorSinceProgress ? ap.lastRuntimeError : null,
@@ -10635,6 +11266,7 @@ var AgentProcessManager = class _AgentProcessManager {
10635
11266
  sendToServer;
10636
11267
  daemonApiKey;
10637
11268
  serverUrl;
11269
+ slockHome;
10638
11270
  dataDir;
10639
11271
  runtimeSessionHomeDir;
10640
11272
  driverResolver;
@@ -10664,8 +11296,9 @@ var AgentProcessManager = class _AgentProcessManager {
10664
11296
  this.sendToServer = sendToServer;
10665
11297
  this.daemonApiKey = daemonApiKey;
10666
11298
  this.serverUrl = opts.serverUrl;
10667
- this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
10668
- this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os6.homedir();
11299
+ this.slockHome = opts.slockHome ? path14.resolve(opts.slockHome) : resolveSlockHome();
11300
+ this.dataDir = opts.dataDir || resolveSlockHomePath("agents", this.slockHome);
11301
+ this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os7.homedir();
10669
11302
  this.driverResolver = opts.driverResolver || getDriver;
10670
11303
  this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
10671
11304
  this.tracer = opts.tracer ?? noopTracer;
@@ -10794,6 +11427,70 @@ var AgentProcessManager = class _AgentProcessManager {
10794
11427
  this.sendStdinNotification(agentId);
10795
11428
  }, delayMs);
10796
11429
  }
11430
+ flushAsyncRejectedIdleDelivery(agentId) {
11431
+ const ap = this.agents.get(agentId);
11432
+ if (!ap) return false;
11433
+ const count = ap.notifications.takePendingAndClearTimer();
11434
+ if (count === 0) return false;
11435
+ if (!ap.driver.supportsStdinNotification || !ap.sessionId || ap.inbox.length === 0) {
11436
+ ap.notifications.add(count);
11437
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
11438
+ agentId,
11439
+ runtime: ap.config.runtime,
11440
+ model: ap.config.model,
11441
+ launchId: ap.launchId || void 0,
11442
+ mode: "idle",
11443
+ outcome: "queued_without_stdin",
11444
+ inbox_count: ap.inbox.length,
11445
+ pending_notification_count: ap.notifications.pendingCount,
11446
+ session_id_present: Boolean(ap.sessionId),
11447
+ supports_stdin_notification: ap.driver.supportsStdinNotification
11448
+ });
11449
+ return false;
11450
+ }
11451
+ if (!this.isApmIdle(ap)) {
11452
+ ap.notifications.add(count);
11453
+ return this.sendStdinNotification(agentId, { forceUnsupportedRetry: true });
11454
+ }
11455
+ ap.notifications.pruneContributedToPending(ap.inbox, ap.sessionId);
11456
+ const messages = ap.notifications.filterUncontributedMessages(ap.inbox, ap.sessionId);
11457
+ if (messages.length === 0) {
11458
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
11459
+ agentId,
11460
+ runtime: ap.config.runtime,
11461
+ model: ap.config.model,
11462
+ launchId: ap.launchId || void 0,
11463
+ mode: "idle",
11464
+ outcome: "suppressed_already_contributed",
11465
+ inbox_count: ap.inbox.length,
11466
+ pending_notification_count: count,
11467
+ session_id_present: true
11468
+ });
11469
+ return false;
11470
+ }
11471
+ this.commitApmIdleState(agentId, ap, false);
11472
+ this.startRuntimeTrace(agentId, ap, "async-rejected-idle-delivery-retry", messages);
11473
+ this.broadcastActivity(agentId, "working", "Message received");
11474
+ const accepted = this.deliverInboxUpdateViaStdin(
11475
+ agentId,
11476
+ ap,
11477
+ messages,
11478
+ "idle",
11479
+ "async_rejected_idle_delivery_retry"
11480
+ );
11481
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
11482
+ agentId,
11483
+ runtime: ap.config.runtime,
11484
+ model: ap.config.model,
11485
+ launchId: ap.launchId || void 0,
11486
+ mode: "idle",
11487
+ outcome: accepted ? "written" : "not_written",
11488
+ inbox_count: ap.inbox.length,
11489
+ messages_count: messages.length,
11490
+ session_id_present: true
11491
+ });
11492
+ return accepted;
11493
+ }
10797
11494
  isApmIdle(ap) {
10798
11495
  return ap.gatedSteering.isIdle;
10799
11496
  }
@@ -11252,6 +11949,23 @@ var AgentProcessManager = class _AgentProcessManager {
11252
11949
  ...runtimeDiagnosticTraceAttrs(event)
11253
11950
  });
11254
11951
  }
11952
+ recordRuntimeRecoveryActivity(agentId, ap, event) {
11953
+ this.sendToServer({
11954
+ type: "agent:activity",
11955
+ agentId,
11956
+ activity: ap.lastActivity || "online",
11957
+ detail: ap.lastActivityDetail || "",
11958
+ entries: [runtimeRecoveryTrajectoryEntry(event)],
11959
+ launchId: ap.launchId || void 0,
11960
+ clientSeq: this.nextActivityClientSeq(agentId)
11961
+ });
11962
+ this.recordDaemonTrace("daemon.runtime.recovery.visible", {
11963
+ agentId,
11964
+ launchId: ap.launchId || void 0,
11965
+ runtime: ap.config.runtime,
11966
+ ...runtimeRecoveryTraceAttrs(event)
11967
+ });
11968
+ }
11255
11969
  recordDaemonTrace(name, attrs, status = "ok", parentTraceparent) {
11256
11970
  const span = this.tracer.startSpan(name, {
11257
11971
  parent: parseTraceparent(parentTraceparent),
@@ -11589,26 +12303,26 @@ var AgentProcessManager = class _AgentProcessManager {
11589
12303
  let pendingStartRebind;
11590
12304
  const originalLaunchId = launchId || null;
11591
12305
  try {
11592
- const agentDataDir = path13.join(this.dataDir, agentId);
12306
+ const agentDataDir = path14.join(this.dataDir, agentId);
11593
12307
  await mkdir(agentDataDir, { recursive: true });
11594
12308
  const initialRuntimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
11595
- const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
12309
+ const memoryMdPath = path14.join(agentDataDir, "MEMORY.md");
11596
12310
  try {
11597
12311
  await access(memoryMdPath);
11598
12312
  } catch {
11599
12313
  const initialMemoryMd = buildInitialMemoryMd(initialRuntimeConfig);
11600
12314
  await writeFile(memoryMdPath, initialMemoryMd);
11601
12315
  }
11602
- const notesDir = path13.join(agentDataDir, "notes");
12316
+ const notesDir = path14.join(agentDataDir, "notes");
11603
12317
  await mkdir(notesDir, { recursive: true });
11604
12318
  if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
11605
12319
  const seedFiles = buildOnboardingSeedFiles();
11606
12320
  for (const { relativePath, content } of seedFiles) {
11607
- const fullPath = path13.join(agentDataDir, relativePath);
12321
+ const fullPath = path14.join(agentDataDir, relativePath);
11608
12322
  try {
11609
12323
  await access(fullPath);
11610
12324
  } catch {
11611
- await mkdir(path13.dirname(fullPath), { recursive: true });
12325
+ await mkdir(path14.dirname(fullPath), { recursive: true });
11612
12326
  await writeFile(fullPath, content);
11613
12327
  }
11614
12328
  }
@@ -11762,6 +12476,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
11762
12476
  workingDirectory: agentDataDir,
11763
12477
  slockCliPath: this.slockCliPath,
11764
12478
  daemonApiKey: this.daemonApiKey,
12479
+ slockHome: this.slockHome,
11765
12480
  launchId: effectiveLaunchId,
11766
12481
  agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId),
11767
12482
  cliTransportTraceDir: this.cliTransportTraceDir,
@@ -12921,7 +13636,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12921
13636
  return true;
12922
13637
  }
12923
13638
  async resetWorkspace(agentId) {
12924
- const agentDataDir = path13.join(this.dataDir, agentId);
13639
+ const agentDataDir = path14.join(this.dataDir, agentId);
12925
13640
  try {
12926
13641
  await rm2(agentDataDir, { recursive: true, force: true });
12927
13642
  logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
@@ -12981,9 +13696,9 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12981
13696
  }
12982
13697
  return result;
12983
13698
  }
12984
- buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
12985
- const workspacePath = path13.join(this.dataDir, agentId);
12986
- const runtimeHomeDir = resolveRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspacePath);
13699
+ buildRuntimeProfileReport(agentId, config, sessionId, launchId, observedRuntimeHomeDir) {
13700
+ const workspacePath = path14.join(this.dataDir, agentId);
13701
+ const runtimeHomeDir = observedRuntimeHomeDir || resolveRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspacePath, { agentId, slockHome: this.slockHome });
12987
13702
  return {
12988
13703
  agentId,
12989
13704
  launchId,
@@ -13004,7 +13719,13 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13004
13719
  getAgentRuntimeProfileReport(agentId) {
13005
13720
  const running = this.agents.get(agentId);
13006
13721
  if (running) {
13007
- return this.buildRuntimeProfileReport(agentId, running.config, running.sessionId, running.launchId);
13722
+ return this.buildRuntimeProfileReport(
13723
+ agentId,
13724
+ running.config,
13725
+ running.sessionId,
13726
+ running.launchId,
13727
+ running.runtime.currentRuntimeHomeDir
13728
+ );
13008
13729
  }
13009
13730
  const idle = this.idleAgentConfigs.get(agentId);
13010
13731
  if (idle) {
@@ -13277,7 +13998,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13277
13998
  }
13278
13999
  // Workspace file browsing
13279
14000
  async getFileTree(agentId, dirPath) {
13280
- const agentDir = path13.join(this.dataDir, agentId);
14001
+ const agentDir = path14.join(this.dataDir, agentId);
13281
14002
  try {
13282
14003
  await stat2(agentDir);
13283
14004
  } catch {
@@ -13285,8 +14006,8 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13285
14006
  }
13286
14007
  let targetDir = agentDir;
13287
14008
  if (dirPath) {
13288
- const resolved = path13.resolve(agentDir, dirPath);
13289
- if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
14009
+ const resolved = path14.resolve(agentDir, dirPath);
14010
+ if (!resolved.startsWith(agentDir + path14.sep) && resolved !== agentDir) {
13290
14011
  return [];
13291
14012
  }
13292
14013
  targetDir = resolved;
@@ -13294,14 +14015,14 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13294
14015
  return this.listDirectoryChildren(targetDir, agentDir);
13295
14016
  }
13296
14017
  async readFile(agentId, filePath) {
13297
- const agentDir = path13.join(this.dataDir, agentId);
13298
- const resolved = path13.resolve(agentDir, filePath);
13299
- if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
14018
+ const agentDir = path14.join(this.dataDir, agentId);
14019
+ const resolved = path14.resolve(agentDir, filePath);
14020
+ if (!resolved.startsWith(agentDir + path14.sep) && resolved !== agentDir) {
13300
14021
  throw new Error("Access denied");
13301
14022
  }
13302
14023
  const info = await stat2(resolved);
13303
14024
  if (info.isDirectory()) throw new Error("Cannot read a directory");
13304
- const ext = path13.extname(resolved).toLowerCase();
14025
+ const ext = path14.extname(resolved).toLowerCase();
13305
14026
  if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
13306
14027
  if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
13307
14028
  const content = await readFile(resolved, "utf-8");
@@ -13352,8 +14073,8 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13352
14073
  };
13353
14074
  }
13354
14075
  const runtime = config.runtime;
13355
- const workspaceDir = path13.join(this.dataDir, agentId);
13356
- const homeDir = ensureRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspaceDir);
14076
+ const workspaceDir = path14.join(this.dataDir, agentId);
14077
+ const homeDir = agent?.runtime.currentRuntimeHomeDir || ensureRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspaceDir, { agentId, slockHome: this.slockHome });
13357
14078
  if (!actualSessionId) {
13358
14079
  return {
13359
14080
  runtime,
@@ -13391,7 +14112,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13391
14112
  let redacted = false;
13392
14113
  if (ref.reachable && ref.path) {
13393
14114
  try {
13394
- const resolved = path13.resolve(ref.path);
14115
+ const resolved = path14.resolve(ref.path);
13395
14116
  const allowedRoots = allowedTranscriptRootsForRuntime(runtime, homeDir, workspaceDir);
13396
14117
  if (!await isPathWithinAllowedRoots(resolved, allowedRoots)) {
13397
14118
  throw new Error("resolved session path is outside allowed runtime directories");
@@ -13402,7 +14123,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13402
14123
  throw new Error("symbolic links are not allowed");
13403
14124
  }
13404
14125
  if (info.isDirectory()) {
13405
- targetPath = path13.join(resolved, "state.json");
14126
+ targetPath = path14.join(resolved, "state.json");
13406
14127
  }
13407
14128
  if (!await isPathWithinAllowedRoots(targetPath, allowedRoots)) {
13408
14129
  throw new Error("resolved session state path is outside allowed runtime directories");
@@ -13514,14 +14235,22 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13514
14235
  const idle = this.idleAgentConfigs.get(agentId);
13515
14236
  const config = agent?.config ?? idle?.config ?? null;
13516
14237
  const runtime = runtimeHint || config?.runtime || "claude";
13517
- const workspaceDir = path13.join(this.dataDir, agentId);
13518
- const home = config ? ensureRuntimeHomeDir(config, os6.homedir(), workspaceDir) : os6.homedir();
14238
+ const workspaceDir = path14.join(this.dataDir, agentId);
14239
+ const hostHome = os7.homedir();
14240
+ const home = agent?.runtime.currentRuntimeHomeDir || (config ? ensureRuntimeHomeDir(config, hostHome, workspaceDir, { agentId, slockHome: this.slockHome }) : runtime === "codex" ? resolveCodexHomeRootFromEnv(process.env, { defaultHomeDir: hostHome, cwd: workspaceDir }) : hostHome);
13519
14241
  const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
14242
+ const globalDirs = runtime === "codex" ? [
14243
+ path14.join(home, "skills"),
14244
+ path14.join(home, "skills", ".system"),
14245
+ path14.join(home, ".agents", "skills"),
14246
+ ...hasConfiguredCodexHome(config) ? [] : [path14.join(hostHome, ".agents", "skills")]
14247
+ ] : paths.global.map((p) => path14.join(home, p));
14248
+ const workspaceDirs = paths.workspace.map((p) => path14.join(workspaceDir, p));
13520
14249
  const globalResults = await Promise.all(
13521
- paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
14250
+ globalDirs.map((dir) => this.scanSkillsDir(dir))
13522
14251
  );
13523
14252
  const workspaceResults = await Promise.all(
13524
- paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
14253
+ workspaceDirs.map((dir) => this.scanSkillsDir(dir))
13525
14254
  );
13526
14255
  const dedup = (skills) => {
13527
14256
  const seen = /* @__PURE__ */ new Set();
@@ -13550,7 +14279,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13550
14279
  const skills = [];
13551
14280
  for (const entry of entries) {
13552
14281
  if (entry.isDirectory() || entry.isSymbolicLink()) {
13553
- const skillMd = path13.join(dir, entry.name, "SKILL.md");
14282
+ const skillMd = path14.join(dir, entry.name, "SKILL.md");
13554
14283
  try {
13555
14284
  const content = await readFile(skillMd, "utf-8");
13556
14285
  const skill = this.parseSkillMd(entry.name, content);
@@ -13561,7 +14290,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13561
14290
  } else if (entry.name.endsWith(".md")) {
13562
14291
  const cmdName = entry.name.replace(/\.md$/, "");
13563
14292
  try {
13564
- const content = await readFile(path13.join(dir, entry.name), "utf-8");
14293
+ const content = await readFile(path14.join(dir, entry.name), "utf-8");
13565
14294
  const skill = this.parseSkillMd(cmdName, content);
13566
14295
  skill.sourcePath = dir;
13567
14296
  skills.push(skill);
@@ -13982,6 +14711,43 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13982
14711
  recordRuntimeTraceEvent(agentId, ap, name, attrs) {
13983
14712
  this.startRuntimeTrace(agentId, ap, "runtime-progress").addEvent(name, attrs);
13984
14713
  }
14714
+ restoreRuntimeDeliveryAfterAsyncRejection(agentId, ap, event) {
14715
+ const pendingBefore = ap.notifications.pendingCount;
14716
+ const restoredMessages = ap.inbox.filter(
14717
+ (message) => ap.notifications.hasContributedMessage(message, ap.sessionId)
14718
+ );
14719
+ ap.notifications.clearNoticeFingerprint();
14720
+ const restoredNotificationCount = ap.driver.supportsStdinNotification && ap.sessionId && restoredMessages.length > 0 ? ap.notifications.add(restoredMessages.length) : 0;
14721
+ if (event.requestMethod === "turn/start") {
14722
+ this.commitApmIdleState(agentId, ap, true);
14723
+ }
14724
+ const idleRetryScheduled = event.requestMethod === "turn/start" && restoredNotificationCount > 0 ? ap.notifications.schedule(() => {
14725
+ this.flushAsyncRejectedIdleDelivery(agentId);
14726
+ }, this.stdinNotificationRetryMs) : false;
14727
+ const attrs = {
14728
+ request_method: event.requestMethod,
14729
+ source: event.source,
14730
+ payloadBytes: event.payloadBytes,
14731
+ inbox_count: ap.inbox.length,
14732
+ restored_messages_count: restoredMessages.length,
14733
+ pending_notification_count_before: pendingBefore,
14734
+ pending_notification_count_after: ap.notifications.pendingCount,
14735
+ restored_notification_count: restoredNotificationCount,
14736
+ session_id_present: Boolean(ap.sessionId),
14737
+ supports_stdin_notification: ap.driver.supportsStdinNotification,
14738
+ busy_delivery_mode: ap.driver.busyDeliveryMode,
14739
+ restored_idle_state: event.requestMethod === "turn/start",
14740
+ idle_retry_scheduled: idleRetryScheduled
14741
+ };
14742
+ this.recordRuntimeTraceEvent(agentId, ap, "runtime.delivery.async_rejected", attrs);
14743
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected", {
14744
+ agentId,
14745
+ launchId: ap.launchId || void 0,
14746
+ runtime: ap.config.runtime,
14747
+ model: ap.config.model,
14748
+ ...attrs
14749
+ }, "error");
14750
+ }
13985
14751
  noteRuntimeProgress(ap, eventKind) {
13986
14752
  ap.runtimeProgress.noteRuntimeEvent(eventKind);
13987
14753
  this.invalidateRecoveryErrorView(ap);
@@ -14170,7 +14936,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14170
14936
  if ((ap.driver.startupReadiness ?? "first_event") !== "initial_turn") {
14171
14937
  return true;
14172
14938
  }
14173
- return event.kind !== "session_init" && event.kind !== "internal_progress" && event.kind !== "runtime_diagnostic";
14939
+ return event.kind !== "session_init" && event.kind !== "internal_progress" && event.kind !== "runtime_diagnostic" && event.kind !== "runtime_recovery";
14174
14940
  }
14175
14941
  handleRuntimeStartupTimeout(agentId, ap, timeoutMs) {
14176
14942
  const current = this.agents.get(agentId);
@@ -14366,6 +15132,21 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14366
15132
  if (ap) this.recordRuntimeTelemetry(agentId, ap, event);
14367
15133
  return;
14368
15134
  }
15135
+ if (event.kind === "delivery_error") {
15136
+ if (ap) {
15137
+ this.restoreRuntimeDeliveryAfterAsyncRejection(agentId, ap, event);
15138
+ } else {
15139
+ this.recordDaemonTrace("daemon.agent.delivery_error.received_without_process", {
15140
+ agentId,
15141
+ event_kind: event.kind,
15142
+ runtime: driver.id,
15143
+ request_method: event.requestMethod,
15144
+ source: event.source,
15145
+ payloadBytes: event.payloadBytes
15146
+ });
15147
+ }
15148
+ return;
15149
+ }
14369
15150
  if (ap && isStartupRequestErrorEvent(event)) {
14370
15151
  this.handleRuntimeStartupRequestError(agentId, ap, event);
14371
15152
  return;
@@ -14382,13 +15163,15 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14382
15163
  source: event.source,
14383
15164
  itemType: event.itemType,
14384
15165
  payloadBytes: event.payloadBytes
14385
- } : event.kind === "runtime_diagnostic" ? runtimeDiagnosticTraceAttrs(event) : { kind: event.kind };
15166
+ } : event.kind === "runtime_diagnostic" ? runtimeDiagnosticTraceAttrs(event) : event.kind === "runtime_recovery" ? runtimeRecoveryTraceAttrs(event) : { kind: event.kind };
14386
15167
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.event.received", eventAttrs);
14387
- if (wasStalled) {
15168
+ const recordProgressObservedAfterStall = () => {
15169
+ if (!wasStalled) return;
14388
15170
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.observed", { afterStall: true });
14389
- }
15171
+ };
14390
15172
  if (event.kind === "internal_progress") {
14391
15173
  ap.runtimeProgress.noteInternalProgress();
15174
+ recordProgressObservedAfterStall();
14392
15175
  this.invalidateRecoveryErrorView(ap);
14393
15176
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
14394
15177
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.internal_observed", {
@@ -14405,11 +15188,17 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14405
15188
  }
14406
15189
  if (event.kind === "runtime_diagnostic") {
14407
15190
  this.noteRuntimeProgress(ap, event.kind);
15191
+ recordProgressObservedAfterStall();
14408
15192
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
14409
15193
  this.recordRuntimeDiagnosticActivity(agentId, ap, event);
14410
15194
  return;
14411
15195
  }
15196
+ if (event.kind === "runtime_recovery") {
15197
+ this.recordRuntimeRecoveryActivity(agentId, ap, event);
15198
+ return;
15199
+ }
14412
15200
  this.noteRuntimeProgress(ap, event.kind);
15201
+ recordProgressObservedAfterStall();
14413
15202
  } else if (event.kind !== "internal_progress") {
14414
15203
  this.recordDaemonTrace("daemon.agent.event.received_without_process", {
14415
15204
  agentId,
@@ -15166,8 +15955,8 @@ ${RESPONSE_TARGET_HINT}`);
15166
15955
  const nodes = [];
15167
15956
  for (const entry of entries) {
15168
15957
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
15169
- const fullPath = path13.join(dir, entry.name);
15170
- const relativePath = path13.relative(rootDir, fullPath);
15958
+ const fullPath = path14.join(dir, entry.name);
15959
+ const relativePath = path14.relative(rootDir, fullPath);
15171
15960
  let info;
15172
15961
  try {
15173
15962
  info = await stat2(fullPath);
@@ -15542,8 +16331,8 @@ var ReminderCache = class {
15542
16331
  // src/machineLock.ts
15543
16332
  import { createHash as createHash4, randomUUID as randomUUID6 } from "crypto";
15544
16333
  import { mkdirSync as mkdirSync6, readFileSync as readFileSync7, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
15545
- import os7 from "os";
15546
- import path14 from "path";
16334
+ import os8 from "os";
16335
+ import path15 from "path";
15547
16336
  var INCOMPLETE_LOCK_STALE_MS = 3e4;
15548
16337
  var DaemonMachineLockConflictError = class extends Error {
15549
16338
  code = "DAEMON_MACHINE_LOCK_HELD";
@@ -15565,7 +16354,7 @@ function resolveDefaultMachineStateRoot() {
15565
16354
  return resolveSlockHomePath("machines");
15566
16355
  }
15567
16356
  function ownerPath(lockDir) {
15568
- return path14.join(lockDir, "owner.json");
16357
+ return path15.join(lockDir, "owner.json");
15569
16358
  }
15570
16359
  function readOwner(lockDir) {
15571
16360
  try {
@@ -15595,8 +16384,8 @@ function acquireDaemonMachineLock(options) {
15595
16384
  const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
15596
16385
  const fingerprint = apiKeyFingerprint(options.apiKey);
15597
16386
  const lockId = getDaemonMachineLockId(options.apiKey);
15598
- const machineDir = path14.join(rootDir, lockId);
15599
- const lockDir = path14.join(machineDir, "daemon.lock");
16387
+ const machineDir = path15.join(rootDir, lockId);
16388
+ const lockDir = path15.join(machineDir, "daemon.lock");
15600
16389
  const token = randomUUID6();
15601
16390
  mkdirSync6(machineDir, { recursive: true });
15602
16391
  for (let attempt = 0; attempt < 2; attempt += 1) {
@@ -15605,7 +16394,7 @@ function acquireDaemonMachineLock(options) {
15605
16394
  const owner = {
15606
16395
  pid: process.pid,
15607
16396
  token,
15608
- hostname: os7.hostname(),
16397
+ hostname: os8.hostname(),
15609
16398
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
15610
16399
  serverUrl: options.serverUrl,
15611
16400
  apiKeyFingerprint: fingerprint.slice(0, 16)
@@ -15657,7 +16446,7 @@ function acquireDaemonMachineLock(options) {
15657
16446
 
15658
16447
  // src/localTraceSink.ts
15659
16448
  import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
15660
- import path15 from "path";
16449
+ import path16 from "path";
15661
16450
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
15662
16451
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
15663
16452
  var DEFAULT_MAX_FILES = 8;
@@ -15694,7 +16483,7 @@ var LocalRotatingTraceSink = class {
15694
16483
  currentSize = 0;
15695
16484
  sequence = 0;
15696
16485
  constructor(options) {
15697
- this.traceDir = path15.join(options.machineDir, "traces");
16486
+ this.traceDir = path16.join(options.machineDir, "traces");
15698
16487
  this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
15699
16488
  const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
15700
16489
  const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
@@ -15724,7 +16513,7 @@ var LocalRotatingTraceSink = class {
15724
16513
  const nowMs = this.nowMsProvider();
15725
16514
  const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
15726
16515
  if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
15727
- this.currentFile = path15.join(
16516
+ this.currentFile = path16.join(
15728
16517
  this.traceDir,
15729
16518
  `daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
15730
16519
  );
@@ -15739,7 +16528,7 @@ var LocalRotatingTraceSink = class {
15739
16528
  const excess = files.length - this.maxFiles;
15740
16529
  if (excess <= 0) return;
15741
16530
  for (const file of files.slice(0, excess)) {
15742
- rmSync4(path15.join(this.traceDir, file), { force: true });
16531
+ rmSync4(path16.join(this.traceDir, file), { force: true });
15743
16532
  }
15744
16533
  }
15745
16534
  };
@@ -15830,7 +16619,7 @@ function isDiagnosticErrorAttr(key) {
15830
16619
  import { createHash as createHash6, randomUUID as randomUUID7 } from "crypto";
15831
16620
  import { gzipSync as gzipSync2 } from "zlib";
15832
16621
  import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
15833
- import path16 from "path";
16622
+ import path17 from "path";
15834
16623
 
15835
16624
  // src/traceJitter.ts
15836
16625
  import { createHash as createHash5 } from "crypto";
@@ -15929,7 +16718,7 @@ var DaemonTraceBundleUploader = class {
15929
16718
  }, nextMs);
15930
16719
  }
15931
16720
  async findUploadCandidates() {
15932
- const traceDir = path16.join(this.options.machineDir, "traces");
16721
+ const traceDir = path17.join(this.options.machineDir, "traces");
15933
16722
  let names;
15934
16723
  try {
15935
16724
  names = await readdir3(traceDir);
@@ -15941,8 +16730,8 @@ var DaemonTraceBundleUploader = class {
15941
16730
  const currentFile = this.options.currentFileProvider?.();
15942
16731
  const candidates = [];
15943
16732
  for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
15944
- const file = path16.join(traceDir, name);
15945
- if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
16733
+ const file = path17.join(traceDir, name);
16734
+ if (currentFile && path17.resolve(file) === path17.resolve(currentFile)) continue;
15946
16735
  if (await this.isUploaded(file)) continue;
15947
16736
  try {
15948
16737
  const info = await stat3(file);
@@ -16016,8 +16805,8 @@ var DaemonTraceBundleUploader = class {
16016
16805
  }
16017
16806
  }
16018
16807
  uploadStatePath(file) {
16019
- const stateDir = path16.join(this.options.machineDir, "trace-uploads");
16020
- return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
16808
+ const stateDir = path17.join(this.options.machineDir, "trace-uploads");
16809
+ return path17.join(stateDir, `${path17.basename(file)}.uploaded.json`);
16021
16810
  }
16022
16811
  async isUploaded(file) {
16023
16812
  try {
@@ -16029,9 +16818,9 @@ var DaemonTraceBundleUploader = class {
16029
16818
  }
16030
16819
  async markUploaded(file, metadata) {
16031
16820
  const stateFile = this.uploadStatePath(file);
16032
- await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
16821
+ await mkdir2(path17.dirname(stateFile), { recursive: true, mode: 448 });
16033
16822
  await writeFile2(stateFile, `${JSON.stringify({
16034
- file: path16.basename(file),
16823
+ file: path17.basename(file),
16035
16824
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
16036
16825
  ...metadata
16037
16826
  }, null, 2)}
@@ -16125,7 +16914,7 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
16125
16914
  spanAttrs: ["agentId", "event_kind", "runtime"]
16126
16915
  }
16127
16916
  };
16128
- var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
16917
+ var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
16129
16918
  var RunnerCredentialMintError2 = class extends Error {
16130
16919
  code;
16131
16920
  retryable;
@@ -16161,9 +16950,9 @@ function runnerCredentialErrorDetail2(error) {
16161
16950
  async function waitForRunnerCredentialRetry2() {
16162
16951
  await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
16163
16952
  }
16164
- function parseDaemonCliArgs(args) {
16953
+ function parseDaemonCliArgs(args, env = {}) {
16165
16954
  let serverUrl = "";
16166
- let apiKey = "";
16955
+ let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
16167
16956
  for (let i = 0; i < args.length; i++) {
16168
16957
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
16169
16958
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -16180,13 +16969,13 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
16180
16969
  }
16181
16970
  }
16182
16971
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
16183
- const thisDir = path17.dirname(fileURLToPath(moduleUrl));
16184
- const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
16972
+ const thisDir = path18.dirname(fileURLToPath(moduleUrl));
16973
+ const bundledDistPath = path18.resolve(thisDir, "cli", "index.js");
16185
16974
  try {
16186
16975
  accessSync(bundledDistPath);
16187
16976
  return bundledDistPath;
16188
16977
  } catch {
16189
- const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
16978
+ const workspaceDistPath = path18.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
16190
16979
  accessSync(workspaceDistPath);
16191
16980
  return workspaceDistPath;
16192
16981
  }
@@ -16200,11 +16989,12 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
16200
16989
  }
16201
16990
  async function runBundledSlockCli(argv) {
16202
16991
  process.argv = [process.execPath, "slock", ...argv];
16203
- await import("./dist-DSRBN3VD.js");
16992
+ await import("./dist-5BEASCX5.js");
16204
16993
  }
16205
16994
  function detectRuntimes(tracer = noopTracer) {
16206
16995
  const ids = [];
16207
16996
  const versions = {};
16997
+ const diagnostics = {};
16208
16998
  const span = tracer.startSpan("daemon.runtime.detect", {
16209
16999
  surface: "daemon",
16210
17000
  kind: "internal",
@@ -16220,20 +17010,26 @@ function detectRuntimes(tracer = noopTracer) {
16220
17010
  const probe = driver.probe();
16221
17011
  if (!probe.available) {
16222
17012
  if (probe.version) versions[runtime.id] = probe.version;
17013
+ if (probe.diagnostic) diagnostics[runtime.id] = probe.diagnostic;
16223
17014
  span.addEvent("daemon.runtime.detect.checked", {
16224
17015
  runtime: runtime.id,
16225
17016
  outcome: "unavailable",
16226
17017
  version_present: Boolean(probe.version),
17018
+ diagnostic_present: Boolean(probe.diagnostic),
17019
+ ...probe.diagnostic ? { diagnostic: probe.diagnostic } : {},
16227
17020
  binary_path_present: false
16228
17021
  });
16229
17022
  continue;
16230
17023
  }
16231
17024
  ids.push(runtime.id);
16232
17025
  if (probe.version) versions[runtime.id] = probe.version;
17026
+ if (probe.diagnostic) diagnostics[runtime.id] = probe.diagnostic;
16233
17027
  span.addEvent("daemon.runtime.detect.checked", {
16234
17028
  runtime: runtime.id,
16235
17029
  outcome: "available",
16236
17030
  version_present: Boolean(probe.version),
17031
+ diagnostic_present: Boolean(probe.diagnostic),
17032
+ ...probe.diagnostic ? { diagnostic: probe.diagnostic } : {},
16237
17033
  binary_path_present: false
16238
17034
  });
16239
17035
  continue;
@@ -16276,7 +17072,7 @@ function detectRuntimes(tracer = noopTracer) {
16276
17072
  detected_runtime_count: ids.length
16277
17073
  }
16278
17074
  });
16279
- return { ids, versions };
17075
+ return { ids, versions, ...Object.keys(diagnostics).length > 0 ? { diagnostics } : {} };
16280
17076
  }
16281
17077
  function readPositiveIntegerEnv3(name, fallback) {
16282
17078
  const raw = process.env[name];
@@ -16398,7 +17194,7 @@ var DaemonCore = class {
16398
17194
  }
16399
17195
  resolveMachineStateRoot() {
16400
17196
  if (this.options.machineStateDir) return this.options.machineStateDir;
16401
- if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
17197
+ if (this.options.dataDir) return path18.join(path18.dirname(this.options.dataDir), "machines");
16402
17198
  return resolveDefaultMachineStateRoot();
16403
17199
  }
16404
17200
  shouldEnableLocalTrace() {
@@ -16425,7 +17221,7 @@ var DaemonCore = class {
16425
17221
  sink: this.localTraceSink
16426
17222
  }));
16427
17223
  this.agentManager.setTracer(this.tracer);
16428
- this.agentManager.setCliTransportTraceDir(path17.join(machineDir, "traces"));
17224
+ this.agentManager.setCliTransportTraceDir(path18.join(machineDir, "traces"));
16429
17225
  }
16430
17226
  installTraceBundleUploader(machineDir) {
16431
17227
  if (!this.shouldEnableLocalTrace()) return;
@@ -16973,9 +17769,12 @@ var DaemonCore = class {
16973
17769
  });
16974
17770
  }
16975
17771
  handleConnect() {
16976
- const { ids: runtimes, versions: runtimeVersions } = this.runtimeDetector();
17772
+ const { ids: runtimes, versions: runtimeVersions, diagnostics: runtimeDiagnostics = {} } = this.runtimeDetector();
16977
17773
  const runtimeInfo = runtimes.map((id) => runtimeVersions[id] ? `${id} (${runtimeVersions[id]})` : id);
16978
17774
  logger.info(`[Daemon] Detected runtimes: ${runtimeInfo.join(", ") || "none"}`);
17775
+ for (const [runtime, diagnostic] of Object.entries(runtimeDiagnostics)) {
17776
+ logger.warn(`[Daemon] Runtime ${runtime} diagnostic: ${diagnostic}`);
17777
+ }
16979
17778
  if (!this.opencliWrappersRegenerated) {
16980
17779
  this.opencliWrappersRegenerated = true;
16981
17780
  try {
@@ -16995,8 +17794,8 @@ var DaemonCore = class {
16995
17794
  capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
16996
17795
  runtimes,
16997
17796
  runningAgents: runningAgentIds,
16998
- hostname: this.options.hostname ?? os8.hostname(),
16999
- os: this.options.osDescription ?? `${os8.platform()} ${os8.arch()}`,
17797
+ hostname: this.options.hostname ?? os9.hostname(),
17798
+ os: this.options.osDescription ?? `${os9.platform()} ${os9.arch()}`,
17000
17799
  daemonVersion: this.daemonVersion,
17001
17800
  ...this.computerVersion ? { computerVersion: this.computerVersion } : {}
17002
17801
  });
@@ -17077,6 +17876,8 @@ var DaemonCore = class {
17077
17876
 
17078
17877
  export {
17079
17878
  subscribeDaemonLogs,
17879
+ DAEMON_API_KEY_ENV,
17880
+ scrubDaemonAuthEnv,
17080
17881
  resolveWorkspaceDirectoryPath,
17081
17882
  scanWorkspaceDirectories,
17082
17883
  deleteWorkspaceDirectory,