@botiverse/raft-daemon 0.62.0 → 0.63.0

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";
@@ -1599,12 +1599,12 @@ var DISPLAY_PLAN_CONFIG = {
1599
1599
  var FREE_MONTHLY_FILE_UPLOAD_LIMIT_BYTES = 100 * 1024 * 1024;
1600
1600
 
1601
1601
  // src/agentProcessManager.ts
1602
- import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync4 } from "fs";
1602
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6, 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 {
@@ -3024,7 +3024,7 @@ function projectApmRuntimeStallDiagnostic(input) {
3024
3024
  gatedPhase: input.runtimeDescriptorBusyDelivery === "gated" ? input.gatedPhase : void 0,
3025
3025
  outstandingToolUses: input.outstandingToolUses,
3026
3026
  compacting: input.compacting,
3027
- reviewing: input.reviewing === true ? true : void 0,
3027
+ ...input.reviewing === true ? { reviewing: true } : {},
3028
3028
  recentStderrCount: input.recentStderrCount,
3029
3029
  recentStdoutCount: input.recentStdoutCount,
3030
3030
  ...input.runtimeTraceCounterAttrs ?? {}
@@ -4150,6 +4150,17 @@ var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
4150
4150
  var RAW_CREDENTIAL_ENV_DENYLIST = [
4151
4151
  "SLOCK_AGENT_CREDENTIAL_KEY"
4152
4152
  ];
4153
+ var WORKSPACE_CLI_TRANSPORT_FILENAMES = [
4154
+ "agent-token",
4155
+ "slock",
4156
+ "slock.cmd",
4157
+ "slock.ps1",
4158
+ "raft",
4159
+ "raft.cmd",
4160
+ "raft.ps1",
4161
+ "opencli",
4162
+ "opencli.cmd"
4163
+ ];
4153
4164
  function deriveCliFallbackCandidates(cliPath) {
4154
4165
  if (!cliPath || cliPath === "__cli") return [];
4155
4166
  const normalized = cliPath.split(path2.sep).join("/");
@@ -4210,6 +4221,18 @@ function resolveOpencliBinPath() {
4210
4221
  return null;
4211
4222
  }
4212
4223
  }
4224
+ function buildCliTransportDir(slockHome, agentId, launchId) {
4225
+ return path2.join(slockHome, "cli-transport", safePathPart(agentId), buildCliTransportLaunchPart(launchId));
4226
+ }
4227
+ function buildCliTransportLaunchPart(launchId) {
4228
+ return safePathPart(launchId || `pid-${process.pid}`);
4229
+ }
4230
+ function cleanupWorkspaceCliTransportFiles(workingDirectory) {
4231
+ const legacySlockDir = path2.join(workingDirectory, ".slock");
4232
+ for (const filename of WORKSPACE_CLI_TRANSPORT_FILENAMES) {
4233
+ rmSync(path2.join(legacySlockDir, filename), { force: true });
4234
+ }
4235
+ }
4213
4236
  function writeOpencliWrapper(slockDir, opencliBinPath, platform = process.platform) {
4214
4237
  const fallbacks = deriveOpencliFallbackCandidates(opencliBinPath);
4215
4238
  let binPath = opencliBinPath;
@@ -4340,9 +4363,10 @@ async function prepareCliTransport(ctx, extraEnv = {}, platform = process.platfo
4340
4363
  );
4341
4364
  }
4342
4365
  }
4343
- const slockDir = path2.join(ctx.workingDirectory, ".slock");
4366
+ const slockHome = ctx.slockHome ? path2.resolve(ctx.slockHome) : resolveSlockHome();
4367
+ cleanupWorkspaceCliTransportFiles(ctx.workingDirectory);
4368
+ const slockDir = buildCliTransportDir(slockHome, ctx.agentId, ctx.launchId);
4344
4369
  mkdirSync(slockDir, { recursive: true });
4345
- const slockHome = resolveSlockHome();
4346
4370
  const tokenFile = path2.join(slockDir, "agent-token");
4347
4371
  const agentCredentialKey = ctx.config.agentCredentialKey;
4348
4372
  let agentCredentialProxy = null;
@@ -4357,7 +4381,7 @@ async function prepareCliTransport(ctx, extraEnv = {}, platform = process.platfo
4357
4381
  activeCapabilities: DEFAULT_ACTIVE_CAPABILITIES,
4358
4382
  inboxCoordinator: ctx.agentCredentialProxyInboxCoordinator
4359
4383
  });
4360
- const launchPart = safePathPart(ctx.launchId || `pid-${process.pid}`);
4384
+ const launchPart = buildCliTransportLaunchPart(ctx.launchId);
4361
4385
  const proxyTokenDir = path2.join(slockHome, "agent-proxy-tokens", safePathPart(ctx.agentId));
4362
4386
  mkdirSync(proxyTokenDir, { recursive: true, mode: 448 });
4363
4387
  agentCredentialProxyTokenFile = path2.join(proxyTokenDir, `${launchPart}.token`);
@@ -4474,7 +4498,6 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
4474
4498
  ...ctx.launchId ? { SLOCK_AGENT_LAUNCH_ID: ctx.launchId } : {},
4475
4499
  ...ctx.cliTransportTraceDir ? { [CLI_TRANSPORT_TRACE_DIR_ENV]: ctx.cliTransportTraceDir } : {},
4476
4500
  SLOCK_SERVER_URL: ctx.config.serverUrl,
4477
- ...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
4478
4501
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
4479
4502
  };
4480
4503
  delete spawnEnv.SLOCK_AGENT_TOKEN;
@@ -4485,9 +4508,7 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
4485
4508
  delete spawnEnv.SLOCK_AGENT_PROXY_TOKEN;
4486
4509
  delete spawnEnv.SLOCK_AGENT_PROXY_TOKEN_FILE;
4487
4510
  delete spawnEnv.SLOCK_AGENT_ACTIVE_CAPABILITIES;
4488
- if (agentCredentialProxy) {
4489
- delete spawnEnv.SLOCK_AGENT_TOKEN_FILE;
4490
- }
4511
+ delete spawnEnv.SLOCK_AGENT_TOKEN_FILE;
4491
4512
  return {
4492
4513
  slockDir,
4493
4514
  tokenFile,
@@ -5177,8 +5198,77 @@ var ClaudeDriver = class {
5177
5198
  // src/drivers/codex.ts
5178
5199
  import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
5179
5200
  import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
5201
+ import os4 from "os";
5202
+ import path7 from "path";
5203
+
5204
+ // src/drivers/codexHome.ts
5180
5205
  import os3 from "os";
5206
+ import { mkdirSync as mkdirSync3 } from "fs";
5181
5207
  import path6 from "path";
5208
+ function readConfiguredCodexHome(env) {
5209
+ const raw = env.CODEX_HOME;
5210
+ return typeof raw === "string" && raw.trim().length > 0 ? raw : null;
5211
+ }
5212
+ function readNonEmpty(value) {
5213
+ return typeof value === "string" && value.trim().length > 0 ? value : null;
5214
+ }
5215
+ function safeCodexHomePathPart(value) {
5216
+ const safe = value.replace(/[^a-zA-Z0-9._-]/g, "_");
5217
+ return safe.length > 0 ? safe : "agent";
5218
+ }
5219
+ function resolveSlockManagedCodexHome(slockHome, agentId) {
5220
+ return path6.join(path6.resolve(slockHome), "codex-home", safeCodexHomePathPart(agentId));
5221
+ }
5222
+ function resolveDefaultCodexHomeRootForAgent(agentId, opts = {}) {
5223
+ return resolveSlockManagedCodexHome(readNonEmpty(opts.slockHome) ?? resolveSlockHome(opts.env), agentId);
5224
+ }
5225
+ function applyDefaultCodexHomeToEnv(env, agentId, opts = {}) {
5226
+ if (readConfiguredCodexHome(env)) return null;
5227
+ const codexHome = resolveDefaultCodexHomeRootForAgent(agentId, {
5228
+ slockHome: readNonEmpty(opts.slockHome) ?? readNonEmpty(env.SLOCK_HOME) ?? void 0,
5229
+ env
5230
+ });
5231
+ mkdirSync3(codexHome, { recursive: true });
5232
+ env.CODEX_HOME = codexHome;
5233
+ return codexHome;
5234
+ }
5235
+ function resolveCodexHomeRootFromEnv(env = process.env, opts = {}) {
5236
+ const raw = readConfiguredCodexHome(env);
5237
+ if (raw) {
5238
+ return path6.resolve(opts.cwd ?? process.cwd(), raw);
5239
+ }
5240
+ return path6.join(opts.defaultHomeDir ?? os3.homedir(), ".codex");
5241
+ }
5242
+ function hasConfiguredCodexHome(config, baseEnv = process.env) {
5243
+ const launchRuntimeFields = config ? runtimeConfigToLaunchFields(config) : null;
5244
+ return Boolean(readConfiguredCodexHome({
5245
+ ...baseEnv,
5246
+ ...launchRuntimeFields?.envVars || {}
5247
+ }));
5248
+ }
5249
+ function resolveCodexHomeRootFromConfig(config, defaultHomeDir, cwd, baseEnv = process.env, opts = {}) {
5250
+ const launchRuntimeFields = runtimeConfigToLaunchFields(config);
5251
+ const env = {
5252
+ ...baseEnv,
5253
+ ...launchRuntimeFields.envVars || {}
5254
+ };
5255
+ if (readConfiguredCodexHome(env)) {
5256
+ return resolveCodexHomeRootFromEnv(env, { defaultHomeDir, cwd });
5257
+ }
5258
+ if (opts.agentId) {
5259
+ return resolveDefaultCodexHomeRootForAgent(opts.agentId, { slockHome: opts.slockHome, env });
5260
+ }
5261
+ return resolveCodexHomeRootFromEnv(env, { defaultHomeDir, cwd });
5262
+ }
5263
+ function codexStateRootCandidates(homeDirOrCodexRoot) {
5264
+ return [
5265
+ homeDirOrCodexRoot,
5266
+ path6.join(homeDirOrCodexRoot, ".codex")
5267
+ ];
5268
+ }
5269
+ function codexSessionRootCandidates(homeDirOrCodexRoot) {
5270
+ return codexStateRootCandidates(homeDirOrCodexRoot).map((root) => path6.join(root, "sessions"));
5271
+ }
5182
5272
 
5183
5273
  // src/runtimeTurnState.ts
5184
5274
  var RuntimeTurnState = class {
@@ -5359,7 +5449,7 @@ function codexNotificationDiagnosticEvent(message) {
5359
5449
  return null;
5360
5450
  }
5361
5451
  const sessionId = codexMessageThreadId(message);
5362
- const path18 = boundedString(params.path);
5452
+ const path19 = boundedString(params.path);
5363
5453
  return {
5364
5454
  kind: "runtime_diagnostic",
5365
5455
  severity: "warning",
@@ -5367,12 +5457,42 @@ function codexNotificationDiagnosticEvent(message) {
5367
5457
  itemType: message.method,
5368
5458
  message: diagnosticMessage,
5369
5459
  ...details ? { details } : {},
5370
- ...path18 ? { path: path18 } : {},
5460
+ ...path19 ? { path: path19 } : {},
5371
5461
  ...params.range !== void 0 ? { range: params.range } : {},
5372
5462
  payloadBytes: payloadBytes(params),
5373
5463
  ...sessionId ? { sessionId } : {}
5374
5464
  };
5375
5465
  }
5466
+ function codexThreadStatusChangedEvent(message) {
5467
+ if (message.method !== "thread/status/changed") return null;
5468
+ const params = message.params ?? {};
5469
+ const status = params.status ?? params.thread?.status;
5470
+ if (!status || typeof status !== "object") return null;
5471
+ const statusType = nonEmptyString2(status.type);
5472
+ const sessionId = codexMessageThreadId(message);
5473
+ if (statusType === "systemError") {
5474
+ const statusMessage = boundedString(status.message) ?? boundedString(status.error?.message) ?? getCodexNotificationErrorMessage(params) ?? "Codex thread entered system error state";
5475
+ return {
5476
+ kind: "error",
5477
+ message: statusMessage
5478
+ };
5479
+ }
5480
+ if (statusType !== "active" || !Array.isArray(status.activeFlags)) return null;
5481
+ const activeFlags = status.activeFlags.filter((flag) => typeof flag === "string");
5482
+ const waitFlags = activeFlags.filter((flag) => flag === "waitingOnApproval" || flag === "waitingOnUserInput");
5483
+ if (waitFlags.length === 0) return null;
5484
+ const waitLabel = waitFlags.includes("waitingOnApproval") && waitFlags.includes("waitingOnUserInput") ? "approval and user input" : waitFlags.includes("waitingOnApproval") ? "approval" : "user input";
5485
+ return {
5486
+ kind: "runtime_diagnostic",
5487
+ severity: "warning",
5488
+ source: "codex_app_server_notification",
5489
+ itemType: "thread/status/changed",
5490
+ message: `Codex thread is waiting on ${waitLabel}`,
5491
+ details: `Active flags: ${waitFlags.join(", ")}`,
5492
+ payloadBytes: payloadBytes(params),
5493
+ ...sessionId ? { sessionId } : {}
5494
+ };
5495
+ }
5376
5496
  function joinReasoningSummaryText(item) {
5377
5497
  const summary = Array.isArray(item.summary) ? item.summary.filter((entry) => typeof entry === "string") : [];
5378
5498
  return summary.join("\n").trim();
@@ -5400,10 +5520,27 @@ function codexMcpToolName(item) {
5400
5520
  function codexMessageThreadId(message) {
5401
5521
  return nonEmptyString2(message.params?.threadId) ?? nonEmptyString2(message.params?.thread?.id) ?? nonEmptyString2(message.params?.sessionId);
5402
5522
  }
5523
+ function codexAgentMessagePhase(value) {
5524
+ return typeof value === "string" && value.length > 0 ? value : null;
5525
+ }
5526
+ function codexAgentMessageDeltaPhase(message) {
5527
+ return codexAgentMessagePhase(message.params?.phase) ?? codexAgentMessagePhase(message.params?.item?.phase);
5528
+ }
5529
+ function isUserVisibleAgentMessagePhase(phase) {
5530
+ return phase === null || phase === "final_answer";
5531
+ }
5532
+ function codexSuppressedAgentMessageEvent(itemType, phase, text, itemId) {
5533
+ return codexNotificationProgressEvent(itemType, {
5534
+ ...typeof itemId === "string" ? { itemId } : {},
5535
+ ...phase ? { phase } : {},
5536
+ bytes: Buffer.byteLength(text, "utf8")
5537
+ });
5538
+ }
5403
5539
  var CodexEventNormalizer = class {
5404
5540
  currentThreadId = null;
5405
5541
  sessionAnnounced = false;
5406
5542
  streamedAgentMessageIds = /* @__PURE__ */ new Set();
5543
+ agentMessagePhases = /* @__PURE__ */ new Map();
5407
5544
  streamedReasoningIds = /* @__PURE__ */ new Set();
5408
5545
  fileChangeToolCallCounts = /* @__PURE__ */ new Map();
5409
5546
  turnState = new RuntimeTurnState();
@@ -5412,6 +5549,7 @@ var CodexEventNormalizer = class {
5412
5549
  this.turnState.reset();
5413
5550
  this.sessionAnnounced = false;
5414
5551
  this.streamedAgentMessageIds.clear();
5552
+ this.agentMessagePhases.clear();
5415
5553
  this.streamedReasoningIds.clear();
5416
5554
  this.fileChangeToolCallCounts.clear();
5417
5555
  }
@@ -5489,12 +5627,17 @@ var CodexEventNormalizer = class {
5489
5627
  case "item/agentMessage/delta": {
5490
5628
  const delta = message.params?.delta;
5491
5629
  const itemId = message.params?.itemId;
5630
+ const phase = codexAgentMessageDeltaPhase(message) ?? (typeof itemId === "string" ? this.agentMessagePhases.get(itemId) ?? null : null);
5492
5631
  if (typeof itemId === "string") {
5493
5632
  this.streamedAgentMessageIds.add(itemId);
5494
5633
  }
5495
5634
  if (typeof delta === "string" && delta.length > 0) {
5496
5635
  this.turnState.markProgress();
5497
- events.push({ kind: "text", text: delta });
5636
+ if (isUserVisibleAgentMessagePhase(phase)) {
5637
+ events.push({ kind: "text", text: delta });
5638
+ } else {
5639
+ events.push(codexSuppressedAgentMessageEvent("agent_message_non_final_delta", phase, delta, itemId));
5640
+ }
5498
5641
  }
5499
5642
  break;
5500
5643
  }
@@ -5545,6 +5688,13 @@ var CodexEventNormalizer = class {
5545
5688
  }
5546
5689
  break;
5547
5690
  }
5691
+ case "thread/status/changed": {
5692
+ const event = codexThreadStatusChangedEvent(message);
5693
+ if (event) {
5694
+ events.push(event);
5695
+ }
5696
+ break;
5697
+ }
5548
5698
  case "item/started":
5549
5699
  case "item/completed": {
5550
5700
  const item = message.params?.item;
@@ -5566,12 +5716,21 @@ var CodexEventNormalizer = class {
5566
5716
  }
5567
5717
  break;
5568
5718
  case "agentMessage":
5719
+ if ((isStarted || isCompleted) && typeof item.id === "string") {
5720
+ this.agentMessagePhases.set(item.id, codexAgentMessagePhase(item.phase));
5721
+ }
5569
5722
  if (isCompleted && typeof item.id === "string" && !this.streamedAgentMessageIds.has(item.id) && typeof item.text === "string" && item.text.length > 0) {
5723
+ const phase = codexAgentMessagePhase(item.phase);
5570
5724
  this.turnState.markProgress();
5571
- events.push({ kind: "text", text: item.text });
5725
+ if (isUserVisibleAgentMessagePhase(phase)) {
5726
+ events.push({ kind: "text", text: item.text });
5727
+ } else {
5728
+ events.push(codexSuppressedAgentMessageEvent("agent_message_non_final_completed", phase, item.text, item.id));
5729
+ }
5572
5730
  }
5573
5731
  if (isCompleted && typeof item.id === "string") {
5574
5732
  this.streamedAgentMessageIds.delete(item.id);
5733
+ this.agentMessagePhases.delete(item.id);
5575
5734
  }
5576
5735
  break;
5577
5736
  case "commandExecution":
@@ -5710,14 +5869,15 @@ var CodexEventNormalizer = class {
5710
5869
 
5711
5870
  // src/drivers/codex.ts
5712
5871
  var CODEX_DESKTOP_BUNDLE_PATH = "/Applications/Codex.app/Contents/Resources/codex";
5872
+ var CODEX_APP_SERVER_PROBE_ARGS = ["app-server", "--help"];
5713
5873
  function isWindowsSandboxRunner(commandPath) {
5714
- return path6.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
5874
+ return path7.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
5715
5875
  }
5716
5876
  function resolveWindowsNpmCodexEntry(deps = {}) {
5717
5877
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
5718
5878
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
5719
5879
  const env = deps.env ?? process.env;
5720
- const winPath = path6.win32;
5880
+ const winPath = path7.win32;
5721
5881
  try {
5722
5882
  const globalRoot = String(execFileSyncFn("npm", ["root", "-g"], {
5723
5883
  encoding: "utf8",
@@ -5739,7 +5899,7 @@ function resolveWindowsCodexDesktopEntry(deps = {}) {
5739
5899
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
5740
5900
  const env = deps.env ?? process.env;
5741
5901
  const homeDir = deps.homeDir;
5742
- const winPath = path6.win32;
5902
+ const winPath = path7.win32;
5743
5903
  const candidates = [
5744
5904
  env.LOCALAPPDATA ? winPath.join(env.LOCALAPPDATA, "OpenAI", "Codex", "bin", "codex.exe") : null,
5745
5905
  env.USERPROFILE ? winPath.join(env.USERPROFILE, "AppData", "Local", "OpenAI", "Codex", "bin", "codex.exe") : null,
@@ -5750,69 +5910,158 @@ function resolveWindowsCodexDesktopEntry(deps = {}) {
5750
5910
  }
5751
5911
  return null;
5752
5912
  }
5753
- function resolveCodexCommand(deps = {}) {
5913
+ function codexSpawnCandidates(deps = {}) {
5754
5914
  const platform = deps.platform ?? process.platform;
5755
5915
  if (platform === "win32") {
5916
+ const candidates2 = [];
5756
5917
  const npmEntry = resolveWindowsNpmCodexEntry(deps);
5757
- if (npmEntry) return npmEntry;
5918
+ if (npmEntry) {
5919
+ candidates2.push({
5920
+ source: "npm_global",
5921
+ command: process.execPath,
5922
+ argsPrefix: [npmEntry],
5923
+ shell: false
5924
+ });
5925
+ }
5758
5926
  const command = resolveCommandOnPath("codex", deps);
5759
- if (command && !isWindowsSandboxRunner(command)) return command;
5760
- return resolveWindowsCodexDesktopEntry(deps);
5927
+ if (command && !isWindowsSandboxRunner(command)) {
5928
+ candidates2.push({
5929
+ source: "path",
5930
+ command,
5931
+ argsPrefix: [],
5932
+ shell: requiresWindowsShell(command, platform)
5933
+ });
5934
+ }
5935
+ const desktopEntry = resolveWindowsCodexDesktopEntry(deps);
5936
+ if (desktopEntry && !candidates2.some((candidate) => candidate.command === desktopEntry)) {
5937
+ candidates2.push({
5938
+ source: "desktop_install",
5939
+ command: desktopEntry,
5940
+ argsPrefix: [],
5941
+ shell: false
5942
+ });
5943
+ }
5944
+ return candidates2;
5761
5945
  }
5946
+ const candidates = [];
5762
5947
  const pathCommand = resolveCommandOnPath("codex", deps);
5763
- if (pathCommand) return pathCommand;
5948
+ if (pathCommand) {
5949
+ candidates.push({
5950
+ source: "path",
5951
+ command: pathCommand,
5952
+ argsPrefix: [],
5953
+ shell: false
5954
+ });
5955
+ }
5764
5956
  if (platform === "darwin") {
5765
- return firstExistingPath([CODEX_DESKTOP_BUNDLE_PATH], deps);
5957
+ const bundleCommand = firstExistingPath([CODEX_DESKTOP_BUNDLE_PATH], deps);
5958
+ if (bundleCommand && !candidates.some((candidate) => candidate.command === bundleCommand)) {
5959
+ candidates.push({
5960
+ source: "desktop_bundle",
5961
+ command: bundleCommand,
5962
+ argsPrefix: [],
5963
+ shell: false
5964
+ });
5965
+ }
5766
5966
  }
5767
- return null;
5967
+ return candidates;
5968
+ }
5969
+ function formatCodexCandidate(candidate) {
5970
+ return [candidate.command, ...candidate.argsPrefix].join(" ");
5971
+ }
5972
+ function describeCodexProbeFailure(error) {
5973
+ if (error && typeof error === "object") {
5974
+ const candidate = error;
5975
+ if (typeof candidate.status === "number") return `exit status ${candidate.status}`;
5976
+ if (typeof candidate.signal === "string") return `terminated by ${candidate.signal}`;
5977
+ if (typeof candidate.code === "string") return candidate.code;
5978
+ }
5979
+ return "probe failed";
5980
+ }
5981
+ function validateCodexAppServer(candidate, deps = {}) {
5982
+ const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
5983
+ const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
5984
+ try {
5985
+ execFileSyncFn(candidate.command, [...candidate.argsPrefix, ...CODEX_APP_SERVER_PROBE_ARGS], {
5986
+ stdio: ["ignore", "pipe", "pipe"],
5987
+ env,
5988
+ timeout: 5e3,
5989
+ shell: candidate.shell
5990
+ });
5991
+ return null;
5992
+ } catch (error) {
5993
+ return describeCodexProbeFailure(error);
5994
+ }
5995
+ }
5996
+ function readCodexCandidateVersion(candidate, deps = {}) {
5997
+ const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
5998
+ const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
5999
+ try {
6000
+ const output = execFileSyncFn(candidate.command, [...candidate.argsPrefix, "--version"], {
6001
+ stdio: ["ignore", "pipe", "pipe"],
6002
+ env,
6003
+ timeout: 5e3,
6004
+ shell: candidate.shell
6005
+ });
6006
+ return (Buffer.isBuffer(output) ? output.toString("utf8") : String(output ?? "")).trim().split(/\r?\n/)[0] || null;
6007
+ } catch {
6008
+ return null;
6009
+ }
6010
+ }
6011
+ function resolveCompatibleCodexCandidate(deps = {}) {
6012
+ const rejected = [];
6013
+ for (const candidate of codexSpawnCandidates(deps)) {
6014
+ const failure = validateCodexAppServer(candidate, deps);
6015
+ if (!failure) return { candidate, rejected };
6016
+ rejected.push(`${candidate.source} ${formatCodexCandidate(candidate)} rejected: app-server probe ${failure}`);
6017
+ }
6018
+ return { candidate: null, rejected };
5768
6019
  }
5769
6020
  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
- }
6021
+ const { candidate, rejected } = resolveCompatibleCodexCandidate(deps);
6022
+ const diagnostic = rejected.length > 0 ? rejected.join("; ") : void 0;
6023
+ if (!candidate) {
6024
+ return {
6025
+ available: false,
6026
+ diagnostic: diagnostic ?? "No Codex CLI app-server candidate was found."
6027
+ };
5780
6028
  }
5781
- const command = resolveCodexCommand(deps);
5782
- if (!command) return { available: false };
5783
6029
  return {
5784
6030
  available: true,
5785
- version: readCommandVersion(command, [], deps) ?? void 0
6031
+ version: readCodexCandidateVersion(candidate, deps) ?? void 0,
6032
+ ...diagnostic ? { diagnostic } : {}
5786
6033
  };
5787
6034
  }
5788
6035
  function resolveCodexSpawn(commandArgs, deps = {}) {
5789
- if ((deps.platform ?? process.platform) !== "win32") {
5790
- return { command: resolveCodexCommand(deps) ?? "codex", args: commandArgs, shell: false };
6036
+ const { candidate, rejected } = resolveCompatibleCodexCandidate(deps);
6037
+ if (candidate) {
6038
+ return { command: candidate.command, args: [...candidate.argsPrefix, ...commandArgs], shell: candidate.shell };
5791
6039
  }
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) };
5803
- }
5804
- const desktopEntry = resolveWindowsCodexDesktopEntry(deps);
5805
- if (desktopEntry) {
5806
- return { command: desktopEntry, args: commandArgs, shell: false };
6040
+ const diagnostic = rejected.length > 0 ? ` Rejected candidates: ${rejected.join("; ")}.` : "";
6041
+ if ((deps.platform ?? process.platform) === "win32") {
6042
+ throw new Error(
6043
+ "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
6044
+ );
5807
6045
  }
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
- );
6046
+ throw new Error(`Cannot resolve a compatible Codex CLI app-server entry point.${diagnostic}`);
5811
6047
  }
5812
6048
  function isCodexMissingRolloutError(message) {
5813
6049
  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
6050
  }
5815
- function classifyCodexResumeError(message) {
6051
+ function codexMissingRolloutRecoveryMessage() {
6052
+ return "Codex could not resume its previous thread; Slock started a fresh Codex thread.";
6053
+ }
6054
+ function codexMissingRolloutRecoveryDetails() {
6055
+ return "Use Slock conversation history and local MEMORY.md/notes as the recovery point; do not assume prior Codex thread context is loaded.";
6056
+ }
6057
+ function prependCodexRecoveryNotice(prompt, recovery) {
6058
+ return `${recovery.message}
6059
+
6060
+ ${recovery.details}
6061
+
6062
+ ${prompt}`;
6063
+ }
6064
+ function classifyCodexResumeError(message, requestedSessionId) {
5816
6065
  if (isCodexMissingRolloutError(message)) {
5817
6066
  return {
5818
6067
  kind: "missing_rollout",
@@ -5826,6 +6075,15 @@ function classifyCodexResumeError(message) {
5826
6075
  resume_error_class: "missing_rollout",
5827
6076
  recovery_action: "fallback_fresh_thread"
5828
6077
  }
6078
+ },
6079
+ recovery: {
6080
+ kind: "runtime_recovery",
6081
+ source: "codex_resume_missing_rollout",
6082
+ resumeErrorClass: "missing_rollout",
6083
+ recoveryAction: "fallback_fresh_thread",
6084
+ message: codexMissingRolloutRecoveryMessage(),
6085
+ details: codexMissingRolloutRecoveryDetails(),
6086
+ ...requestedSessionId ? { requestedSessionId } : {}
5829
6087
  }
5830
6088
  };
5831
6089
  }
@@ -5849,6 +6107,26 @@ function isJsonRpcResponse(message) {
5849
6107
  function isCodexServerRequest(message) {
5850
6108
  return message.id !== void 0 && typeof message.method === "string" && !isJsonRpcResponse(message);
5851
6109
  }
6110
+ function payloadBytes2(value) {
6111
+ try {
6112
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
6113
+ } catch {
6114
+ return void 0;
6115
+ }
6116
+ }
6117
+ function extractCodexHome(result) {
6118
+ if (!result || typeof result !== "object") return null;
6119
+ const candidate = result.codexHome;
6120
+ return typeof candidate === "string" && candidate.trim().length > 0 ? candidate : null;
6121
+ }
6122
+ function isCompatibleInitializeResult(result) {
6123
+ if (!result || typeof result !== "object" || Array.isArray(result)) return false;
6124
+ const userAgent = result.userAgent;
6125
+ return typeof userAgent === "string" && userAgent.trim().length > 0;
6126
+ }
6127
+ function unsupportedInitializeResultMessage() {
6128
+ return "Codex app-server initialize response is missing the expected userAgent handshake field; upgrade Codex CLI to a compatible app-server build.";
6129
+ }
5852
6130
  var CodexDriver = class {
5853
6131
  id = "codex";
5854
6132
  lifecycle = {
@@ -5919,11 +6197,19 @@ var CodexDriver = class {
5919
6197
  pendingThreadRequestId = null;
5920
6198
  pendingThreadRequestMethod = null;
5921
6199
  pendingResumeFallbackParams = null;
6200
+ pendingResumeThreadId = null;
5922
6201
  pendingInitialTurnRequestId = null;
6202
+ pendingDeliveryRequests = /* @__PURE__ */ new Map();
5923
6203
  initialTurnStarted = false;
5924
6204
  normalizer = new CodexEventNormalizer();
6205
+ codexHomeRoot = null;
6206
+ spawnWorkingDirectory = null;
6207
+ get currentRuntimeHomeDir() {
6208
+ return this.codexHomeRoot;
6209
+ }
5925
6210
  async spawn(ctx) {
5926
6211
  const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
6212
+ applyDefaultCodexHomeToEnv(spawnEnv, ctx.agentId, { slockHome: ctx.slockHome });
5927
6213
  this.process = null;
5928
6214
  this.requestId = 0;
5929
6215
  this.pendingInitialPrompt = ctx.prompt;
@@ -5932,9 +6218,16 @@ var CodexDriver = class {
5932
6218
  this.pendingThreadRequestId = null;
5933
6219
  this.pendingThreadRequestMethod = null;
5934
6220
  this.pendingResumeFallbackParams = null;
6221
+ this.pendingResumeThreadId = null;
5935
6222
  this.pendingInitialTurnRequestId = null;
6223
+ this.pendingDeliveryRequests.clear();
5936
6224
  this.initialTurnStarted = false;
5937
6225
  this.normalizer.reset();
6226
+ this.spawnWorkingDirectory = ctx.workingDirectory;
6227
+ this.codexHomeRoot = resolveCodexHomeRootFromEnv(spawnEnv, {
6228
+ defaultHomeDir: os4.homedir(),
6229
+ cwd: ctx.workingDirectory
6230
+ });
5938
6231
  const args = ["app-server", "--listen", "stdio://"];
5939
6232
  const { command, args: spawnArgs, shell } = resolveCodexSpawn(args);
5940
6233
  const proc = spawn2(command, spawnArgs, {
@@ -5969,6 +6262,24 @@ var CodexDriver = class {
5969
6262
  const isResponse = isJsonRpcResponse(message);
5970
6263
  if (isResponse && hasJsonRpcField(message, "result")) {
5971
6264
  if (message.id === this.initializeRequestId) {
6265
+ if (!isCompatibleInitializeResult(message.result)) {
6266
+ this.initializeRequestId = null;
6267
+ this.pendingThreadRequest = null;
6268
+ this.pendingThreadRequestId = null;
6269
+ this.pendingThreadRequestMethod = null;
6270
+ this.pendingResumeFallbackParams = null;
6271
+ this.pendingResumeThreadId = null;
6272
+ events.push({
6273
+ kind: "error",
6274
+ message: unsupportedInitializeResultMessage(),
6275
+ startupRequestMethod: "initialize"
6276
+ });
6277
+ return events;
6278
+ }
6279
+ const codexHome = extractCodexHome(message.result);
6280
+ if (codexHome) {
6281
+ this.codexHomeRoot = path7.resolve(this.spawnWorkingDirectory ?? process.cwd(), codexHome);
6282
+ }
5972
6283
  this.initializeRequestId = null;
5973
6284
  this.sendNotification("initialized", {});
5974
6285
  if (this.pendingThreadRequest) {
@@ -5984,6 +6295,7 @@ var CodexDriver = class {
5984
6295
  this.pendingThreadRequestId = null;
5985
6296
  this.pendingThreadRequestMethod = null;
5986
6297
  this.pendingResumeFallbackParams = null;
6298
+ this.pendingResumeThreadId = null;
5987
6299
  events.push({
5988
6300
  kind: "error",
5989
6301
  message: message.error?.message || "Codex app-server request failed",
@@ -5995,9 +6307,16 @@ var CodexDriver = class {
5995
6307
  if (hasJsonRpcField(message, "error")) {
5996
6308
  const errorMessage = message.error?.message || "Codex app-server request failed";
5997
6309
  const requestMethod = this.pendingThreadRequestMethod;
5998
- const resumeErrorClassification = requestMethod === "thread/resume" ? classifyCodexResumeError(errorMessage) : null;
6310
+ const resumeErrorClassification = requestMethod === "thread/resume" ? classifyCodexResumeError(errorMessage, this.pendingResumeThreadId || void 0) : null;
5999
6311
  if (this.pendingResumeFallbackParams && resumeErrorClassification?.kind === "missing_rollout") {
6000
6312
  events.push(resumeErrorClassification.telemetry);
6313
+ events.push(resumeErrorClassification.recovery);
6314
+ if (this.pendingInitialPrompt) {
6315
+ this.pendingInitialPrompt = prependCodexRecoveryNotice(
6316
+ this.pendingInitialPrompt,
6317
+ resumeErrorClassification.recovery
6318
+ );
6319
+ }
6001
6320
  this.sendThreadRequest("thread/start", this.pendingResumeFallbackParams);
6002
6321
  this.pendingResumeFallbackParams = null;
6003
6322
  return events;
@@ -6005,12 +6324,14 @@ var CodexDriver = class {
6005
6324
  this.pendingThreadRequestId = null;
6006
6325
  this.pendingThreadRequestMethod = null;
6007
6326
  this.pendingResumeFallbackParams = null;
6327
+ this.pendingResumeThreadId = null;
6008
6328
  events.push(requestMethod ? { kind: "error", message: errorMessage, startupRequestMethod: requestMethod } : { kind: "error", message: errorMessage });
6009
6329
  return events;
6010
6330
  }
6011
6331
  this.pendingThreadRequestId = null;
6012
6332
  this.pendingThreadRequestMethod = null;
6013
6333
  this.pendingResumeFallbackParams = null;
6334
+ this.pendingResumeThreadId = null;
6014
6335
  }
6015
6336
  if (isResponse && message.id === this.pendingInitialTurnRequestId) {
6016
6337
  this.pendingInitialTurnRequestId = null;
@@ -6025,6 +6346,21 @@ var CodexDriver = class {
6025
6346
  }
6026
6347
  return events;
6027
6348
  }
6349
+ if (isResponse && message.id !== void 0 && this.pendingDeliveryRequests.has(message.id)) {
6350
+ const requestMethod = this.pendingDeliveryRequests.get(message.id);
6351
+ this.pendingDeliveryRequests.delete(message.id);
6352
+ if (hasJsonRpcField(message, "error")) {
6353
+ const params = message.error ?? {};
6354
+ events.push({
6355
+ kind: "delivery_error",
6356
+ message: message.error?.message || "Codex app-server request failed",
6357
+ requestMethod,
6358
+ source: "codex_app_server_response",
6359
+ payloadBytes: payloadBytes2(params)
6360
+ });
6361
+ }
6362
+ return events;
6363
+ }
6028
6364
  const result = this.normalizer.normalizeMessage(message);
6029
6365
  if (result.turnStarted) {
6030
6366
  this.pendingInitialTurnRequestId = null;
@@ -6043,9 +6379,11 @@ var CodexDriver = class {
6043
6379
  const mode = opts?.mode || "busy";
6044
6380
  if (mode === "busy") {
6045
6381
  if (!this.normalizer.canSteerBusy) return null;
6382
+ const id2 = this.nextRequestId();
6383
+ this.pendingDeliveryRequests.set(id2, "turn/steer");
6046
6384
  return JSON.stringify({
6047
6385
  jsonrpc: "2.0",
6048
- id: this.nextRequestId(),
6386
+ id: id2,
6049
6387
  method: "turn/steer",
6050
6388
  params: {
6051
6389
  threadId: this.normalizer.threadId,
@@ -6054,9 +6392,11 @@ var CodexDriver = class {
6054
6392
  }
6055
6393
  });
6056
6394
  }
6395
+ const id = this.nextRequestId();
6396
+ this.pendingDeliveryRequests.set(id, "turn/start");
6057
6397
  return JSON.stringify({
6058
6398
  jsonrpc: "2.0",
6059
- id: this.nextRequestId(),
6399
+ id,
6060
6400
  method: "turn/start",
6061
6401
  params: {
6062
6402
  threadId: this.normalizer.threadId,
@@ -6112,9 +6452,11 @@ var CodexDriver = class {
6112
6452
  this.pendingThreadRequestId = id;
6113
6453
  this.pendingThreadRequestMethod = method;
6114
6454
  this.pendingResumeFallbackParams = null;
6455
+ this.pendingResumeThreadId = null;
6115
6456
  if (method === "thread/resume") {
6116
6457
  const { threadId: _threadId, ...freshParams } = params;
6117
6458
  this.pendingResumeFallbackParams = freshParams;
6459
+ this.pendingResumeThreadId = typeof _threadId === "string" && _threadId.trim() ? _threadId.trim() : null;
6118
6460
  }
6119
6461
  return id;
6120
6462
  }
@@ -6126,12 +6468,194 @@ var CodexDriver = class {
6126
6468
  }) + "\n");
6127
6469
  }
6128
6470
  async detectModels() {
6129
- return detectCodexModels();
6471
+ return await detectCodexModelsFromAppServer() ?? detectCodexModels(resolveCodexHomeRootFromEnv());
6130
6472
  }
6131
6473
  };
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");
6474
+ function asNonEmptyString(value) {
6475
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
6476
+ }
6477
+ function modelListPage(result) {
6478
+ if (!result || typeof result !== "object") return null;
6479
+ const object = result;
6480
+ const entries = Array.isArray(object.data) ? object.data : Array.isArray(object.models) ? object.models : null;
6481
+ if (!entries) return null;
6482
+ return {
6483
+ entries,
6484
+ nextCursor: asNonEmptyString(object.nextCursor)
6485
+ };
6486
+ }
6487
+ function modelListRequestParams(cursor) {
6488
+ return cursor ? { cursor } : {};
6489
+ }
6490
+ function stringArray(values, read) {
6491
+ const result = [];
6492
+ const seen = /* @__PURE__ */ new Set();
6493
+ for (const value of values) {
6494
+ const item = read(value);
6495
+ if (!item || seen.has(item)) continue;
6496
+ seen.add(item);
6497
+ result.push(item);
6498
+ }
6499
+ return result;
6500
+ }
6501
+ function codexReasoningEfforts(value) {
6502
+ if (!Array.isArray(value)) return [];
6503
+ return stringArray(value, (entry) => {
6504
+ if (typeof entry === "string") return asNonEmptyString(entry);
6505
+ if (!entry || typeof entry !== "object") return null;
6506
+ const object = entry;
6507
+ return asNonEmptyString(object.reasoningEffort) ?? asNonEmptyString(object.id);
6508
+ });
6509
+ }
6510
+ function codexServiceTierIds(value) {
6511
+ if (!Array.isArray(value)) return [];
6512
+ return stringArray(value, (entry) => {
6513
+ if (typeof entry === "string") return asNonEmptyString(entry);
6514
+ if (!entry || typeof entry !== "object") return null;
6515
+ const object = entry;
6516
+ return asNonEmptyString(object.id) ?? asNonEmptyString(object.serviceTier);
6517
+ });
6518
+ }
6519
+ function codexModelInfoFromAppServer(entry) {
6520
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
6521
+ const object = entry;
6522
+ if (object.hidden === true) return null;
6523
+ const id = asNonEmptyString(object.id) ?? asNonEmptyString(object.model) ?? asNonEmptyString(object.slug);
6524
+ if (!id) return null;
6525
+ const label = asNonEmptyString(object.displayName) ?? asNonEmptyString(object.display_name) ?? asNonEmptyString(object.label) ?? id;
6526
+ const supportedReasoningEfforts = codexReasoningEfforts(object.supportedReasoningEfforts);
6527
+ const serviceTiers = codexServiceTierIds(object.serviceTiers);
6528
+ const additionalSpeedTiers = codexServiceTierIds(object.additionalSpeedTiers);
6529
+ const runtimeServiceTiers = serviceTiers.length > 0 ? serviceTiers : additionalSpeedTiers;
6530
+ const defaultReasoningEffort = asNonEmptyString(object.defaultReasoningEffort);
6531
+ const defaultServiceTier = asNonEmptyString(object.defaultServiceTier);
6532
+ return {
6533
+ id,
6534
+ label,
6535
+ verified: "launchable",
6536
+ ...supportedReasoningEfforts.length > 0 ? { supportedReasoningEfforts } : {},
6537
+ ...defaultReasoningEffort ? { defaultReasoningEffort } : {},
6538
+ ...runtimeServiceTiers.length > 0 ? { serviceTiers: runtimeServiceTiers } : {},
6539
+ ...defaultServiceTier ? { defaultServiceTier } : {}
6540
+ };
6541
+ }
6542
+ function codexModelSetFromAppServerEntries(entries) {
6543
+ const models = [];
6544
+ let defaultModel;
6545
+ for (const entry of entries) {
6546
+ const model = codexModelInfoFromAppServer(entry);
6547
+ if (!model) continue;
6548
+ models.push(model);
6549
+ if (!defaultModel && typeof entry === "object" && entry && entry.isDefault === true) {
6550
+ defaultModel = model.id;
6551
+ }
6552
+ }
6553
+ return models.length > 0 ? { models, default: defaultModel } : null;
6554
+ }
6555
+ async function detectCodexModelsFromAppServer(options = {}) {
6556
+ const env = options.env ?? process.env;
6557
+ let launch;
6558
+ try {
6559
+ launch = resolveCodexSpawn(["app-server", "--listen", "stdio://"], { env });
6560
+ } catch {
6561
+ return null;
6562
+ }
6563
+ return await new Promise((resolve) => {
6564
+ const timeoutMs = options.timeoutMs ?? 5e3;
6565
+ const proc = spawn2(launch.command, launch.args, {
6566
+ cwd: options.cwd ?? process.cwd(),
6567
+ stdio: ["pipe", "pipe", "ignore"],
6568
+ env,
6569
+ shell: launch.shell
6570
+ });
6571
+ let settled = false;
6572
+ let buffer = "";
6573
+ let requestId = 0;
6574
+ let initializeRequestId = null;
6575
+ let modelListRequestId = null;
6576
+ let pageCount = 0;
6577
+ const entries = [];
6578
+ const finish = (result) => {
6579
+ if (settled) return;
6580
+ settled = true;
6581
+ clearTimeout(timer);
6582
+ proc.kill();
6583
+ resolve(result);
6584
+ };
6585
+ const timer = setTimeout(() => finish(null), timeoutMs);
6586
+ const sendRequest = (method, params) => {
6587
+ requestId += 1;
6588
+ const id = requestId;
6589
+ proc.stdin?.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n");
6590
+ return id;
6591
+ };
6592
+ const sendNotification = (method, params) => {
6593
+ proc.stdin?.write(JSON.stringify({ jsonrpc: "2.0", method, params }) + "\n");
6594
+ };
6595
+ const requestModelPage = (cursor) => {
6596
+ pageCount += 1;
6597
+ modelListRequestId = sendRequest("model/list", modelListRequestParams(cursor));
6598
+ };
6599
+ proc.once("error", () => finish(null));
6600
+ proc.once("exit", () => finish(null));
6601
+ proc.stdout?.on("data", (chunk) => {
6602
+ if (settled) return;
6603
+ buffer += chunk.toString();
6604
+ for (; ; ) {
6605
+ const newline = buffer.indexOf("\n");
6606
+ if (newline === -1) break;
6607
+ const line = buffer.slice(0, newline).trim();
6608
+ buffer = buffer.slice(newline + 1);
6609
+ if (!line) continue;
6610
+ const message = parseCodexJsonRpcLine(line);
6611
+ if (!message || !isJsonRpcResponse(message)) continue;
6612
+ if (message.id === initializeRequestId) {
6613
+ if (!hasJsonRpcField(message, "result") || !isCompatibleInitializeResult(message.result)) {
6614
+ finish(null);
6615
+ return;
6616
+ }
6617
+ sendNotification("initialized", {});
6618
+ requestModelPage(null);
6619
+ continue;
6620
+ }
6621
+ if (message.id === modelListRequestId) {
6622
+ if (!hasJsonRpcField(message, "result")) {
6623
+ finish(null);
6624
+ return;
6625
+ }
6626
+ const page = modelListPage(message.result);
6627
+ if (!page) {
6628
+ finish(null);
6629
+ return;
6630
+ }
6631
+ entries.push(...page.entries);
6632
+ if (page.nextCursor && pageCount < 5) {
6633
+ requestModelPage(page.nextCursor);
6634
+ continue;
6635
+ }
6636
+ finish(codexModelSetFromAppServerEntries(entries));
6637
+ return;
6638
+ }
6639
+ }
6640
+ });
6641
+ initializeRequestId = sendRequest("initialize", {
6642
+ clientInfo: { name: "slock-daemon", version: "1.0.0" },
6643
+ capabilities: { experimentalApi: true }
6644
+ });
6645
+ });
6646
+ }
6647
+ function detectCodexModels(home = resolveCodexHomeRootFromEnv()) {
6648
+ let cachePath = null;
6649
+ let configPath = null;
6650
+ for (const root of codexStateRootCandidates(home)) {
6651
+ const candidate = path7.join(root, "models_cache.json");
6652
+ if (existsSync5(candidate)) {
6653
+ cachePath = candidate;
6654
+ configPath = path7.join(root, "config.toml");
6655
+ break;
6656
+ }
6657
+ }
6658
+ if (!cachePath || !configPath) return null;
6135
6659
  let models = [];
6136
6660
  try {
6137
6661
  const raw = readFileSync2(cachePath, "utf8");
@@ -6601,7 +7125,7 @@ function runCursorModelsCommand() {
6601
7125
  // src/drivers/gemini.ts
6602
7126
  import { execFileSync as execFileSync3, spawn as spawn6 } from "child_process";
6603
7127
  import { existsSync as existsSync6 } from "fs";
6604
- import path7 from "path";
7128
+ import path8 from "path";
6605
7129
  async function buildGeminiSpawnEnv(ctx, platform = process.platform) {
6606
7130
  const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
6607
7131
  const launchEnvVars = runtimeConfigToLaunchFields(ctx.config).envVars;
@@ -6645,7 +7169,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
6645
7169
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
6646
7170
  const existsSyncFn = deps.existsSyncFn ?? existsSync6;
6647
7171
  const env = deps.env ?? process.env;
6648
- const winPath = path7.win32;
7172
+ const winPath = path8.win32;
6649
7173
  let geminiEntry = null;
6650
7174
  try {
6651
7175
  const globalRoot = normalizeExecOutput2(execFileSyncFn("npm", ["root", "-g"], {
@@ -6782,8 +7306,8 @@ var GeminiDriver = class {
6782
7306
  import { randomUUID as randomUUID2 } from "crypto";
6783
7307
  import { spawn as spawn7 } from "child_process";
6784
7308
  import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
6785
- import os4 from "os";
6786
- import path8 from "path";
7309
+ import os5 from "os";
7310
+ import path9 from "path";
6787
7311
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
6788
7312
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
6789
7313
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
@@ -6830,8 +7354,8 @@ var KimiDriver = class {
6830
7354
  this.sessionId = ctx.config.sessionId || randomUUID2();
6831
7355
  this.sessionAnnounced = false;
6832
7356
  this.promptRequestId = randomUUID2();
6833
- const systemPromptPath = path8.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
6834
- const agentFilePath = path8.join(ctx.workingDirectory, KIMI_AGENT_FILE);
7357
+ const systemPromptPath = path9.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
7358
+ const agentFilePath = path9.join(ctx.workingDirectory, KIMI_AGENT_FILE);
6835
7359
  if (!isResume || !existsSync7(systemPromptPath)) {
6836
7360
  writeFileSync3(systemPromptPath, ctx.prompt, "utf8");
6837
7361
  }
@@ -6977,8 +7501,8 @@ var KimiDriver = class {
6977
7501
  return detectKimiModels();
6978
7502
  }
6979
7503
  };
6980
- function detectKimiModels(home = os4.homedir()) {
6981
- const configPath = path8.join(home, ".kimi", "config.toml");
7504
+ function detectKimiModels(home = os5.homedir()) {
7505
+ const configPath = path9.join(home, ".kimi", "config.toml");
6982
7506
  let raw;
6983
7507
  try {
6984
7508
  raw = readFileSync3(configPath, "utf8");
@@ -7006,8 +7530,8 @@ function detectKimiModels(home = os4.homedir()) {
7006
7530
  // src/drivers/kimi-sdk.ts
7007
7531
  import { randomUUID as randomUUID3 } from "crypto";
7008
7532
  import { EventEmitter } from "events";
7009
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync4 } from "fs";
7010
- import path9 from "path";
7533
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync4 } from "fs";
7534
+ import path10 from "path";
7011
7535
  import { createRequire as createRequire2 } from "module";
7012
7536
  import {
7013
7537
  createKimiHarness,
@@ -7032,7 +7556,7 @@ function createKimiSdkEventMappingState(sessionId = null) {
7032
7556
  };
7033
7557
  }
7034
7558
  function buildKimiSessionDir(workingDirectory) {
7035
- return path9.join(workingDirectory, KIMI_SESSION_DIR);
7559
+ return path10.join(workingDirectory, KIMI_SESSION_DIR);
7036
7560
  }
7037
7561
  function kimiErrorMessage(error) {
7038
7562
  if (typeof error === "string" && error.trim()) return error.trim();
@@ -7155,12 +7679,12 @@ var KIMI_SDK_RUNTIME_SESSION_DESCRIPTOR = {
7155
7679
  };
7156
7680
  async function createKimiAgentSessionForContext(ctx, sessionId) {
7157
7681
  const sessionDir = buildKimiSessionDir(ctx.workingDirectory);
7158
- mkdirSync3(sessionDir, { recursive: true });
7682
+ mkdirSync4(sessionDir, { recursive: true });
7159
7683
  const cliTransport = await prepareCliTransport(ctx, { NO_COLOR: "1" });
7160
7684
  const spawnEnv = cliTransport.spawnEnv;
7161
7685
  const wrapperPath = cliTransport.wrapperPath;
7162
- const homeDir = spawnEnv.KIMI_HOME || (process.env.HOME ? path9.join(process.env.HOME, ".kimi") : path9.join(ctx.workingDirectory, ".kimi"));
7163
- mkdirSync3(homeDir, { recursive: true });
7686
+ const homeDir = spawnEnv.KIMI_HOME || (process.env.HOME ? path10.join(process.env.HOME, ".kimi") : path10.join(ctx.workingDirectory, ".kimi"));
7687
+ mkdirSync4(homeDir, { recursive: true });
7164
7688
  const harness = createKimiHarness({
7165
7689
  homeDir,
7166
7690
  identity: {
@@ -7384,7 +7908,7 @@ So instead of \`raft message send ...\`, run \`${this.wrapperPath} message send
7384
7908
  };
7385
7909
  function detectKimiSdkModels(home = resolveKimiHome(), ctx = {}) {
7386
7910
  const span = ctx.span;
7387
- const configPath = path9.join(home, "config.toml");
7911
+ const configPath = path10.join(home, "config.toml");
7388
7912
  const homeFromEnv = Boolean(process.env.KIMI_CODE_HOME);
7389
7913
  const emit2 = (outcome, extra = {}) => {
7390
7914
  span?.addEvent("daemon.kimi_sdk.models.config", {
@@ -7492,8 +8016,8 @@ var KimiSdkDriver = class {
7492
8016
  // src/drivers/opencode.ts
7493
8017
  import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
7494
8018
  import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
7495
- import os5 from "os";
7496
- import path10 from "path";
8019
+ import os6 from "os";
8020
+ import path11 from "path";
7497
8021
  var SLOCK_AGENT_NAME = "slock";
7498
8022
  var NO_MESSAGE_PROMPT = "No new messages are pending. Stop now.";
7499
8023
  var FIRST_MESSAGE_TASK_PREFIX = "First message task (system-triggered):";
@@ -7521,8 +8045,8 @@ function parseUserOpenCodeConfig(ctx) {
7521
8045
  const raw = runtimeConfigToLaunchFields(ctx.config).envVars?.OPENCODE_CONFIG_CONTENT;
7522
8046
  return parseOpenCodeConfigContent(raw);
7523
8047
  }
7524
- function readLocalOpenCodeConfig(home = os5.homedir()) {
7525
- const configPath = path10.join(home, ".config", "opencode", "opencode.json");
8048
+ function readLocalOpenCodeConfig(home = os6.homedir()) {
8049
+ const configPath = path11.join(home, ".config", "opencode", "opencode.json");
7526
8050
  try {
7527
8051
  return parseOpenCodeConfigContent(readFileSync5(configPath, "utf8"));
7528
8052
  } catch {
@@ -7582,7 +8106,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
7582
8106
  }
7583
8107
  };
7584
8108
  }
7585
- function buildOpenCodeConfig(ctx, home = os5.homedir()) {
8109
+ function buildOpenCodeConfig(ctx, home = os6.homedir()) {
7586
8110
  const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
7587
8111
  const userAgents = recordField(userConfig.agent);
7588
8112
  const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
@@ -7600,7 +8124,7 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
7600
8124
  mcp: recordField(userConfig.mcp)
7601
8125
  };
7602
8126
  }
7603
- async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = null) {
8127
+ async function buildOpenCodeLaunchOptions(ctx, home = os6.homedir(), version = null) {
7604
8128
  const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
7605
8129
  const config = buildOpenCodeConfig(ctx, home);
7606
8130
  const env = {
@@ -7699,7 +8223,7 @@ function formatOpenCodeLabelToken(token) {
7699
8223
  if (/^\d/.test(token)) return token;
7700
8224
  return normalized.charAt(0).toUpperCase() + normalized.slice(1);
7701
8225
  }
7702
- function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeModelsCommand) {
8226
+ function detectOpenCodeModels(home = os6.homedir(), runCommand = runOpenCodeModelsCommand) {
7703
8227
  const commandResult = runCommand(home);
7704
8228
  if (commandResult.error || commandResult.status !== 0) return null;
7705
8229
  return parseOpenCodeModelsOutput(commandResult.stdout);
@@ -7720,11 +8244,11 @@ function runOpenCodeModelsCommand(home, deps = {}) {
7720
8244
  };
7721
8245
  }
7722
8246
  function isWindowsCommandShim(commandPath) {
7723
- const ext = path10.win32.extname(commandPath).toLowerCase();
8247
+ const ext = path11.win32.extname(commandPath).toLowerCase();
7724
8248
  return ext === ".cmd" || ext === ".bat";
7725
8249
  }
7726
8250
  function opencodePackageEntryCandidates(packageRoot) {
7727
- const winPath = path10.win32;
8251
+ const winPath = path11.win32;
7728
8252
  return [
7729
8253
  winPath.join(packageRoot, "bin", "opencode.exe"),
7730
8254
  winPath.join(packageRoot, "bin", "opencode.js"),
@@ -7733,7 +8257,7 @@ function opencodePackageEntryCandidates(packageRoot) {
7733
8257
  ];
7734
8258
  }
7735
8259
  function openCodeSpecForEntry(entry, commandArgs) {
7736
- if (path10.win32.extname(entry).toLowerCase() === ".exe") {
8260
+ if (path11.win32.extname(entry).toLowerCase() === ".exe") {
7737
8261
  return { command: entry, args: commandArgs, shell: false };
7738
8262
  }
7739
8263
  return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
@@ -7742,7 +8266,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
7742
8266
  const existsSyncFn = deps.existsSyncFn ?? existsSync8;
7743
8267
  const execFileSyncFn = deps.execFileSyncFn;
7744
8268
  const env = deps.env ?? process.env;
7745
- const winPath = path10.win32;
8269
+ const winPath = path11.win32;
7746
8270
  const candidates = [];
7747
8271
  if (execFileSyncFn) {
7748
8272
  try {
@@ -7770,7 +8294,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
7770
8294
  function extractWindowsShimTargets(commandPath, deps = {}) {
7771
8295
  if (!isWindowsCommandShim(commandPath)) return [];
7772
8296
  const readFileSyncFn = deps.readFileSyncFn ?? readFileSync5;
7773
- const commandDir = path10.win32.dirname(commandPath);
8297
+ const commandDir = path11.win32.dirname(commandPath);
7774
8298
  let raw;
7775
8299
  try {
7776
8300
  raw = String(readFileSyncFn(commandPath, "utf8"));
@@ -7781,7 +8305,7 @@ function extractWindowsShimTargets(commandPath, deps = {}) {
7781
8305
  const dp0Pattern = /%~dp0\\?([^"\r\n]*?opencode\.(?:exe|js|mjs|cjs))/gi;
7782
8306
  for (const match of raw.matchAll(dp0Pattern)) {
7783
8307
  const relative = match[1]?.replace(/^\\+/, "");
7784
- if (relative) candidates.push(path10.win32.normalize(path10.win32.join(commandDir, relative)));
8308
+ if (relative) candidates.push(path11.win32.normalize(path11.win32.join(commandDir, relative)));
7785
8309
  }
7786
8310
  return candidates;
7787
8311
  }
@@ -7795,7 +8319,7 @@ function resolveOpenCodeSpawn(commandArgs, deps = {}) {
7795
8319
  };
7796
8320
  }
7797
8321
  const command = resolveCommandOnPath("opencode", deps);
7798
- if (command && path10.win32.extname(command).toLowerCase() === ".exe") {
8322
+ if (command && path11.win32.extname(command).toLowerCase() === ".exe") {
7799
8323
  return { command, args: commandArgs, shell: false };
7800
8324
  }
7801
8325
  const packageEntry = resolveWindowsOpenCodePackageEntry(command, deps);
@@ -7900,7 +8424,7 @@ var OpenCodeDriver = class {
7900
8424
  if (unsupportedMessage) {
7901
8425
  throw new Error(unsupportedMessage);
7902
8426
  }
7903
- const launch = await buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
8427
+ const launch = await buildOpenCodeLaunchOptions(ctx, os6.homedir(), version);
7904
8428
  const spawnSpec = resolveOpenCodeSpawn(launch.args);
7905
8429
  const proc = spawn8(spawnSpec.command, spawnSpec.args, {
7906
8430
  cwd: ctx.workingDirectory,
@@ -7967,8 +8491,8 @@ var OpenCodeDriver = class {
7967
8491
  // src/drivers/pi.ts
7968
8492
  import { randomUUID as randomUUID4 } from "crypto";
7969
8493
  import { EventEmitter as EventEmitter2 } from "events";
7970
- import { mkdirSync as mkdirSync4, readdirSync as readdirSync2 } from "fs";
7971
- import path11 from "path";
8494
+ import { mkdirSync as mkdirSync5, readdirSync as readdirSync2 } from "fs";
8495
+ import path12 from "path";
7972
8496
  import {
7973
8497
  AuthStorage,
7974
8498
  createBashTool,
@@ -7995,7 +8519,7 @@ function createPiSdkEventMappingState(sessionId = null) {
7995
8519
  };
7996
8520
  }
7997
8521
  function buildPiSessionDir(workingDirectory) {
7998
- return path11.join(workingDirectory, PI_SESSION_DIR);
8522
+ return path12.join(workingDirectory, PI_SESSION_DIR);
7999
8523
  }
8000
8524
  async function buildPiSpawnEnv(ctx) {
8001
8525
  return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
@@ -8017,7 +8541,7 @@ function findPiSessionFile(sessionDir, sessionId) {
8017
8541
  }
8018
8542
  const suffix = `_${sessionId}.jsonl`;
8019
8543
  const match = entries.find((entry) => entry.endsWith(suffix));
8020
- return match ? path11.join(sessionDir, match) : null;
8544
+ return match ? path12.join(sessionDir, match) : null;
8021
8545
  }
8022
8546
  function detectPiModelsFromRegistry(modelRegistry) {
8023
8547
  const models = [];
@@ -8235,7 +8759,7 @@ var PI_IDLE_PROMPT_RETRY_MS = 25;
8235
8759
  var PI_IDLE_PROMPT_MAX_WAIT_MS = 1e3;
8236
8760
  async function createPiAgentSessionForContext(ctx, sessionId) {
8237
8761
  const sessionDir = buildPiSessionDir(ctx.workingDirectory);
8238
- mkdirSync4(sessionDir, { recursive: true });
8762
+ mkdirSync5(sessionDir, { recursive: true });
8239
8763
  const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
8240
8764
  const requestedModel = launchRuntimeFields.model || "default";
8241
8765
  const traceSpan = ctx.tracer?.startSpan("daemon.pi.session.create", {
@@ -8253,7 +8777,7 @@ async function createPiAgentSessionForContext(ctx, sessionId) {
8253
8777
  try {
8254
8778
  const spawnEnv = await buildPiSpawnEnv(ctx);
8255
8779
  const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
8256
- const authStorage = AuthStorage.create(path11.join(agentDir, "auth.json"));
8780
+ const authStorage = AuthStorage.create(path12.join(agentDir, "auth.json"));
8257
8781
  const settingsManager = SettingsManager.create(ctx.workingDirectory, agentDir);
8258
8782
  const services = await createAgentSessionServices({
8259
8783
  cwd: ctx.workingDirectory,
@@ -8652,6 +9176,9 @@ var ChildProcessRuntimeSession = class {
8652
9176
  get currentSessionId() {
8653
9177
  return this.driver.currentSessionId;
8654
9178
  }
9179
+ get currentRuntimeHomeDir() {
9180
+ return this.driver.currentRuntimeHomeDir;
9181
+ }
8655
9182
  get exitCode() {
8656
9183
  return this.process?.exitCode ?? null;
8657
9184
  }
@@ -8775,7 +9302,7 @@ function getDriver(runtimeId) {
8775
9302
 
8776
9303
  // src/workspaces.ts
8777
9304
  import { readdir, rm, stat } from "fs/promises";
8778
- import path12 from "path";
9305
+ import path13 from "path";
8779
9306
  function isValidWorkspaceDirectoryName(directoryName) {
8780
9307
  return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
8781
9308
  }
@@ -8783,7 +9310,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
8783
9310
  if (!isValidWorkspaceDirectoryName(directoryName)) {
8784
9311
  return null;
8785
9312
  }
8786
- return path12.join(dataDir, directoryName);
9313
+ return path13.join(dataDir, directoryName);
8787
9314
  }
8788
9315
  function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
8789
9316
  return {
@@ -8832,7 +9359,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
8832
9359
  return summary;
8833
9360
  }
8834
9361
  const childSummaries = await Promise.all(
8835
- entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
9362
+ entries.map((entry) => summarizeWorkspaceEntry(path13.join(dirPath, entry.name), entry))
8836
9363
  );
8837
9364
  for (const childSummary of childSummaries) {
8838
9365
  summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
@@ -8851,7 +9378,7 @@ async function scanWorkspaceDirectories(dataDir) {
8851
9378
  if (!entry.isDirectory()) {
8852
9379
  return null;
8853
9380
  }
8854
- const dirPath = path12.join(dataDir, entry.name);
9381
+ const dirPath = path13.join(dataDir, entry.name);
8855
9382
  try {
8856
9383
  const summary = await summarizeWorkspaceDirectory(dirPath);
8857
9384
  return {
@@ -9400,17 +9927,17 @@ function allowedTranscriptRootsForRuntime(runtime, homeDir, workspaceDir) {
9400
9927
  const roots = [workspaceDir];
9401
9928
  switch (runtime) {
9402
9929
  case "claude":
9403
- roots.push(path13.join(homeDir, ".claude"));
9930
+ roots.push(path14.join(homeDir, ".claude"));
9404
9931
  break;
9405
9932
  case "codex":
9406
- roots.push(path13.join(homeDir, ".codex"));
9933
+ roots.push(homeDir, path14.join(homeDir, ".codex"));
9407
9934
  break;
9408
9935
  case "kimi":
9409
9936
  case "kimi-sdk":
9410
- roots.push(path13.join(homeDir, ".kimi"));
9937
+ roots.push(path14.join(homeDir, ".kimi"));
9411
9938
  break;
9412
9939
  case "pi":
9413
- roots.push(path13.join(homeDir, ".pi"), path13.join(homeDir, ".pi", "agent"));
9940
+ roots.push(path14.join(homeDir, ".pi"), path14.join(homeDir, ".pi", "agent"));
9414
9941
  break;
9415
9942
  }
9416
9943
  return roots;
@@ -9421,8 +9948,8 @@ async function isPathWithinAllowedRoots(filePath, roots) {
9421
9948
  for (const root of roots) {
9422
9949
  const realRoot = await realpath(root).catch(() => null);
9423
9950
  if (!realRoot) continue;
9424
- const rel = path13.relative(realRoot, real);
9425
- if (!rel.startsWith("..") && !path13.isAbsolute(rel)) return true;
9951
+ const rel = path14.relative(realRoot, real);
9952
+ if (!rel.startsWith("..") && !path14.isAbsolute(rel)) return true;
9426
9953
  }
9427
9954
  return false;
9428
9955
  }
@@ -9469,12 +9996,12 @@ function findSessionJsonl(root, predicate) {
9469
9996
  for (const entry of entries) {
9470
9997
  if (++visited > maxEntries) return null;
9471
9998
  if (!entry.isFile() || !predicate(entry.name)) continue;
9472
- return path13.join(dir, entry.name);
9999
+ return path14.join(dir, entry.name);
9473
10000
  }
9474
10001
  for (const entry of entries) {
9475
10002
  if (++visited > maxEntries) return null;
9476
10003
  if (!entry.isDirectory()) continue;
9477
- const found = visit(path13.join(dir, entry.name), depth - 1);
10004
+ const found = visit(path14.join(dir, entry.name), depth - 1);
9478
10005
  if (found) return found;
9479
10006
  }
9480
10007
  return null;
@@ -9482,7 +10009,7 @@ function findSessionJsonl(root, predicate) {
9482
10009
  return visit(root, maxDepth);
9483
10010
  }
9484
10011
  function findKimiSdkSessionDir(sessionId, agentId, homeDir) {
9485
- const indexPath = path13.join(homeDir, ".kimi", "session_index.jsonl");
10012
+ const indexPath = path14.join(homeDir, ".kimi", "session_index.jsonl");
9486
10013
  try {
9487
10014
  const index = readFileSync6(indexPath, "utf8");
9488
10015
  for (const line of index.split("\n")) {
@@ -9497,12 +10024,12 @@ function findKimiSdkSessionDir(sessionId, agentId, homeDir) {
9497
10024
  }
9498
10025
  } catch {
9499
10026
  }
9500
- const sessionsRoot = path13.join(homeDir, ".kimi", "sessions");
10027
+ const sessionsRoot = path14.join(homeDir, ".kimi", "sessions");
9501
10028
  try {
9502
10029
  const prefix = agentId ? `wd_${agentId}_` : `wd_`;
9503
10030
  for (const entry of readdirSync3(sessionsRoot, { withFileTypes: true })) {
9504
10031
  if (!entry.isDirectory() || !entry.name.startsWith(prefix)) continue;
9505
- const candidate = path13.join(sessionsRoot, entry.name, `session_${sessionId}`);
10032
+ const candidate = path14.join(sessionsRoot, entry.name, `session_${sessionId}`);
9506
10033
  if (existsSync9(candidate)) return candidate;
9507
10034
  }
9508
10035
  } catch {
@@ -9511,16 +10038,16 @@ function findKimiSdkSessionDir(sessionId, agentId, homeDir) {
9511
10038
  }
9512
10039
  function findPiSessionFile2(sessionId, workingDirectory, homeDir) {
9513
10040
  if (workingDirectory) {
9514
- const piSessionsDir = path13.join(workingDirectory, ".pi-sessions");
10041
+ const piSessionsDir = path14.join(workingDirectory, ".pi-sessions");
9515
10042
  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);
10043
+ 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
10044
  if (files[0]) return files[0].path;
9518
10045
  } catch {
9519
10046
  }
9520
10047
  }
9521
10048
  const legacyRoots = [
9522
- path13.join(homeDir, ".pi", "agent"),
9523
- path13.join(homeDir, ".pi")
10049
+ path14.join(homeDir, ".pi", "agent"),
10050
+ path14.join(homeDir, ".pi")
9524
10051
  ];
9525
10052
  for (const root of legacyRoots) {
9526
10053
  const found = findSessionJsonl(root, (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
@@ -9534,9 +10061,9 @@ function safeSessionFilename(value) {
9534
10061
  }
9535
10062
  function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
9536
10063
  try {
9537
- const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
9538
- mkdirSync5(dir, { recursive: true });
9539
- const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
10064
+ const dir = path14.join(fallbackDir, ".slock", "runtime-sessions");
10065
+ mkdirSync6(dir, { recursive: true });
10066
+ const filePath = path14.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
9540
10067
  writeFileSync4(filePath, JSON.stringify({
9541
10068
  type: "runtime_session_handoff",
9542
10069
  runtime,
@@ -9555,27 +10082,40 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
9555
10082
  return null;
9556
10083
  }
9557
10084
  }
9558
- function resolveRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
10085
+ function resolveRuntimeHomeDir(config, defaultHomeDir, workspacePath, opts = {}) {
9559
10086
  if (isClaudeCustomProviderConfig(config)) {
9560
10087
  return getClaudeProviderStatePaths(workspacePath).home;
9561
10088
  }
10089
+ if (config.runtime === "codex") {
10090
+ return resolveCodexHomeRootFromConfig(config, defaultHomeDir, workspacePath, process.env, opts);
10091
+ }
9562
10092
  return defaultHomeDir;
9563
10093
  }
9564
- function ensureRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
10094
+ function ensureRuntimeHomeDir(config, defaultHomeDir, workspacePath, opts = {}) {
9565
10095
  if (isClaudeCustomProviderConfig(config)) {
9566
10096
  return ensureClaudeProviderStatePaths(workspacePath).home;
9567
10097
  }
10098
+ if (config.runtime === "codex") {
10099
+ const home = resolveCodexHomeRootFromConfig(config, defaultHomeDir, workspacePath, process.env, opts);
10100
+ if (opts.agentId) {
10101
+ mkdirSync6(home, { recursive: true });
10102
+ }
10103
+ return home;
10104
+ }
9568
10105
  return defaultHomeDir;
9569
10106
  }
9570
- function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir, opts) {
10107
+ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os7.homedir(), fallbackDir, opts) {
9571
10108
  let resolvedPath = null;
9572
10109
  let lookupMethod = "none";
9573
10110
  if (runtime === "claude") {
9574
10111
  lookupMethod = "claude_jsonl";
9575
- resolvedPath = findSessionJsonl(path13.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`);
10112
+ resolvedPath = findSessionJsonl(path14.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`);
9576
10113
  } else if (runtime === "codex") {
9577
10114
  lookupMethod = "codex_jsonl";
9578
- resolvedPath = findSessionJsonl(path13.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
10115
+ for (const root of codexSessionRootCandidates(homeDir)) {
10116
+ resolvedPath = findSessionJsonl(root, (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
10117
+ if (resolvedPath) break;
10118
+ }
9579
10119
  } else if (runtime === "kimi-sdk" || runtime === "kimi") {
9580
10120
  lookupMethod = "kimi_sdk_index";
9581
10121
  resolvedPath = findKimiSdkSessionDir(sessionId, opts?.agentId, homeDir);
@@ -10312,6 +10852,28 @@ function runtimeDiagnosticTraceAttrs(event) {
10312
10852
  session_id_present: Boolean(event.sessionId)
10313
10853
  };
10314
10854
  }
10855
+ function runtimeRecoveryTrajectoryEntry(event) {
10856
+ const lines = [event.message];
10857
+ if (event.details && event.details !== event.message) {
10858
+ lines.push(event.details);
10859
+ }
10860
+ return {
10861
+ kind: "system",
10862
+ title: "Codex resume recovery",
10863
+ text: lines.join("\n")
10864
+ };
10865
+ }
10866
+ function runtimeRecoveryTraceAttrs(event) {
10867
+ return {
10868
+ kind: event.kind,
10869
+ source: event.source,
10870
+ resume_error_class: event.resumeErrorClass,
10871
+ recovery_action: event.recoveryAction,
10872
+ requested_session_id_present: Boolean(event.requestedSessionId),
10873
+ message_present: Boolean(event.message),
10874
+ details_present: Boolean(event.details)
10875
+ };
10876
+ }
10315
10877
  function currentErrorCandidates(ap) {
10316
10878
  return [
10317
10879
  ap.runtimeErrorSinceProgress ? ap.lastRuntimeError : null,
@@ -10635,6 +11197,7 @@ var AgentProcessManager = class _AgentProcessManager {
10635
11197
  sendToServer;
10636
11198
  daemonApiKey;
10637
11199
  serverUrl;
11200
+ slockHome;
10638
11201
  dataDir;
10639
11202
  runtimeSessionHomeDir;
10640
11203
  driverResolver;
@@ -10664,8 +11227,9 @@ var AgentProcessManager = class _AgentProcessManager {
10664
11227
  this.sendToServer = sendToServer;
10665
11228
  this.daemonApiKey = daemonApiKey;
10666
11229
  this.serverUrl = opts.serverUrl;
10667
- this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
10668
- this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os6.homedir();
11230
+ this.slockHome = opts.slockHome ? path14.resolve(opts.slockHome) : resolveSlockHome();
11231
+ this.dataDir = opts.dataDir || resolveSlockHomePath("agents", this.slockHome);
11232
+ this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os7.homedir();
10669
11233
  this.driverResolver = opts.driverResolver || getDriver;
10670
11234
  this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
10671
11235
  this.tracer = opts.tracer ?? noopTracer;
@@ -10794,6 +11358,70 @@ var AgentProcessManager = class _AgentProcessManager {
10794
11358
  this.sendStdinNotification(agentId);
10795
11359
  }, delayMs);
10796
11360
  }
11361
+ flushAsyncRejectedIdleDelivery(agentId) {
11362
+ const ap = this.agents.get(agentId);
11363
+ if (!ap) return false;
11364
+ const count = ap.notifications.takePendingAndClearTimer();
11365
+ if (count === 0) return false;
11366
+ if (!ap.driver.supportsStdinNotification || !ap.sessionId || ap.inbox.length === 0) {
11367
+ ap.notifications.add(count);
11368
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
11369
+ agentId,
11370
+ runtime: ap.config.runtime,
11371
+ model: ap.config.model,
11372
+ launchId: ap.launchId || void 0,
11373
+ mode: "idle",
11374
+ outcome: "queued_without_stdin",
11375
+ inbox_count: ap.inbox.length,
11376
+ pending_notification_count: ap.notifications.pendingCount,
11377
+ session_id_present: Boolean(ap.sessionId),
11378
+ supports_stdin_notification: ap.driver.supportsStdinNotification
11379
+ });
11380
+ return false;
11381
+ }
11382
+ if (!this.isApmIdle(ap)) {
11383
+ ap.notifications.add(count);
11384
+ return this.sendStdinNotification(agentId, { forceUnsupportedRetry: true });
11385
+ }
11386
+ ap.notifications.pruneContributedToPending(ap.inbox, ap.sessionId);
11387
+ const messages = ap.notifications.filterUncontributedMessages(ap.inbox, ap.sessionId);
11388
+ if (messages.length === 0) {
11389
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
11390
+ agentId,
11391
+ runtime: ap.config.runtime,
11392
+ model: ap.config.model,
11393
+ launchId: ap.launchId || void 0,
11394
+ mode: "idle",
11395
+ outcome: "suppressed_already_contributed",
11396
+ inbox_count: ap.inbox.length,
11397
+ pending_notification_count: count,
11398
+ session_id_present: true
11399
+ });
11400
+ return false;
11401
+ }
11402
+ this.commitApmIdleState(agentId, ap, false);
11403
+ this.startRuntimeTrace(agentId, ap, "async-rejected-idle-delivery-retry", messages);
11404
+ this.broadcastActivity(agentId, "working", "Message received");
11405
+ const accepted = this.deliverInboxUpdateViaStdin(
11406
+ agentId,
11407
+ ap,
11408
+ messages,
11409
+ "idle",
11410
+ "async_rejected_idle_delivery_retry"
11411
+ );
11412
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
11413
+ agentId,
11414
+ runtime: ap.config.runtime,
11415
+ model: ap.config.model,
11416
+ launchId: ap.launchId || void 0,
11417
+ mode: "idle",
11418
+ outcome: accepted ? "written" : "not_written",
11419
+ inbox_count: ap.inbox.length,
11420
+ messages_count: messages.length,
11421
+ session_id_present: true
11422
+ });
11423
+ return accepted;
11424
+ }
10797
11425
  isApmIdle(ap) {
10798
11426
  return ap.gatedSteering.isIdle;
10799
11427
  }
@@ -11252,6 +11880,23 @@ var AgentProcessManager = class _AgentProcessManager {
11252
11880
  ...runtimeDiagnosticTraceAttrs(event)
11253
11881
  });
11254
11882
  }
11883
+ recordRuntimeRecoveryActivity(agentId, ap, event) {
11884
+ this.sendToServer({
11885
+ type: "agent:activity",
11886
+ agentId,
11887
+ activity: ap.lastActivity || "online",
11888
+ detail: ap.lastActivityDetail || "",
11889
+ entries: [runtimeRecoveryTrajectoryEntry(event)],
11890
+ launchId: ap.launchId || void 0,
11891
+ clientSeq: this.nextActivityClientSeq(agentId)
11892
+ });
11893
+ this.recordDaemonTrace("daemon.runtime.recovery.visible", {
11894
+ agentId,
11895
+ launchId: ap.launchId || void 0,
11896
+ runtime: ap.config.runtime,
11897
+ ...runtimeRecoveryTraceAttrs(event)
11898
+ });
11899
+ }
11255
11900
  recordDaemonTrace(name, attrs, status = "ok", parentTraceparent) {
11256
11901
  const span = this.tracer.startSpan(name, {
11257
11902
  parent: parseTraceparent(parentTraceparent),
@@ -11589,26 +12234,26 @@ var AgentProcessManager = class _AgentProcessManager {
11589
12234
  let pendingStartRebind;
11590
12235
  const originalLaunchId = launchId || null;
11591
12236
  try {
11592
- const agentDataDir = path13.join(this.dataDir, agentId);
12237
+ const agentDataDir = path14.join(this.dataDir, agentId);
11593
12238
  await mkdir(agentDataDir, { recursive: true });
11594
12239
  const initialRuntimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
11595
- const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
12240
+ const memoryMdPath = path14.join(agentDataDir, "MEMORY.md");
11596
12241
  try {
11597
12242
  await access(memoryMdPath);
11598
12243
  } catch {
11599
12244
  const initialMemoryMd = buildInitialMemoryMd(initialRuntimeConfig);
11600
12245
  await writeFile(memoryMdPath, initialMemoryMd);
11601
12246
  }
11602
- const notesDir = path13.join(agentDataDir, "notes");
12247
+ const notesDir = path14.join(agentDataDir, "notes");
11603
12248
  await mkdir(notesDir, { recursive: true });
11604
12249
  if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
11605
12250
  const seedFiles = buildOnboardingSeedFiles();
11606
12251
  for (const { relativePath, content } of seedFiles) {
11607
- const fullPath = path13.join(agentDataDir, relativePath);
12252
+ const fullPath = path14.join(agentDataDir, relativePath);
11608
12253
  try {
11609
12254
  await access(fullPath);
11610
12255
  } catch {
11611
- await mkdir(path13.dirname(fullPath), { recursive: true });
12256
+ await mkdir(path14.dirname(fullPath), { recursive: true });
11612
12257
  await writeFile(fullPath, content);
11613
12258
  }
11614
12259
  }
@@ -11762,6 +12407,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
11762
12407
  workingDirectory: agentDataDir,
11763
12408
  slockCliPath: this.slockCliPath,
11764
12409
  daemonApiKey: this.daemonApiKey,
12410
+ slockHome: this.slockHome,
11765
12411
  launchId: effectiveLaunchId,
11766
12412
  agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId),
11767
12413
  cliTransportTraceDir: this.cliTransportTraceDir,
@@ -12921,7 +13567,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12921
13567
  return true;
12922
13568
  }
12923
13569
  async resetWorkspace(agentId) {
12924
- const agentDataDir = path13.join(this.dataDir, agentId);
13570
+ const agentDataDir = path14.join(this.dataDir, agentId);
12925
13571
  try {
12926
13572
  await rm2(agentDataDir, { recursive: true, force: true });
12927
13573
  logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
@@ -12981,9 +13627,9 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12981
13627
  }
12982
13628
  return result;
12983
13629
  }
12984
- buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
12985
- const workspacePath = path13.join(this.dataDir, agentId);
12986
- const runtimeHomeDir = resolveRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspacePath);
13630
+ buildRuntimeProfileReport(agentId, config, sessionId, launchId, observedRuntimeHomeDir) {
13631
+ const workspacePath = path14.join(this.dataDir, agentId);
13632
+ const runtimeHomeDir = observedRuntimeHomeDir || resolveRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspacePath, { agentId, slockHome: this.slockHome });
12987
13633
  return {
12988
13634
  agentId,
12989
13635
  launchId,
@@ -13004,7 +13650,13 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13004
13650
  getAgentRuntimeProfileReport(agentId) {
13005
13651
  const running = this.agents.get(agentId);
13006
13652
  if (running) {
13007
- return this.buildRuntimeProfileReport(agentId, running.config, running.sessionId, running.launchId);
13653
+ return this.buildRuntimeProfileReport(
13654
+ agentId,
13655
+ running.config,
13656
+ running.sessionId,
13657
+ running.launchId,
13658
+ running.runtime.currentRuntimeHomeDir
13659
+ );
13008
13660
  }
13009
13661
  const idle = this.idleAgentConfigs.get(agentId);
13010
13662
  if (idle) {
@@ -13277,7 +13929,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13277
13929
  }
13278
13930
  // Workspace file browsing
13279
13931
  async getFileTree(agentId, dirPath) {
13280
- const agentDir = path13.join(this.dataDir, agentId);
13932
+ const agentDir = path14.join(this.dataDir, agentId);
13281
13933
  try {
13282
13934
  await stat2(agentDir);
13283
13935
  } catch {
@@ -13285,8 +13937,8 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13285
13937
  }
13286
13938
  let targetDir = agentDir;
13287
13939
  if (dirPath) {
13288
- const resolved = path13.resolve(agentDir, dirPath);
13289
- if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
13940
+ const resolved = path14.resolve(agentDir, dirPath);
13941
+ if (!resolved.startsWith(agentDir + path14.sep) && resolved !== agentDir) {
13290
13942
  return [];
13291
13943
  }
13292
13944
  targetDir = resolved;
@@ -13294,14 +13946,14 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13294
13946
  return this.listDirectoryChildren(targetDir, agentDir);
13295
13947
  }
13296
13948
  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) {
13949
+ const agentDir = path14.join(this.dataDir, agentId);
13950
+ const resolved = path14.resolve(agentDir, filePath);
13951
+ if (!resolved.startsWith(agentDir + path14.sep) && resolved !== agentDir) {
13300
13952
  throw new Error("Access denied");
13301
13953
  }
13302
13954
  const info = await stat2(resolved);
13303
13955
  if (info.isDirectory()) throw new Error("Cannot read a directory");
13304
- const ext = path13.extname(resolved).toLowerCase();
13956
+ const ext = path14.extname(resolved).toLowerCase();
13305
13957
  if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
13306
13958
  if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
13307
13959
  const content = await readFile(resolved, "utf-8");
@@ -13352,8 +14004,8 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13352
14004
  };
13353
14005
  }
13354
14006
  const runtime = config.runtime;
13355
- const workspaceDir = path13.join(this.dataDir, agentId);
13356
- const homeDir = ensureRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspaceDir);
14007
+ const workspaceDir = path14.join(this.dataDir, agentId);
14008
+ const homeDir = agent?.runtime.currentRuntimeHomeDir || ensureRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspaceDir, { agentId, slockHome: this.slockHome });
13357
14009
  if (!actualSessionId) {
13358
14010
  return {
13359
14011
  runtime,
@@ -13391,7 +14043,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13391
14043
  let redacted = false;
13392
14044
  if (ref.reachable && ref.path) {
13393
14045
  try {
13394
- const resolved = path13.resolve(ref.path);
14046
+ const resolved = path14.resolve(ref.path);
13395
14047
  const allowedRoots = allowedTranscriptRootsForRuntime(runtime, homeDir, workspaceDir);
13396
14048
  if (!await isPathWithinAllowedRoots(resolved, allowedRoots)) {
13397
14049
  throw new Error("resolved session path is outside allowed runtime directories");
@@ -13402,7 +14054,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13402
14054
  throw new Error("symbolic links are not allowed");
13403
14055
  }
13404
14056
  if (info.isDirectory()) {
13405
- targetPath = path13.join(resolved, "state.json");
14057
+ targetPath = path14.join(resolved, "state.json");
13406
14058
  }
13407
14059
  if (!await isPathWithinAllowedRoots(targetPath, allowedRoots)) {
13408
14060
  throw new Error("resolved session state path is outside allowed runtime directories");
@@ -13514,14 +14166,22 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13514
14166
  const idle = this.idleAgentConfigs.get(agentId);
13515
14167
  const config = agent?.config ?? idle?.config ?? null;
13516
14168
  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();
14169
+ const workspaceDir = path14.join(this.dataDir, agentId);
14170
+ const hostHome = os7.homedir();
14171
+ const home = agent?.runtime.currentRuntimeHomeDir || (config ? ensureRuntimeHomeDir(config, hostHome, workspaceDir, { agentId, slockHome: this.slockHome }) : runtime === "codex" ? resolveDefaultCodexHomeRootForAgent(agentId, { slockHome: this.slockHome }) : hostHome);
13519
14172
  const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
14173
+ const globalDirs = runtime === "codex" ? [
14174
+ path14.join(home, "skills"),
14175
+ path14.join(home, "skills", ".system"),
14176
+ path14.join(home, ".agents", "skills"),
14177
+ ...hasConfiguredCodexHome(config) ? [] : [path14.join(hostHome, ".agents", "skills")]
14178
+ ] : paths.global.map((p) => path14.join(home, p));
14179
+ const workspaceDirs = paths.workspace.map((p) => path14.join(workspaceDir, p));
13520
14180
  const globalResults = await Promise.all(
13521
- paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
14181
+ globalDirs.map((dir) => this.scanSkillsDir(dir))
13522
14182
  );
13523
14183
  const workspaceResults = await Promise.all(
13524
- paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
14184
+ workspaceDirs.map((dir) => this.scanSkillsDir(dir))
13525
14185
  );
13526
14186
  const dedup = (skills) => {
13527
14187
  const seen = /* @__PURE__ */ new Set();
@@ -13550,7 +14210,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13550
14210
  const skills = [];
13551
14211
  for (const entry of entries) {
13552
14212
  if (entry.isDirectory() || entry.isSymbolicLink()) {
13553
- const skillMd = path13.join(dir, entry.name, "SKILL.md");
14213
+ const skillMd = path14.join(dir, entry.name, "SKILL.md");
13554
14214
  try {
13555
14215
  const content = await readFile(skillMd, "utf-8");
13556
14216
  const skill = this.parseSkillMd(entry.name, content);
@@ -13561,7 +14221,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13561
14221
  } else if (entry.name.endsWith(".md")) {
13562
14222
  const cmdName = entry.name.replace(/\.md$/, "");
13563
14223
  try {
13564
- const content = await readFile(path13.join(dir, entry.name), "utf-8");
14224
+ const content = await readFile(path14.join(dir, entry.name), "utf-8");
13565
14225
  const skill = this.parseSkillMd(cmdName, content);
13566
14226
  skill.sourcePath = dir;
13567
14227
  skills.push(skill);
@@ -13982,6 +14642,43 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13982
14642
  recordRuntimeTraceEvent(agentId, ap, name, attrs) {
13983
14643
  this.startRuntimeTrace(agentId, ap, "runtime-progress").addEvent(name, attrs);
13984
14644
  }
14645
+ restoreRuntimeDeliveryAfterAsyncRejection(agentId, ap, event) {
14646
+ const pendingBefore = ap.notifications.pendingCount;
14647
+ const restoredMessages = ap.inbox.filter(
14648
+ (message) => ap.notifications.hasContributedMessage(message, ap.sessionId)
14649
+ );
14650
+ ap.notifications.clearNoticeFingerprint();
14651
+ const restoredNotificationCount = ap.driver.supportsStdinNotification && ap.sessionId && restoredMessages.length > 0 ? ap.notifications.add(restoredMessages.length) : 0;
14652
+ if (event.requestMethod === "turn/start") {
14653
+ this.commitApmIdleState(agentId, ap, true);
14654
+ }
14655
+ const idleRetryScheduled = event.requestMethod === "turn/start" && restoredNotificationCount > 0 ? ap.notifications.schedule(() => {
14656
+ this.flushAsyncRejectedIdleDelivery(agentId);
14657
+ }, this.stdinNotificationRetryMs) : false;
14658
+ const attrs = {
14659
+ request_method: event.requestMethod,
14660
+ source: event.source,
14661
+ payloadBytes: event.payloadBytes,
14662
+ inbox_count: ap.inbox.length,
14663
+ restored_messages_count: restoredMessages.length,
14664
+ pending_notification_count_before: pendingBefore,
14665
+ pending_notification_count_after: ap.notifications.pendingCount,
14666
+ restored_notification_count: restoredNotificationCount,
14667
+ session_id_present: Boolean(ap.sessionId),
14668
+ supports_stdin_notification: ap.driver.supportsStdinNotification,
14669
+ busy_delivery_mode: ap.driver.busyDeliveryMode,
14670
+ restored_idle_state: event.requestMethod === "turn/start",
14671
+ idle_retry_scheduled: idleRetryScheduled
14672
+ };
14673
+ this.recordRuntimeTraceEvent(agentId, ap, "runtime.delivery.async_rejected", attrs);
14674
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected", {
14675
+ agentId,
14676
+ launchId: ap.launchId || void 0,
14677
+ runtime: ap.config.runtime,
14678
+ model: ap.config.model,
14679
+ ...attrs
14680
+ }, "error");
14681
+ }
13985
14682
  noteRuntimeProgress(ap, eventKind) {
13986
14683
  ap.runtimeProgress.noteRuntimeEvent(eventKind);
13987
14684
  this.invalidateRecoveryErrorView(ap);
@@ -14170,7 +14867,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14170
14867
  if ((ap.driver.startupReadiness ?? "first_event") !== "initial_turn") {
14171
14868
  return true;
14172
14869
  }
14173
- return event.kind !== "session_init" && event.kind !== "internal_progress" && event.kind !== "runtime_diagnostic";
14870
+ return event.kind !== "session_init" && event.kind !== "internal_progress" && event.kind !== "runtime_diagnostic" && event.kind !== "runtime_recovery";
14174
14871
  }
14175
14872
  handleRuntimeStartupTimeout(agentId, ap, timeoutMs) {
14176
14873
  const current = this.agents.get(agentId);
@@ -14366,6 +15063,21 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14366
15063
  if (ap) this.recordRuntimeTelemetry(agentId, ap, event);
14367
15064
  return;
14368
15065
  }
15066
+ if (event.kind === "delivery_error") {
15067
+ if (ap) {
15068
+ this.restoreRuntimeDeliveryAfterAsyncRejection(agentId, ap, event);
15069
+ } else {
15070
+ this.recordDaemonTrace("daemon.agent.delivery_error.received_without_process", {
15071
+ agentId,
15072
+ event_kind: event.kind,
15073
+ runtime: driver.id,
15074
+ request_method: event.requestMethod,
15075
+ source: event.source,
15076
+ payloadBytes: event.payloadBytes
15077
+ });
15078
+ }
15079
+ return;
15080
+ }
14369
15081
  if (ap && isStartupRequestErrorEvent(event)) {
14370
15082
  this.handleRuntimeStartupRequestError(agentId, ap, event);
14371
15083
  return;
@@ -14382,13 +15094,15 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14382
15094
  source: event.source,
14383
15095
  itemType: event.itemType,
14384
15096
  payloadBytes: event.payloadBytes
14385
- } : event.kind === "runtime_diagnostic" ? runtimeDiagnosticTraceAttrs(event) : { kind: event.kind };
15097
+ } : event.kind === "runtime_diagnostic" ? runtimeDiagnosticTraceAttrs(event) : event.kind === "runtime_recovery" ? runtimeRecoveryTraceAttrs(event) : { kind: event.kind };
14386
15098
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.event.received", eventAttrs);
14387
- if (wasStalled) {
15099
+ const recordProgressObservedAfterStall = () => {
15100
+ if (!wasStalled) return;
14388
15101
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.observed", { afterStall: true });
14389
- }
15102
+ };
14390
15103
  if (event.kind === "internal_progress") {
14391
15104
  ap.runtimeProgress.noteInternalProgress();
15105
+ recordProgressObservedAfterStall();
14392
15106
  this.invalidateRecoveryErrorView(ap);
14393
15107
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
14394
15108
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.internal_observed", {
@@ -14405,11 +15119,17 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14405
15119
  }
14406
15120
  if (event.kind === "runtime_diagnostic") {
14407
15121
  this.noteRuntimeProgress(ap, event.kind);
15122
+ recordProgressObservedAfterStall();
14408
15123
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
14409
15124
  this.recordRuntimeDiagnosticActivity(agentId, ap, event);
14410
15125
  return;
14411
15126
  }
15127
+ if (event.kind === "runtime_recovery") {
15128
+ this.recordRuntimeRecoveryActivity(agentId, ap, event);
15129
+ return;
15130
+ }
14412
15131
  this.noteRuntimeProgress(ap, event.kind);
15132
+ recordProgressObservedAfterStall();
14413
15133
  } else if (event.kind !== "internal_progress") {
14414
15134
  this.recordDaemonTrace("daemon.agent.event.received_without_process", {
14415
15135
  agentId,
@@ -15166,8 +15886,8 @@ ${RESPONSE_TARGET_HINT}`);
15166
15886
  const nodes = [];
15167
15887
  for (const entry of entries) {
15168
15888
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
15169
- const fullPath = path13.join(dir, entry.name);
15170
- const relativePath = path13.relative(rootDir, fullPath);
15889
+ const fullPath = path14.join(dir, entry.name);
15890
+ const relativePath = path14.relative(rootDir, fullPath);
15171
15891
  let info;
15172
15892
  try {
15173
15893
  info = await stat2(fullPath);
@@ -15541,9 +16261,9 @@ var ReminderCache = class {
15541
16261
 
15542
16262
  // src/machineLock.ts
15543
16263
  import { createHash as createHash4, randomUUID as randomUUID6 } from "crypto";
15544
- 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";
16264
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
16265
+ import os8 from "os";
16266
+ import path15 from "path";
15547
16267
  var INCOMPLETE_LOCK_STALE_MS = 3e4;
15548
16268
  var DaemonMachineLockConflictError = class extends Error {
15549
16269
  code = "DAEMON_MACHINE_LOCK_HELD";
@@ -15565,7 +16285,7 @@ function resolveDefaultMachineStateRoot() {
15565
16285
  return resolveSlockHomePath("machines");
15566
16286
  }
15567
16287
  function ownerPath(lockDir) {
15568
- return path14.join(lockDir, "owner.json");
16288
+ return path15.join(lockDir, "owner.json");
15569
16289
  }
15570
16290
  function readOwner(lockDir) {
15571
16291
  try {
@@ -15595,17 +16315,17 @@ function acquireDaemonMachineLock(options) {
15595
16315
  const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
15596
16316
  const fingerprint = apiKeyFingerprint(options.apiKey);
15597
16317
  const lockId = getDaemonMachineLockId(options.apiKey);
15598
- const machineDir = path14.join(rootDir, lockId);
15599
- const lockDir = path14.join(machineDir, "daemon.lock");
16318
+ const machineDir = path15.join(rootDir, lockId);
16319
+ const lockDir = path15.join(machineDir, "daemon.lock");
15600
16320
  const token = randomUUID6();
15601
- mkdirSync6(machineDir, { recursive: true });
16321
+ mkdirSync7(machineDir, { recursive: true });
15602
16322
  for (let attempt = 0; attempt < 2; attempt += 1) {
15603
16323
  try {
15604
- mkdirSync6(lockDir);
16324
+ mkdirSync7(lockDir);
15605
16325
  const owner = {
15606
16326
  pid: process.pid,
15607
16327
  token,
15608
- hostname: os7.hostname(),
16328
+ hostname: os8.hostname(),
15609
16329
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
15610
16330
  serverUrl: options.serverUrl,
15611
16331
  apiKeyFingerprint: fingerprint.slice(0, 16)
@@ -15656,8 +16376,8 @@ function acquireDaemonMachineLock(options) {
15656
16376
  }
15657
16377
 
15658
16378
  // src/localTraceSink.ts
15659
- import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
15660
- import path15 from "path";
16379
+ import { appendFileSync, mkdirSync as mkdirSync8, readdirSync as readdirSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
16380
+ import path16 from "path";
15661
16381
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
15662
16382
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
15663
16383
  var DEFAULT_MAX_FILES = 8;
@@ -15694,7 +16414,7 @@ var LocalRotatingTraceSink = class {
15694
16414
  currentSize = 0;
15695
16415
  sequence = 0;
15696
16416
  constructor(options) {
15697
- this.traceDir = path15.join(options.machineDir, "traces");
16417
+ this.traceDir = path16.join(options.machineDir, "traces");
15698
16418
  this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
15699
16419
  const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
15700
16420
  const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
@@ -15720,11 +16440,11 @@ var LocalRotatingTraceSink = class {
15720
16440
  return this.currentFile;
15721
16441
  }
15722
16442
  ensureFile(nextBytes) {
15723
- mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
16443
+ mkdirSync8(this.traceDir, { recursive: true, mode: 448 });
15724
16444
  const nowMs = this.nowMsProvider();
15725
16445
  const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
15726
16446
  if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
15727
- this.currentFile = path15.join(
16447
+ this.currentFile = path16.join(
15728
16448
  this.traceDir,
15729
16449
  `daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
15730
16450
  );
@@ -15739,7 +16459,7 @@ var LocalRotatingTraceSink = class {
15739
16459
  const excess = files.length - this.maxFiles;
15740
16460
  if (excess <= 0) return;
15741
16461
  for (const file of files.slice(0, excess)) {
15742
- rmSync4(path15.join(this.traceDir, file), { force: true });
16462
+ rmSync4(path16.join(this.traceDir, file), { force: true });
15743
16463
  }
15744
16464
  }
15745
16465
  };
@@ -15830,7 +16550,7 @@ function isDiagnosticErrorAttr(key) {
15830
16550
  import { createHash as createHash6, randomUUID as randomUUID7 } from "crypto";
15831
16551
  import { gzipSync as gzipSync2 } from "zlib";
15832
16552
  import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
15833
- import path16 from "path";
16553
+ import path17 from "path";
15834
16554
 
15835
16555
  // src/traceJitter.ts
15836
16556
  import { createHash as createHash5 } from "crypto";
@@ -15929,7 +16649,7 @@ var DaemonTraceBundleUploader = class {
15929
16649
  }, nextMs);
15930
16650
  }
15931
16651
  async findUploadCandidates() {
15932
- const traceDir = path16.join(this.options.machineDir, "traces");
16652
+ const traceDir = path17.join(this.options.machineDir, "traces");
15933
16653
  let names;
15934
16654
  try {
15935
16655
  names = await readdir3(traceDir);
@@ -15941,8 +16661,8 @@ var DaemonTraceBundleUploader = class {
15941
16661
  const currentFile = this.options.currentFileProvider?.();
15942
16662
  const candidates = [];
15943
16663
  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;
16664
+ const file = path17.join(traceDir, name);
16665
+ if (currentFile && path17.resolve(file) === path17.resolve(currentFile)) continue;
15946
16666
  if (await this.isUploaded(file)) continue;
15947
16667
  try {
15948
16668
  const info = await stat3(file);
@@ -16016,8 +16736,8 @@ var DaemonTraceBundleUploader = class {
16016
16736
  }
16017
16737
  }
16018
16738
  uploadStatePath(file) {
16019
- const stateDir = path16.join(this.options.machineDir, "trace-uploads");
16020
- return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
16739
+ const stateDir = path17.join(this.options.machineDir, "trace-uploads");
16740
+ return path17.join(stateDir, `${path17.basename(file)}.uploaded.json`);
16021
16741
  }
16022
16742
  async isUploaded(file) {
16023
16743
  try {
@@ -16029,9 +16749,9 @@ var DaemonTraceBundleUploader = class {
16029
16749
  }
16030
16750
  async markUploaded(file, metadata) {
16031
16751
  const stateFile = this.uploadStatePath(file);
16032
- await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
16752
+ await mkdir2(path17.dirname(stateFile), { recursive: true, mode: 448 });
16033
16753
  await writeFile2(stateFile, `${JSON.stringify({
16034
- file: path16.basename(file),
16754
+ file: path17.basename(file),
16035
16755
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
16036
16756
  ...metadata
16037
16757
  }, null, 2)}
@@ -16180,13 +16900,13 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
16180
16900
  }
16181
16901
  }
16182
16902
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
16183
- const thisDir = path17.dirname(fileURLToPath(moduleUrl));
16184
- const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
16903
+ const thisDir = path18.dirname(fileURLToPath(moduleUrl));
16904
+ const bundledDistPath = path18.resolve(thisDir, "cli", "index.js");
16185
16905
  try {
16186
16906
  accessSync(bundledDistPath);
16187
16907
  return bundledDistPath;
16188
16908
  } catch {
16189
- const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
16909
+ const workspaceDistPath = path18.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
16190
16910
  accessSync(workspaceDistPath);
16191
16911
  return workspaceDistPath;
16192
16912
  }
@@ -16200,11 +16920,12 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
16200
16920
  }
16201
16921
  async function runBundledSlockCli(argv) {
16202
16922
  process.argv = [process.execPath, "slock", ...argv];
16203
- await import("./dist-DSRBN3VD.js");
16923
+ await import("./dist-5BEASCX5.js");
16204
16924
  }
16205
16925
  function detectRuntimes(tracer = noopTracer) {
16206
16926
  const ids = [];
16207
16927
  const versions = {};
16928
+ const diagnostics = {};
16208
16929
  const span = tracer.startSpan("daemon.runtime.detect", {
16209
16930
  surface: "daemon",
16210
16931
  kind: "internal",
@@ -16220,20 +16941,26 @@ function detectRuntimes(tracer = noopTracer) {
16220
16941
  const probe = driver.probe();
16221
16942
  if (!probe.available) {
16222
16943
  if (probe.version) versions[runtime.id] = probe.version;
16944
+ if (probe.diagnostic) diagnostics[runtime.id] = probe.diagnostic;
16223
16945
  span.addEvent("daemon.runtime.detect.checked", {
16224
16946
  runtime: runtime.id,
16225
16947
  outcome: "unavailable",
16226
16948
  version_present: Boolean(probe.version),
16949
+ diagnostic_present: Boolean(probe.diagnostic),
16950
+ ...probe.diagnostic ? { diagnostic: probe.diagnostic } : {},
16227
16951
  binary_path_present: false
16228
16952
  });
16229
16953
  continue;
16230
16954
  }
16231
16955
  ids.push(runtime.id);
16232
16956
  if (probe.version) versions[runtime.id] = probe.version;
16957
+ if (probe.diagnostic) diagnostics[runtime.id] = probe.diagnostic;
16233
16958
  span.addEvent("daemon.runtime.detect.checked", {
16234
16959
  runtime: runtime.id,
16235
16960
  outcome: "available",
16236
16961
  version_present: Boolean(probe.version),
16962
+ diagnostic_present: Boolean(probe.diagnostic),
16963
+ ...probe.diagnostic ? { diagnostic: probe.diagnostic } : {},
16237
16964
  binary_path_present: false
16238
16965
  });
16239
16966
  continue;
@@ -16276,7 +17003,7 @@ function detectRuntimes(tracer = noopTracer) {
16276
17003
  detected_runtime_count: ids.length
16277
17004
  }
16278
17005
  });
16279
- return { ids, versions };
17006
+ return { ids, versions, ...Object.keys(diagnostics).length > 0 ? { diagnostics } : {} };
16280
17007
  }
16281
17008
  function readPositiveIntegerEnv3(name, fallback) {
16282
17009
  const raw = process.env[name];
@@ -16398,7 +17125,7 @@ var DaemonCore = class {
16398
17125
  }
16399
17126
  resolveMachineStateRoot() {
16400
17127
  if (this.options.machineStateDir) return this.options.machineStateDir;
16401
- if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
17128
+ if (this.options.dataDir) return path18.join(path18.dirname(this.options.dataDir), "machines");
16402
17129
  return resolveDefaultMachineStateRoot();
16403
17130
  }
16404
17131
  shouldEnableLocalTrace() {
@@ -16425,7 +17152,7 @@ var DaemonCore = class {
16425
17152
  sink: this.localTraceSink
16426
17153
  }));
16427
17154
  this.agentManager.setTracer(this.tracer);
16428
- this.agentManager.setCliTransportTraceDir(path17.join(machineDir, "traces"));
17155
+ this.agentManager.setCliTransportTraceDir(path18.join(machineDir, "traces"));
16429
17156
  }
16430
17157
  installTraceBundleUploader(machineDir) {
16431
17158
  if (!this.shouldEnableLocalTrace()) return;
@@ -16973,9 +17700,12 @@ var DaemonCore = class {
16973
17700
  });
16974
17701
  }
16975
17702
  handleConnect() {
16976
- const { ids: runtimes, versions: runtimeVersions } = this.runtimeDetector();
17703
+ const { ids: runtimes, versions: runtimeVersions, diagnostics: runtimeDiagnostics = {} } = this.runtimeDetector();
16977
17704
  const runtimeInfo = runtimes.map((id) => runtimeVersions[id] ? `${id} (${runtimeVersions[id]})` : id);
16978
17705
  logger.info(`[Daemon] Detected runtimes: ${runtimeInfo.join(", ") || "none"}`);
17706
+ for (const [runtime, diagnostic] of Object.entries(runtimeDiagnostics)) {
17707
+ logger.warn(`[Daemon] Runtime ${runtime} diagnostic: ${diagnostic}`);
17708
+ }
16979
17709
  if (!this.opencliWrappersRegenerated) {
16980
17710
  this.opencliWrappersRegenerated = true;
16981
17711
  try {
@@ -16995,8 +17725,8 @@ var DaemonCore = class {
16995
17725
  capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
16996
17726
  runtimes,
16997
17727
  runningAgents: runningAgentIds,
16998
- hostname: this.options.hostname ?? os8.hostname(),
16999
- os: this.options.osDescription ?? `${os8.platform()} ${os8.arch()}`,
17728
+ hostname: this.options.hostname ?? os9.hostname(),
17729
+ os: this.options.osDescription ?? `${os9.platform()} ${os9.arch()}`,
17000
17730
  daemonVersion: this.daemonVersion,
17001
17731
  ...this.computerVersion ? { computerVersion: this.computerVersion } : {}
17002
17732
  });