@botiverse/raft-daemon 0.62.0 → 0.63.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  // src/core.ts
2
- import path17 from "path";
3
- import os8 from "os";
2
+ import path18 from "path";
3
+ import os9 from "os";
4
4
  import { createRequire as createRequire3 } from "module";
5
5
  import { accessSync } from "fs";
6
6
  import { fileURLToPath } from "url";
@@ -1602,9 +1602,9 @@ var FREE_MONTHLY_FILE_UPLOAD_LIMIT_BYTES = 100 * 1024 * 1024;
1602
1602
  import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync4 } from "fs";
1603
1603
  import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2, lstat, realpath, open } from "fs/promises";
1604
1604
  import { createHash as createHash3, randomUUID as randomUUID5 } from "crypto";
1605
- import path13 from "path";
1605
+ import path14 from "path";
1606
1606
  import { gzipSync } from "zlib";
1607
- import os6 from "os";
1607
+ import os7 from "os";
1608
1608
 
1609
1609
  // src/proxy.ts
1610
1610
  import { HttpsProxyAgent } from "https-proxy-agent";
@@ -1873,8 +1873,8 @@ async function executeResponseRequest(url, init, {
1873
1873
  }
1874
1874
 
1875
1875
  // src/directUploadCapability.ts
1876
- function joinUrl(base, path18) {
1877
- return `${base.replace(/\/+$/, "")}${path18}`;
1876
+ function joinUrl(base, path19) {
1877
+ return `${base.replace(/\/+$/, "")}${path19}`;
1878
1878
  }
1879
1879
  function jsonHeaders(apiKey) {
1880
1880
  return {
@@ -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,47 @@ 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";
5181
5206
  import path6 from "path";
5207
+ function readConfiguredCodexHome(env) {
5208
+ const raw = env.CODEX_HOME;
5209
+ return typeof raw === "string" && raw.trim().length > 0 ? raw : null;
5210
+ }
5211
+ function resolveCodexHomeRootFromEnv(env = process.env, opts = {}) {
5212
+ const raw = readConfiguredCodexHome(env);
5213
+ if (raw) {
5214
+ return path6.resolve(opts.cwd ?? process.cwd(), raw);
5215
+ }
5216
+ return path6.join(opts.defaultHomeDir ?? os3.homedir(), ".codex");
5217
+ }
5218
+ function hasConfiguredCodexHome(config, baseEnv = process.env) {
5219
+ const launchRuntimeFields = config ? runtimeConfigToLaunchFields(config) : null;
5220
+ return Boolean(readConfiguredCodexHome({
5221
+ ...baseEnv,
5222
+ ...launchRuntimeFields?.envVars || {}
5223
+ }));
5224
+ }
5225
+ function resolveCodexHomeRootFromConfig(config, defaultHomeDir, cwd, baseEnv = process.env, _opts = {}) {
5226
+ const launchRuntimeFields = runtimeConfigToLaunchFields(config);
5227
+ const env = {
5228
+ ...baseEnv,
5229
+ ...launchRuntimeFields.envVars || {}
5230
+ };
5231
+ return resolveCodexHomeRootFromEnv(env, { defaultHomeDir, cwd });
5232
+ }
5233
+ function codexStateRootCandidates(homeDirOrCodexRoot) {
5234
+ return [
5235
+ homeDirOrCodexRoot,
5236
+ path6.join(homeDirOrCodexRoot, ".codex")
5237
+ ];
5238
+ }
5239
+ function codexSessionRootCandidates(homeDirOrCodexRoot) {
5240
+ return codexStateRootCandidates(homeDirOrCodexRoot).map((root) => path6.join(root, "sessions"));
5241
+ }
5182
5242
 
5183
5243
  // src/runtimeTurnState.ts
5184
5244
  var RuntimeTurnState = class {
@@ -5359,7 +5419,7 @@ function codexNotificationDiagnosticEvent(message) {
5359
5419
  return null;
5360
5420
  }
5361
5421
  const sessionId = codexMessageThreadId(message);
5362
- const path18 = boundedString(params.path);
5422
+ const path19 = boundedString(params.path);
5363
5423
  return {
5364
5424
  kind: "runtime_diagnostic",
5365
5425
  severity: "warning",
@@ -5367,12 +5427,42 @@ function codexNotificationDiagnosticEvent(message) {
5367
5427
  itemType: message.method,
5368
5428
  message: diagnosticMessage,
5369
5429
  ...details ? { details } : {},
5370
- ...path18 ? { path: path18 } : {},
5430
+ ...path19 ? { path: path19 } : {},
5371
5431
  ...params.range !== void 0 ? { range: params.range } : {},
5372
5432
  payloadBytes: payloadBytes(params),
5373
5433
  ...sessionId ? { sessionId } : {}
5374
5434
  };
5375
5435
  }
5436
+ function codexThreadStatusChangedEvent(message) {
5437
+ if (message.method !== "thread/status/changed") return null;
5438
+ const params = message.params ?? {};
5439
+ const status = params.status ?? params.thread?.status;
5440
+ if (!status || typeof status !== "object") return null;
5441
+ const statusType = nonEmptyString2(status.type);
5442
+ const sessionId = codexMessageThreadId(message);
5443
+ if (statusType === "systemError") {
5444
+ const statusMessage = boundedString(status.message) ?? boundedString(status.error?.message) ?? getCodexNotificationErrorMessage(params) ?? "Codex thread entered system error state";
5445
+ return {
5446
+ kind: "error",
5447
+ message: statusMessage
5448
+ };
5449
+ }
5450
+ if (statusType !== "active" || !Array.isArray(status.activeFlags)) return null;
5451
+ const activeFlags = status.activeFlags.filter((flag) => typeof flag === "string");
5452
+ const waitFlags = activeFlags.filter((flag) => flag === "waitingOnApproval" || flag === "waitingOnUserInput");
5453
+ if (waitFlags.length === 0) return null;
5454
+ const waitLabel = waitFlags.includes("waitingOnApproval") && waitFlags.includes("waitingOnUserInput") ? "approval and user input" : waitFlags.includes("waitingOnApproval") ? "approval" : "user input";
5455
+ return {
5456
+ kind: "runtime_diagnostic",
5457
+ severity: "warning",
5458
+ source: "codex_app_server_notification",
5459
+ itemType: "thread/status/changed",
5460
+ message: `Codex thread is waiting on ${waitLabel}`,
5461
+ details: `Active flags: ${waitFlags.join(", ")}`,
5462
+ payloadBytes: payloadBytes(params),
5463
+ ...sessionId ? { sessionId } : {}
5464
+ };
5465
+ }
5376
5466
  function joinReasoningSummaryText(item) {
5377
5467
  const summary = Array.isArray(item.summary) ? item.summary.filter((entry) => typeof entry === "string") : [];
5378
5468
  return summary.join("\n").trim();
@@ -5400,10 +5490,27 @@ function codexMcpToolName(item) {
5400
5490
  function codexMessageThreadId(message) {
5401
5491
  return nonEmptyString2(message.params?.threadId) ?? nonEmptyString2(message.params?.thread?.id) ?? nonEmptyString2(message.params?.sessionId);
5402
5492
  }
5493
+ function codexAgentMessagePhase(value) {
5494
+ return typeof value === "string" && value.length > 0 ? value : null;
5495
+ }
5496
+ function codexAgentMessageDeltaPhase(message) {
5497
+ return codexAgentMessagePhase(message.params?.phase) ?? codexAgentMessagePhase(message.params?.item?.phase);
5498
+ }
5499
+ function isUserVisibleAgentMessagePhase(phase) {
5500
+ return phase === null || phase === "final_answer";
5501
+ }
5502
+ function codexSuppressedAgentMessageEvent(itemType, phase, text, itemId) {
5503
+ return codexNotificationProgressEvent(itemType, {
5504
+ ...typeof itemId === "string" ? { itemId } : {},
5505
+ ...phase ? { phase } : {},
5506
+ bytes: Buffer.byteLength(text, "utf8")
5507
+ });
5508
+ }
5403
5509
  var CodexEventNormalizer = class {
5404
5510
  currentThreadId = null;
5405
5511
  sessionAnnounced = false;
5406
5512
  streamedAgentMessageIds = /* @__PURE__ */ new Set();
5513
+ agentMessagePhases = /* @__PURE__ */ new Map();
5407
5514
  streamedReasoningIds = /* @__PURE__ */ new Set();
5408
5515
  fileChangeToolCallCounts = /* @__PURE__ */ new Map();
5409
5516
  turnState = new RuntimeTurnState();
@@ -5412,6 +5519,7 @@ var CodexEventNormalizer = class {
5412
5519
  this.turnState.reset();
5413
5520
  this.sessionAnnounced = false;
5414
5521
  this.streamedAgentMessageIds.clear();
5522
+ this.agentMessagePhases.clear();
5415
5523
  this.streamedReasoningIds.clear();
5416
5524
  this.fileChangeToolCallCounts.clear();
5417
5525
  }
@@ -5489,12 +5597,17 @@ var CodexEventNormalizer = class {
5489
5597
  case "item/agentMessage/delta": {
5490
5598
  const delta = message.params?.delta;
5491
5599
  const itemId = message.params?.itemId;
5600
+ const phase = codexAgentMessageDeltaPhase(message) ?? (typeof itemId === "string" ? this.agentMessagePhases.get(itemId) ?? null : null);
5492
5601
  if (typeof itemId === "string") {
5493
5602
  this.streamedAgentMessageIds.add(itemId);
5494
5603
  }
5495
5604
  if (typeof delta === "string" && delta.length > 0) {
5496
5605
  this.turnState.markProgress();
5497
- events.push({ kind: "text", text: delta });
5606
+ if (isUserVisibleAgentMessagePhase(phase)) {
5607
+ events.push({ kind: "text", text: delta });
5608
+ } else {
5609
+ events.push(codexSuppressedAgentMessageEvent("agent_message_non_final_delta", phase, delta, itemId));
5610
+ }
5498
5611
  }
5499
5612
  break;
5500
5613
  }
@@ -5545,6 +5658,13 @@ var CodexEventNormalizer = class {
5545
5658
  }
5546
5659
  break;
5547
5660
  }
5661
+ case "thread/status/changed": {
5662
+ const event = codexThreadStatusChangedEvent(message);
5663
+ if (event) {
5664
+ events.push(event);
5665
+ }
5666
+ break;
5667
+ }
5548
5668
  case "item/started":
5549
5669
  case "item/completed": {
5550
5670
  const item = message.params?.item;
@@ -5566,12 +5686,21 @@ var CodexEventNormalizer = class {
5566
5686
  }
5567
5687
  break;
5568
5688
  case "agentMessage":
5689
+ if ((isStarted || isCompleted) && typeof item.id === "string") {
5690
+ this.agentMessagePhases.set(item.id, codexAgentMessagePhase(item.phase));
5691
+ }
5569
5692
  if (isCompleted && typeof item.id === "string" && !this.streamedAgentMessageIds.has(item.id) && typeof item.text === "string" && item.text.length > 0) {
5693
+ const phase = codexAgentMessagePhase(item.phase);
5570
5694
  this.turnState.markProgress();
5571
- events.push({ kind: "text", text: item.text });
5695
+ if (isUserVisibleAgentMessagePhase(phase)) {
5696
+ events.push({ kind: "text", text: item.text });
5697
+ } else {
5698
+ events.push(codexSuppressedAgentMessageEvent("agent_message_non_final_completed", phase, item.text, item.id));
5699
+ }
5572
5700
  }
5573
5701
  if (isCompleted && typeof item.id === "string") {
5574
5702
  this.streamedAgentMessageIds.delete(item.id);
5703
+ this.agentMessagePhases.delete(item.id);
5575
5704
  }
5576
5705
  break;
5577
5706
  case "commandExecution":
@@ -5710,14 +5839,15 @@ var CodexEventNormalizer = class {
5710
5839
 
5711
5840
  // src/drivers/codex.ts
5712
5841
  var CODEX_DESKTOP_BUNDLE_PATH = "/Applications/Codex.app/Contents/Resources/codex";
5842
+ var CODEX_APP_SERVER_PROBE_ARGS = ["app-server", "--help"];
5713
5843
  function isWindowsSandboxRunner(commandPath) {
5714
- return path6.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
5844
+ return path7.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
5715
5845
  }
5716
5846
  function resolveWindowsNpmCodexEntry(deps = {}) {
5717
5847
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
5718
5848
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
5719
5849
  const env = deps.env ?? process.env;
5720
- const winPath = path6.win32;
5850
+ const winPath = path7.win32;
5721
5851
  try {
5722
5852
  const globalRoot = String(execFileSyncFn("npm", ["root", "-g"], {
5723
5853
  encoding: "utf8",
@@ -5739,7 +5869,7 @@ function resolveWindowsCodexDesktopEntry(deps = {}) {
5739
5869
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
5740
5870
  const env = deps.env ?? process.env;
5741
5871
  const homeDir = deps.homeDir;
5742
- const winPath = path6.win32;
5872
+ const winPath = path7.win32;
5743
5873
  const candidates = [
5744
5874
  env.LOCALAPPDATA ? winPath.join(env.LOCALAPPDATA, "OpenAI", "Codex", "bin", "codex.exe") : null,
5745
5875
  env.USERPROFILE ? winPath.join(env.USERPROFILE, "AppData", "Local", "OpenAI", "Codex", "bin", "codex.exe") : null,
@@ -5750,69 +5880,158 @@ function resolveWindowsCodexDesktopEntry(deps = {}) {
5750
5880
  }
5751
5881
  return null;
5752
5882
  }
5753
- function resolveCodexCommand(deps = {}) {
5883
+ function codexSpawnCandidates(deps = {}) {
5754
5884
  const platform = deps.platform ?? process.platform;
5755
5885
  if (platform === "win32") {
5886
+ const candidates2 = [];
5756
5887
  const npmEntry = resolveWindowsNpmCodexEntry(deps);
5757
- if (npmEntry) return npmEntry;
5888
+ if (npmEntry) {
5889
+ candidates2.push({
5890
+ source: "npm_global",
5891
+ command: process.execPath,
5892
+ argsPrefix: [npmEntry],
5893
+ shell: false
5894
+ });
5895
+ }
5758
5896
  const command = resolveCommandOnPath("codex", deps);
5759
- if (command && !isWindowsSandboxRunner(command)) return command;
5760
- return resolveWindowsCodexDesktopEntry(deps);
5897
+ if (command && !isWindowsSandboxRunner(command)) {
5898
+ candidates2.push({
5899
+ source: "path",
5900
+ command,
5901
+ argsPrefix: [],
5902
+ shell: requiresWindowsShell(command, platform)
5903
+ });
5904
+ }
5905
+ const desktopEntry = resolveWindowsCodexDesktopEntry(deps);
5906
+ if (desktopEntry && !candidates2.some((candidate) => candidate.command === desktopEntry)) {
5907
+ candidates2.push({
5908
+ source: "desktop_install",
5909
+ command: desktopEntry,
5910
+ argsPrefix: [],
5911
+ shell: false
5912
+ });
5913
+ }
5914
+ return candidates2;
5761
5915
  }
5916
+ const candidates = [];
5762
5917
  const pathCommand = resolveCommandOnPath("codex", deps);
5763
- if (pathCommand) return pathCommand;
5918
+ if (pathCommand) {
5919
+ candidates.push({
5920
+ source: "path",
5921
+ command: pathCommand,
5922
+ argsPrefix: [],
5923
+ shell: false
5924
+ });
5925
+ }
5764
5926
  if (platform === "darwin") {
5765
- return firstExistingPath([CODEX_DESKTOP_BUNDLE_PATH], deps);
5927
+ const bundleCommand = firstExistingPath([CODEX_DESKTOP_BUNDLE_PATH], deps);
5928
+ if (bundleCommand && !candidates.some((candidate) => candidate.command === bundleCommand)) {
5929
+ candidates.push({
5930
+ source: "desktop_bundle",
5931
+ command: bundleCommand,
5932
+ argsPrefix: [],
5933
+ shell: false
5934
+ });
5935
+ }
5766
5936
  }
5767
- return null;
5937
+ return candidates;
5938
+ }
5939
+ function formatCodexCandidate(candidate) {
5940
+ return [candidate.command, ...candidate.argsPrefix].join(" ");
5941
+ }
5942
+ function describeCodexProbeFailure(error) {
5943
+ if (error && typeof error === "object") {
5944
+ const candidate = error;
5945
+ if (typeof candidate.status === "number") return `exit status ${candidate.status}`;
5946
+ if (typeof candidate.signal === "string") return `terminated by ${candidate.signal}`;
5947
+ if (typeof candidate.code === "string") return candidate.code;
5948
+ }
5949
+ return "probe failed";
5950
+ }
5951
+ function validateCodexAppServer(candidate, deps = {}) {
5952
+ const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
5953
+ const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
5954
+ try {
5955
+ execFileSyncFn(candidate.command, [...candidate.argsPrefix, ...CODEX_APP_SERVER_PROBE_ARGS], {
5956
+ stdio: ["ignore", "pipe", "pipe"],
5957
+ env,
5958
+ timeout: 5e3,
5959
+ shell: candidate.shell
5960
+ });
5961
+ return null;
5962
+ } catch (error) {
5963
+ return describeCodexProbeFailure(error);
5964
+ }
5965
+ }
5966
+ function readCodexCandidateVersion(candidate, deps = {}) {
5967
+ const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
5968
+ const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
5969
+ try {
5970
+ const output = execFileSyncFn(candidate.command, [...candidate.argsPrefix, "--version"], {
5971
+ stdio: ["ignore", "pipe", "pipe"],
5972
+ env,
5973
+ timeout: 5e3,
5974
+ shell: candidate.shell
5975
+ });
5976
+ return (Buffer.isBuffer(output) ? output.toString("utf8") : String(output ?? "")).trim().split(/\r?\n/)[0] || null;
5977
+ } catch {
5978
+ return null;
5979
+ }
5980
+ }
5981
+ function resolveCompatibleCodexCandidate(deps = {}) {
5982
+ const rejected = [];
5983
+ for (const candidate of codexSpawnCandidates(deps)) {
5984
+ const failure = validateCodexAppServer(candidate, deps);
5985
+ if (!failure) return { candidate, rejected };
5986
+ rejected.push(`${candidate.source} ${formatCodexCandidate(candidate)} rejected: app-server probe ${failure}`);
5987
+ }
5988
+ return { candidate: null, rejected };
5768
5989
  }
5769
5990
  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
- }
5991
+ const { candidate, rejected } = resolveCompatibleCodexCandidate(deps);
5992
+ const diagnostic = rejected.length > 0 ? rejected.join("; ") : void 0;
5993
+ if (!candidate) {
5994
+ return {
5995
+ available: false,
5996
+ diagnostic: diagnostic ?? "No Codex CLI app-server candidate was found."
5997
+ };
5780
5998
  }
5781
- const command = resolveCodexCommand(deps);
5782
- if (!command) return { available: false };
5783
5999
  return {
5784
6000
  available: true,
5785
- version: readCommandVersion(command, [], deps) ?? void 0
6001
+ version: readCodexCandidateVersion(candidate, deps) ?? void 0,
6002
+ ...diagnostic ? { diagnostic } : {}
5786
6003
  };
5787
6004
  }
5788
6005
  function resolveCodexSpawn(commandArgs, deps = {}) {
5789
- if ((deps.platform ?? process.platform) !== "win32") {
5790
- return { command: resolveCodexCommand(deps) ?? "codex", args: commandArgs, shell: false };
6006
+ const { candidate, rejected } = resolveCompatibleCodexCandidate(deps);
6007
+ if (candidate) {
6008
+ return { command: candidate.command, args: [...candidate.argsPrefix, ...commandArgs], shell: candidate.shell };
5791
6009
  }
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 };
6010
+ const diagnostic = rejected.length > 0 ? ` Rejected candidates: ${rejected.join("; ")}.` : "";
6011
+ if ((deps.platform ?? process.platform) === "win32") {
6012
+ throw new Error(
6013
+ "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
6014
+ );
5807
6015
  }
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
- );
6016
+ throw new Error(`Cannot resolve a compatible Codex CLI app-server entry point.${diagnostic}`);
5811
6017
  }
5812
6018
  function isCodexMissingRolloutError(message) {
5813
6019
  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
6020
  }
5815
- function classifyCodexResumeError(message) {
6021
+ function codexMissingRolloutRecoveryMessage() {
6022
+ return "Codex could not resume its previous thread; Slock started a fresh Codex thread.";
6023
+ }
6024
+ function codexMissingRolloutRecoveryDetails() {
6025
+ return "Use Slock conversation history and local MEMORY.md/notes as the recovery point; do not assume prior Codex thread context is loaded.";
6026
+ }
6027
+ function prependCodexRecoveryNotice(prompt, recovery) {
6028
+ return `${recovery.message}
6029
+
6030
+ ${recovery.details}
6031
+
6032
+ ${prompt}`;
6033
+ }
6034
+ function classifyCodexResumeError(message, requestedSessionId) {
5816
6035
  if (isCodexMissingRolloutError(message)) {
5817
6036
  return {
5818
6037
  kind: "missing_rollout",
@@ -5826,6 +6045,15 @@ function classifyCodexResumeError(message) {
5826
6045
  resume_error_class: "missing_rollout",
5827
6046
  recovery_action: "fallback_fresh_thread"
5828
6047
  }
6048
+ },
6049
+ recovery: {
6050
+ kind: "runtime_recovery",
6051
+ source: "codex_resume_missing_rollout",
6052
+ resumeErrorClass: "missing_rollout",
6053
+ recoveryAction: "fallback_fresh_thread",
6054
+ message: codexMissingRolloutRecoveryMessage(),
6055
+ details: codexMissingRolloutRecoveryDetails(),
6056
+ ...requestedSessionId ? { requestedSessionId } : {}
5829
6057
  }
5830
6058
  };
5831
6059
  }
@@ -5849,6 +6077,26 @@ function isJsonRpcResponse(message) {
5849
6077
  function isCodexServerRequest(message) {
5850
6078
  return message.id !== void 0 && typeof message.method === "string" && !isJsonRpcResponse(message);
5851
6079
  }
6080
+ function payloadBytes2(value) {
6081
+ try {
6082
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
6083
+ } catch {
6084
+ return void 0;
6085
+ }
6086
+ }
6087
+ function extractCodexHome(result) {
6088
+ if (!result || typeof result !== "object") return null;
6089
+ const candidate = result.codexHome;
6090
+ return typeof candidate === "string" && candidate.trim().length > 0 ? candidate : null;
6091
+ }
6092
+ function isCompatibleInitializeResult(result) {
6093
+ if (!result || typeof result !== "object" || Array.isArray(result)) return false;
6094
+ const userAgent = result.userAgent;
6095
+ return typeof userAgent === "string" && userAgent.trim().length > 0;
6096
+ }
6097
+ function unsupportedInitializeResultMessage() {
6098
+ return "Codex app-server initialize response is missing the expected userAgent handshake field; upgrade Codex CLI to a compatible app-server build.";
6099
+ }
5852
6100
  var CodexDriver = class {
5853
6101
  id = "codex";
5854
6102
  lifecycle = {
@@ -5919,9 +6167,16 @@ var CodexDriver = class {
5919
6167
  pendingThreadRequestId = null;
5920
6168
  pendingThreadRequestMethod = null;
5921
6169
  pendingResumeFallbackParams = null;
6170
+ pendingResumeThreadId = null;
5922
6171
  pendingInitialTurnRequestId = null;
6172
+ pendingDeliveryRequests = /* @__PURE__ */ new Map();
5923
6173
  initialTurnStarted = false;
5924
6174
  normalizer = new CodexEventNormalizer();
6175
+ codexHomeRoot = null;
6176
+ spawnWorkingDirectory = null;
6177
+ get currentRuntimeHomeDir() {
6178
+ return this.codexHomeRoot;
6179
+ }
5925
6180
  async spawn(ctx) {
5926
6181
  const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
5927
6182
  this.process = null;
@@ -5932,9 +6187,16 @@ var CodexDriver = class {
5932
6187
  this.pendingThreadRequestId = null;
5933
6188
  this.pendingThreadRequestMethod = null;
5934
6189
  this.pendingResumeFallbackParams = null;
6190
+ this.pendingResumeThreadId = null;
5935
6191
  this.pendingInitialTurnRequestId = null;
6192
+ this.pendingDeliveryRequests.clear();
5936
6193
  this.initialTurnStarted = false;
5937
6194
  this.normalizer.reset();
6195
+ this.spawnWorkingDirectory = ctx.workingDirectory;
6196
+ this.codexHomeRoot = resolveCodexHomeRootFromEnv(spawnEnv, {
6197
+ defaultHomeDir: os4.homedir(),
6198
+ cwd: ctx.workingDirectory
6199
+ });
5938
6200
  const args = ["app-server", "--listen", "stdio://"];
5939
6201
  const { command, args: spawnArgs, shell } = resolveCodexSpawn(args);
5940
6202
  const proc = spawn2(command, spawnArgs, {
@@ -5969,6 +6231,24 @@ var CodexDriver = class {
5969
6231
  const isResponse = isJsonRpcResponse(message);
5970
6232
  if (isResponse && hasJsonRpcField(message, "result")) {
5971
6233
  if (message.id === this.initializeRequestId) {
6234
+ if (!isCompatibleInitializeResult(message.result)) {
6235
+ this.initializeRequestId = null;
6236
+ this.pendingThreadRequest = null;
6237
+ this.pendingThreadRequestId = null;
6238
+ this.pendingThreadRequestMethod = null;
6239
+ this.pendingResumeFallbackParams = null;
6240
+ this.pendingResumeThreadId = null;
6241
+ events.push({
6242
+ kind: "error",
6243
+ message: unsupportedInitializeResultMessage(),
6244
+ startupRequestMethod: "initialize"
6245
+ });
6246
+ return events;
6247
+ }
6248
+ const codexHome = extractCodexHome(message.result);
6249
+ if (codexHome) {
6250
+ this.codexHomeRoot = path7.resolve(this.spawnWorkingDirectory ?? process.cwd(), codexHome);
6251
+ }
5972
6252
  this.initializeRequestId = null;
5973
6253
  this.sendNotification("initialized", {});
5974
6254
  if (this.pendingThreadRequest) {
@@ -5984,6 +6264,7 @@ var CodexDriver = class {
5984
6264
  this.pendingThreadRequestId = null;
5985
6265
  this.pendingThreadRequestMethod = null;
5986
6266
  this.pendingResumeFallbackParams = null;
6267
+ this.pendingResumeThreadId = null;
5987
6268
  events.push({
5988
6269
  kind: "error",
5989
6270
  message: message.error?.message || "Codex app-server request failed",
@@ -5995,9 +6276,16 @@ var CodexDriver = class {
5995
6276
  if (hasJsonRpcField(message, "error")) {
5996
6277
  const errorMessage = message.error?.message || "Codex app-server request failed";
5997
6278
  const requestMethod = this.pendingThreadRequestMethod;
5998
- const resumeErrorClassification = requestMethod === "thread/resume" ? classifyCodexResumeError(errorMessage) : null;
6279
+ const resumeErrorClassification = requestMethod === "thread/resume" ? classifyCodexResumeError(errorMessage, this.pendingResumeThreadId || void 0) : null;
5999
6280
  if (this.pendingResumeFallbackParams && resumeErrorClassification?.kind === "missing_rollout") {
6000
6281
  events.push(resumeErrorClassification.telemetry);
6282
+ events.push(resumeErrorClassification.recovery);
6283
+ if (this.pendingInitialPrompt) {
6284
+ this.pendingInitialPrompt = prependCodexRecoveryNotice(
6285
+ this.pendingInitialPrompt,
6286
+ resumeErrorClassification.recovery
6287
+ );
6288
+ }
6001
6289
  this.sendThreadRequest("thread/start", this.pendingResumeFallbackParams);
6002
6290
  this.pendingResumeFallbackParams = null;
6003
6291
  return events;
@@ -6005,12 +6293,14 @@ var CodexDriver = class {
6005
6293
  this.pendingThreadRequestId = null;
6006
6294
  this.pendingThreadRequestMethod = null;
6007
6295
  this.pendingResumeFallbackParams = null;
6296
+ this.pendingResumeThreadId = null;
6008
6297
  events.push(requestMethod ? { kind: "error", message: errorMessage, startupRequestMethod: requestMethod } : { kind: "error", message: errorMessage });
6009
6298
  return events;
6010
6299
  }
6011
6300
  this.pendingThreadRequestId = null;
6012
6301
  this.pendingThreadRequestMethod = null;
6013
6302
  this.pendingResumeFallbackParams = null;
6303
+ this.pendingResumeThreadId = null;
6014
6304
  }
6015
6305
  if (isResponse && message.id === this.pendingInitialTurnRequestId) {
6016
6306
  this.pendingInitialTurnRequestId = null;
@@ -6025,6 +6315,21 @@ var CodexDriver = class {
6025
6315
  }
6026
6316
  return events;
6027
6317
  }
6318
+ if (isResponse && message.id !== void 0 && this.pendingDeliveryRequests.has(message.id)) {
6319
+ const requestMethod = this.pendingDeliveryRequests.get(message.id);
6320
+ this.pendingDeliveryRequests.delete(message.id);
6321
+ if (hasJsonRpcField(message, "error")) {
6322
+ const params = message.error ?? {};
6323
+ events.push({
6324
+ kind: "delivery_error",
6325
+ message: message.error?.message || "Codex app-server request failed",
6326
+ requestMethod,
6327
+ source: "codex_app_server_response",
6328
+ payloadBytes: payloadBytes2(params)
6329
+ });
6330
+ }
6331
+ return events;
6332
+ }
6028
6333
  const result = this.normalizer.normalizeMessage(message);
6029
6334
  if (result.turnStarted) {
6030
6335
  this.pendingInitialTurnRequestId = null;
@@ -6043,9 +6348,11 @@ var CodexDriver = class {
6043
6348
  const mode = opts?.mode || "busy";
6044
6349
  if (mode === "busy") {
6045
6350
  if (!this.normalizer.canSteerBusy) return null;
6351
+ const id2 = this.nextRequestId();
6352
+ this.pendingDeliveryRequests.set(id2, "turn/steer");
6046
6353
  return JSON.stringify({
6047
6354
  jsonrpc: "2.0",
6048
- id: this.nextRequestId(),
6355
+ id: id2,
6049
6356
  method: "turn/steer",
6050
6357
  params: {
6051
6358
  threadId: this.normalizer.threadId,
@@ -6054,9 +6361,11 @@ var CodexDriver = class {
6054
6361
  }
6055
6362
  });
6056
6363
  }
6364
+ const id = this.nextRequestId();
6365
+ this.pendingDeliveryRequests.set(id, "turn/start");
6057
6366
  return JSON.stringify({
6058
6367
  jsonrpc: "2.0",
6059
- id: this.nextRequestId(),
6368
+ id,
6060
6369
  method: "turn/start",
6061
6370
  params: {
6062
6371
  threadId: this.normalizer.threadId,
@@ -6112,9 +6421,11 @@ var CodexDriver = class {
6112
6421
  this.pendingThreadRequestId = id;
6113
6422
  this.pendingThreadRequestMethod = method;
6114
6423
  this.pendingResumeFallbackParams = null;
6424
+ this.pendingResumeThreadId = null;
6115
6425
  if (method === "thread/resume") {
6116
6426
  const { threadId: _threadId, ...freshParams } = params;
6117
6427
  this.pendingResumeFallbackParams = freshParams;
6428
+ this.pendingResumeThreadId = typeof _threadId === "string" && _threadId.trim() ? _threadId.trim() : null;
6118
6429
  }
6119
6430
  return id;
6120
6431
  }
@@ -6126,12 +6437,194 @@ var CodexDriver = class {
6126
6437
  }) + "\n");
6127
6438
  }
6128
6439
  async detectModels() {
6129
- return detectCodexModels();
6440
+ return await detectCodexModelsFromAppServer() ?? detectCodexModels(resolveCodexHomeRootFromEnv());
6130
6441
  }
6131
6442
  };
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");
6443
+ function asNonEmptyString(value) {
6444
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
6445
+ }
6446
+ function modelListPage(result) {
6447
+ if (!result || typeof result !== "object") return null;
6448
+ const object = result;
6449
+ const entries = Array.isArray(object.data) ? object.data : Array.isArray(object.models) ? object.models : null;
6450
+ if (!entries) return null;
6451
+ return {
6452
+ entries,
6453
+ nextCursor: asNonEmptyString(object.nextCursor)
6454
+ };
6455
+ }
6456
+ function modelListRequestParams(cursor) {
6457
+ return cursor ? { cursor } : {};
6458
+ }
6459
+ function stringArray(values, read) {
6460
+ const result = [];
6461
+ const seen = /* @__PURE__ */ new Set();
6462
+ for (const value of values) {
6463
+ const item = read(value);
6464
+ if (!item || seen.has(item)) continue;
6465
+ seen.add(item);
6466
+ result.push(item);
6467
+ }
6468
+ return result;
6469
+ }
6470
+ function codexReasoningEfforts(value) {
6471
+ if (!Array.isArray(value)) return [];
6472
+ return stringArray(value, (entry) => {
6473
+ if (typeof entry === "string") return asNonEmptyString(entry);
6474
+ if (!entry || typeof entry !== "object") return null;
6475
+ const object = entry;
6476
+ return asNonEmptyString(object.reasoningEffort) ?? asNonEmptyString(object.id);
6477
+ });
6478
+ }
6479
+ function codexServiceTierIds(value) {
6480
+ if (!Array.isArray(value)) return [];
6481
+ return stringArray(value, (entry) => {
6482
+ if (typeof entry === "string") return asNonEmptyString(entry);
6483
+ if (!entry || typeof entry !== "object") return null;
6484
+ const object = entry;
6485
+ return asNonEmptyString(object.id) ?? asNonEmptyString(object.serviceTier);
6486
+ });
6487
+ }
6488
+ function codexModelInfoFromAppServer(entry) {
6489
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
6490
+ const object = entry;
6491
+ if (object.hidden === true) return null;
6492
+ const id = asNonEmptyString(object.id) ?? asNonEmptyString(object.model) ?? asNonEmptyString(object.slug);
6493
+ if (!id) return null;
6494
+ const label = asNonEmptyString(object.displayName) ?? asNonEmptyString(object.display_name) ?? asNonEmptyString(object.label) ?? id;
6495
+ const supportedReasoningEfforts = codexReasoningEfforts(object.supportedReasoningEfforts);
6496
+ const serviceTiers = codexServiceTierIds(object.serviceTiers);
6497
+ const additionalSpeedTiers = codexServiceTierIds(object.additionalSpeedTiers);
6498
+ const runtimeServiceTiers = serviceTiers.length > 0 ? serviceTiers : additionalSpeedTiers;
6499
+ const defaultReasoningEffort = asNonEmptyString(object.defaultReasoningEffort);
6500
+ const defaultServiceTier = asNonEmptyString(object.defaultServiceTier);
6501
+ return {
6502
+ id,
6503
+ label,
6504
+ verified: "launchable",
6505
+ ...supportedReasoningEfforts.length > 0 ? { supportedReasoningEfforts } : {},
6506
+ ...defaultReasoningEffort ? { defaultReasoningEffort } : {},
6507
+ ...runtimeServiceTiers.length > 0 ? { serviceTiers: runtimeServiceTiers } : {},
6508
+ ...defaultServiceTier ? { defaultServiceTier } : {}
6509
+ };
6510
+ }
6511
+ function codexModelSetFromAppServerEntries(entries) {
6512
+ const models = [];
6513
+ let defaultModel;
6514
+ for (const entry of entries) {
6515
+ const model = codexModelInfoFromAppServer(entry);
6516
+ if (!model) continue;
6517
+ models.push(model);
6518
+ if (!defaultModel && typeof entry === "object" && entry && entry.isDefault === true) {
6519
+ defaultModel = model.id;
6520
+ }
6521
+ }
6522
+ return models.length > 0 ? { models, default: defaultModel } : null;
6523
+ }
6524
+ async function detectCodexModelsFromAppServer(options = {}) {
6525
+ const env = options.env ?? process.env;
6526
+ let launch;
6527
+ try {
6528
+ launch = resolveCodexSpawn(["app-server", "--listen", "stdio://"], { env });
6529
+ } catch {
6530
+ return null;
6531
+ }
6532
+ return await new Promise((resolve) => {
6533
+ const timeoutMs = options.timeoutMs ?? 5e3;
6534
+ const proc = spawn2(launch.command, launch.args, {
6535
+ cwd: options.cwd ?? process.cwd(),
6536
+ stdio: ["pipe", "pipe", "ignore"],
6537
+ env,
6538
+ shell: launch.shell
6539
+ });
6540
+ let settled = false;
6541
+ let buffer = "";
6542
+ let requestId = 0;
6543
+ let initializeRequestId = null;
6544
+ let modelListRequestId = null;
6545
+ let pageCount = 0;
6546
+ const entries = [];
6547
+ const finish = (result) => {
6548
+ if (settled) return;
6549
+ settled = true;
6550
+ clearTimeout(timer);
6551
+ proc.kill();
6552
+ resolve(result);
6553
+ };
6554
+ const timer = setTimeout(() => finish(null), timeoutMs);
6555
+ const sendRequest = (method, params) => {
6556
+ requestId += 1;
6557
+ const id = requestId;
6558
+ proc.stdin?.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n");
6559
+ return id;
6560
+ };
6561
+ const sendNotification = (method, params) => {
6562
+ proc.stdin?.write(JSON.stringify({ jsonrpc: "2.0", method, params }) + "\n");
6563
+ };
6564
+ const requestModelPage = (cursor) => {
6565
+ pageCount += 1;
6566
+ modelListRequestId = sendRequest("model/list", modelListRequestParams(cursor));
6567
+ };
6568
+ proc.once("error", () => finish(null));
6569
+ proc.once("exit", () => finish(null));
6570
+ proc.stdout?.on("data", (chunk) => {
6571
+ if (settled) return;
6572
+ buffer += chunk.toString();
6573
+ for (; ; ) {
6574
+ const newline = buffer.indexOf("\n");
6575
+ if (newline === -1) break;
6576
+ const line = buffer.slice(0, newline).trim();
6577
+ buffer = buffer.slice(newline + 1);
6578
+ if (!line) continue;
6579
+ const message = parseCodexJsonRpcLine(line);
6580
+ if (!message || !isJsonRpcResponse(message)) continue;
6581
+ if (message.id === initializeRequestId) {
6582
+ if (!hasJsonRpcField(message, "result") || !isCompatibleInitializeResult(message.result)) {
6583
+ finish(null);
6584
+ return;
6585
+ }
6586
+ sendNotification("initialized", {});
6587
+ requestModelPage(null);
6588
+ continue;
6589
+ }
6590
+ if (message.id === modelListRequestId) {
6591
+ if (!hasJsonRpcField(message, "result")) {
6592
+ finish(null);
6593
+ return;
6594
+ }
6595
+ const page = modelListPage(message.result);
6596
+ if (!page) {
6597
+ finish(null);
6598
+ return;
6599
+ }
6600
+ entries.push(...page.entries);
6601
+ if (page.nextCursor && pageCount < 5) {
6602
+ requestModelPage(page.nextCursor);
6603
+ continue;
6604
+ }
6605
+ finish(codexModelSetFromAppServerEntries(entries));
6606
+ return;
6607
+ }
6608
+ }
6609
+ });
6610
+ initializeRequestId = sendRequest("initialize", {
6611
+ clientInfo: { name: "slock-daemon", version: "1.0.0" },
6612
+ capabilities: { experimentalApi: true }
6613
+ });
6614
+ });
6615
+ }
6616
+ function detectCodexModels(home = resolveCodexHomeRootFromEnv()) {
6617
+ let cachePath = null;
6618
+ let configPath = null;
6619
+ for (const root of codexStateRootCandidates(home)) {
6620
+ const candidate = path7.join(root, "models_cache.json");
6621
+ if (existsSync5(candidate)) {
6622
+ cachePath = candidate;
6623
+ configPath = path7.join(root, "config.toml");
6624
+ break;
6625
+ }
6626
+ }
6627
+ if (!cachePath || !configPath) return null;
6135
6628
  let models = [];
6136
6629
  try {
6137
6630
  const raw = readFileSync2(cachePath, "utf8");
@@ -6601,7 +7094,7 @@ function runCursorModelsCommand() {
6601
7094
  // src/drivers/gemini.ts
6602
7095
  import { execFileSync as execFileSync3, spawn as spawn6 } from "child_process";
6603
7096
  import { existsSync as existsSync6 } from "fs";
6604
- import path7 from "path";
7097
+ import path8 from "path";
6605
7098
  async function buildGeminiSpawnEnv(ctx, platform = process.platform) {
6606
7099
  const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
6607
7100
  const launchEnvVars = runtimeConfigToLaunchFields(ctx.config).envVars;
@@ -6645,7 +7138,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
6645
7138
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
6646
7139
  const existsSyncFn = deps.existsSyncFn ?? existsSync6;
6647
7140
  const env = deps.env ?? process.env;
6648
- const winPath = path7.win32;
7141
+ const winPath = path8.win32;
6649
7142
  let geminiEntry = null;
6650
7143
  try {
6651
7144
  const globalRoot = normalizeExecOutput2(execFileSyncFn("npm", ["root", "-g"], {
@@ -6782,8 +7275,8 @@ var GeminiDriver = class {
6782
7275
  import { randomUUID as randomUUID2 } from "crypto";
6783
7276
  import { spawn as spawn7 } from "child_process";
6784
7277
  import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
6785
- import os4 from "os";
6786
- import path8 from "path";
7278
+ import os5 from "os";
7279
+ import path9 from "path";
6787
7280
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
6788
7281
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
6789
7282
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
@@ -6830,8 +7323,8 @@ var KimiDriver = class {
6830
7323
  this.sessionId = ctx.config.sessionId || randomUUID2();
6831
7324
  this.sessionAnnounced = false;
6832
7325
  this.promptRequestId = randomUUID2();
6833
- const systemPromptPath = path8.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
6834
- const agentFilePath = path8.join(ctx.workingDirectory, KIMI_AGENT_FILE);
7326
+ const systemPromptPath = path9.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
7327
+ const agentFilePath = path9.join(ctx.workingDirectory, KIMI_AGENT_FILE);
6835
7328
  if (!isResume || !existsSync7(systemPromptPath)) {
6836
7329
  writeFileSync3(systemPromptPath, ctx.prompt, "utf8");
6837
7330
  }
@@ -6977,8 +7470,8 @@ var KimiDriver = class {
6977
7470
  return detectKimiModels();
6978
7471
  }
6979
7472
  };
6980
- function detectKimiModels(home = os4.homedir()) {
6981
- const configPath = path8.join(home, ".kimi", "config.toml");
7473
+ function detectKimiModels(home = os5.homedir()) {
7474
+ const configPath = path9.join(home, ".kimi", "config.toml");
6982
7475
  let raw;
6983
7476
  try {
6984
7477
  raw = readFileSync3(configPath, "utf8");
@@ -7007,7 +7500,7 @@ function detectKimiModels(home = os4.homedir()) {
7007
7500
  import { randomUUID as randomUUID3 } from "crypto";
7008
7501
  import { EventEmitter } from "events";
7009
7502
  import { mkdirSync as mkdirSync3, readFileSync as readFileSync4 } from "fs";
7010
- import path9 from "path";
7503
+ import path10 from "path";
7011
7504
  import { createRequire as createRequire2 } from "module";
7012
7505
  import {
7013
7506
  createKimiHarness,
@@ -7032,7 +7525,7 @@ function createKimiSdkEventMappingState(sessionId = null) {
7032
7525
  };
7033
7526
  }
7034
7527
  function buildKimiSessionDir(workingDirectory) {
7035
- return path9.join(workingDirectory, KIMI_SESSION_DIR);
7528
+ return path10.join(workingDirectory, KIMI_SESSION_DIR);
7036
7529
  }
7037
7530
  function kimiErrorMessage(error) {
7038
7531
  if (typeof error === "string" && error.trim()) return error.trim();
@@ -7159,7 +7652,7 @@ async function createKimiAgentSessionForContext(ctx, sessionId) {
7159
7652
  const cliTransport = await prepareCliTransport(ctx, { NO_COLOR: "1" });
7160
7653
  const spawnEnv = cliTransport.spawnEnv;
7161
7654
  const wrapperPath = cliTransport.wrapperPath;
7162
- const homeDir = spawnEnv.KIMI_HOME || (process.env.HOME ? path9.join(process.env.HOME, ".kimi") : path9.join(ctx.workingDirectory, ".kimi"));
7655
+ const homeDir = spawnEnv.KIMI_HOME || (process.env.HOME ? path10.join(process.env.HOME, ".kimi") : path10.join(ctx.workingDirectory, ".kimi"));
7163
7656
  mkdirSync3(homeDir, { recursive: true });
7164
7657
  const harness = createKimiHarness({
7165
7658
  homeDir,
@@ -7384,7 +7877,7 @@ So instead of \`raft message send ...\`, run \`${this.wrapperPath} message send
7384
7877
  };
7385
7878
  function detectKimiSdkModels(home = resolveKimiHome(), ctx = {}) {
7386
7879
  const span = ctx.span;
7387
- const configPath = path9.join(home, "config.toml");
7880
+ const configPath = path10.join(home, "config.toml");
7388
7881
  const homeFromEnv = Boolean(process.env.KIMI_CODE_HOME);
7389
7882
  const emit2 = (outcome, extra = {}) => {
7390
7883
  span?.addEvent("daemon.kimi_sdk.models.config", {
@@ -7492,8 +7985,8 @@ var KimiSdkDriver = class {
7492
7985
  // src/drivers/opencode.ts
7493
7986
  import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
7494
7987
  import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
7495
- import os5 from "os";
7496
- import path10 from "path";
7988
+ import os6 from "os";
7989
+ import path11 from "path";
7497
7990
  var SLOCK_AGENT_NAME = "slock";
7498
7991
  var NO_MESSAGE_PROMPT = "No new messages are pending. Stop now.";
7499
7992
  var FIRST_MESSAGE_TASK_PREFIX = "First message task (system-triggered):";
@@ -7521,8 +8014,8 @@ function parseUserOpenCodeConfig(ctx) {
7521
8014
  const raw = runtimeConfigToLaunchFields(ctx.config).envVars?.OPENCODE_CONFIG_CONTENT;
7522
8015
  return parseOpenCodeConfigContent(raw);
7523
8016
  }
7524
- function readLocalOpenCodeConfig(home = os5.homedir()) {
7525
- const configPath = path10.join(home, ".config", "opencode", "opencode.json");
8017
+ function readLocalOpenCodeConfig(home = os6.homedir()) {
8018
+ const configPath = path11.join(home, ".config", "opencode", "opencode.json");
7526
8019
  try {
7527
8020
  return parseOpenCodeConfigContent(readFileSync5(configPath, "utf8"));
7528
8021
  } catch {
@@ -7582,7 +8075,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
7582
8075
  }
7583
8076
  };
7584
8077
  }
7585
- function buildOpenCodeConfig(ctx, home = os5.homedir()) {
8078
+ function buildOpenCodeConfig(ctx, home = os6.homedir()) {
7586
8079
  const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
7587
8080
  const userAgents = recordField(userConfig.agent);
7588
8081
  const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
@@ -7600,7 +8093,7 @@ function buildOpenCodeConfig(ctx, home = os5.homedir()) {
7600
8093
  mcp: recordField(userConfig.mcp)
7601
8094
  };
7602
8095
  }
7603
- async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = null) {
8096
+ async function buildOpenCodeLaunchOptions(ctx, home = os6.homedir(), version = null) {
7604
8097
  const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
7605
8098
  const config = buildOpenCodeConfig(ctx, home);
7606
8099
  const env = {
@@ -7699,7 +8192,7 @@ function formatOpenCodeLabelToken(token) {
7699
8192
  if (/^\d/.test(token)) return token;
7700
8193
  return normalized.charAt(0).toUpperCase() + normalized.slice(1);
7701
8194
  }
7702
- function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeModelsCommand) {
8195
+ function detectOpenCodeModels(home = os6.homedir(), runCommand = runOpenCodeModelsCommand) {
7703
8196
  const commandResult = runCommand(home);
7704
8197
  if (commandResult.error || commandResult.status !== 0) return null;
7705
8198
  return parseOpenCodeModelsOutput(commandResult.stdout);
@@ -7720,11 +8213,11 @@ function runOpenCodeModelsCommand(home, deps = {}) {
7720
8213
  };
7721
8214
  }
7722
8215
  function isWindowsCommandShim(commandPath) {
7723
- const ext = path10.win32.extname(commandPath).toLowerCase();
8216
+ const ext = path11.win32.extname(commandPath).toLowerCase();
7724
8217
  return ext === ".cmd" || ext === ".bat";
7725
8218
  }
7726
8219
  function opencodePackageEntryCandidates(packageRoot) {
7727
- const winPath = path10.win32;
8220
+ const winPath = path11.win32;
7728
8221
  return [
7729
8222
  winPath.join(packageRoot, "bin", "opencode.exe"),
7730
8223
  winPath.join(packageRoot, "bin", "opencode.js"),
@@ -7733,7 +8226,7 @@ function opencodePackageEntryCandidates(packageRoot) {
7733
8226
  ];
7734
8227
  }
7735
8228
  function openCodeSpecForEntry(entry, commandArgs) {
7736
- if (path10.win32.extname(entry).toLowerCase() === ".exe") {
8229
+ if (path11.win32.extname(entry).toLowerCase() === ".exe") {
7737
8230
  return { command: entry, args: commandArgs, shell: false };
7738
8231
  }
7739
8232
  return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
@@ -7742,7 +8235,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
7742
8235
  const existsSyncFn = deps.existsSyncFn ?? existsSync8;
7743
8236
  const execFileSyncFn = deps.execFileSyncFn;
7744
8237
  const env = deps.env ?? process.env;
7745
- const winPath = path10.win32;
8238
+ const winPath = path11.win32;
7746
8239
  const candidates = [];
7747
8240
  if (execFileSyncFn) {
7748
8241
  try {
@@ -7770,7 +8263,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
7770
8263
  function extractWindowsShimTargets(commandPath, deps = {}) {
7771
8264
  if (!isWindowsCommandShim(commandPath)) return [];
7772
8265
  const readFileSyncFn = deps.readFileSyncFn ?? readFileSync5;
7773
- const commandDir = path10.win32.dirname(commandPath);
8266
+ const commandDir = path11.win32.dirname(commandPath);
7774
8267
  let raw;
7775
8268
  try {
7776
8269
  raw = String(readFileSyncFn(commandPath, "utf8"));
@@ -7781,7 +8274,7 @@ function extractWindowsShimTargets(commandPath, deps = {}) {
7781
8274
  const dp0Pattern = /%~dp0\\?([^"\r\n]*?opencode\.(?:exe|js|mjs|cjs))/gi;
7782
8275
  for (const match of raw.matchAll(dp0Pattern)) {
7783
8276
  const relative = match[1]?.replace(/^\\+/, "");
7784
- if (relative) candidates.push(path10.win32.normalize(path10.win32.join(commandDir, relative)));
8277
+ if (relative) candidates.push(path11.win32.normalize(path11.win32.join(commandDir, relative)));
7785
8278
  }
7786
8279
  return candidates;
7787
8280
  }
@@ -7795,7 +8288,7 @@ function resolveOpenCodeSpawn(commandArgs, deps = {}) {
7795
8288
  };
7796
8289
  }
7797
8290
  const command = resolveCommandOnPath("opencode", deps);
7798
- if (command && path10.win32.extname(command).toLowerCase() === ".exe") {
8291
+ if (command && path11.win32.extname(command).toLowerCase() === ".exe") {
7799
8292
  return { command, args: commandArgs, shell: false };
7800
8293
  }
7801
8294
  const packageEntry = resolveWindowsOpenCodePackageEntry(command, deps);
@@ -7900,7 +8393,7 @@ var OpenCodeDriver = class {
7900
8393
  if (unsupportedMessage) {
7901
8394
  throw new Error(unsupportedMessage);
7902
8395
  }
7903
- const launch = await buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
8396
+ const launch = await buildOpenCodeLaunchOptions(ctx, os6.homedir(), version);
7904
8397
  const spawnSpec = resolveOpenCodeSpawn(launch.args);
7905
8398
  const proc = spawn8(spawnSpec.command, spawnSpec.args, {
7906
8399
  cwd: ctx.workingDirectory,
@@ -7968,7 +8461,7 @@ var OpenCodeDriver = class {
7968
8461
  import { randomUUID as randomUUID4 } from "crypto";
7969
8462
  import { EventEmitter as EventEmitter2 } from "events";
7970
8463
  import { mkdirSync as mkdirSync4, readdirSync as readdirSync2 } from "fs";
7971
- import path11 from "path";
8464
+ import path12 from "path";
7972
8465
  import {
7973
8466
  AuthStorage,
7974
8467
  createBashTool,
@@ -7995,7 +8488,7 @@ function createPiSdkEventMappingState(sessionId = null) {
7995
8488
  };
7996
8489
  }
7997
8490
  function buildPiSessionDir(workingDirectory) {
7998
- return path11.join(workingDirectory, PI_SESSION_DIR);
8491
+ return path12.join(workingDirectory, PI_SESSION_DIR);
7999
8492
  }
8000
8493
  async function buildPiSpawnEnv(ctx) {
8001
8494
  return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
@@ -8017,7 +8510,7 @@ function findPiSessionFile(sessionDir, sessionId) {
8017
8510
  }
8018
8511
  const suffix = `_${sessionId}.jsonl`;
8019
8512
  const match = entries.find((entry) => entry.endsWith(suffix));
8020
- return match ? path11.join(sessionDir, match) : null;
8513
+ return match ? path12.join(sessionDir, match) : null;
8021
8514
  }
8022
8515
  function detectPiModelsFromRegistry(modelRegistry) {
8023
8516
  const models = [];
@@ -8253,7 +8746,7 @@ async function createPiAgentSessionForContext(ctx, sessionId) {
8253
8746
  try {
8254
8747
  const spawnEnv = await buildPiSpawnEnv(ctx);
8255
8748
  const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
8256
- const authStorage = AuthStorage.create(path11.join(agentDir, "auth.json"));
8749
+ const authStorage = AuthStorage.create(path12.join(agentDir, "auth.json"));
8257
8750
  const settingsManager = SettingsManager.create(ctx.workingDirectory, agentDir);
8258
8751
  const services = await createAgentSessionServices({
8259
8752
  cwd: ctx.workingDirectory,
@@ -8652,6 +9145,9 @@ var ChildProcessRuntimeSession = class {
8652
9145
  get currentSessionId() {
8653
9146
  return this.driver.currentSessionId;
8654
9147
  }
9148
+ get currentRuntimeHomeDir() {
9149
+ return this.driver.currentRuntimeHomeDir;
9150
+ }
8655
9151
  get exitCode() {
8656
9152
  return this.process?.exitCode ?? null;
8657
9153
  }
@@ -8775,7 +9271,7 @@ function getDriver(runtimeId) {
8775
9271
 
8776
9272
  // src/workspaces.ts
8777
9273
  import { readdir, rm, stat } from "fs/promises";
8778
- import path12 from "path";
9274
+ import path13 from "path";
8779
9275
  function isValidWorkspaceDirectoryName(directoryName) {
8780
9276
  return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
8781
9277
  }
@@ -8783,7 +9279,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
8783
9279
  if (!isValidWorkspaceDirectoryName(directoryName)) {
8784
9280
  return null;
8785
9281
  }
8786
- return path12.join(dataDir, directoryName);
9282
+ return path13.join(dataDir, directoryName);
8787
9283
  }
8788
9284
  function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
8789
9285
  return {
@@ -8832,7 +9328,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
8832
9328
  return summary;
8833
9329
  }
8834
9330
  const childSummaries = await Promise.all(
8835
- entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
9331
+ entries.map((entry) => summarizeWorkspaceEntry(path13.join(dirPath, entry.name), entry))
8836
9332
  );
8837
9333
  for (const childSummary of childSummaries) {
8838
9334
  summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
@@ -8851,7 +9347,7 @@ async function scanWorkspaceDirectories(dataDir) {
8851
9347
  if (!entry.isDirectory()) {
8852
9348
  return null;
8853
9349
  }
8854
- const dirPath = path12.join(dataDir, entry.name);
9350
+ const dirPath = path13.join(dataDir, entry.name);
8855
9351
  try {
8856
9352
  const summary = await summarizeWorkspaceDirectory(dirPath);
8857
9353
  return {
@@ -9400,17 +9896,17 @@ function allowedTranscriptRootsForRuntime(runtime, homeDir, workspaceDir) {
9400
9896
  const roots = [workspaceDir];
9401
9897
  switch (runtime) {
9402
9898
  case "claude":
9403
- roots.push(path13.join(homeDir, ".claude"));
9899
+ roots.push(path14.join(homeDir, ".claude"));
9404
9900
  break;
9405
9901
  case "codex":
9406
- roots.push(path13.join(homeDir, ".codex"));
9902
+ roots.push(homeDir, path14.join(homeDir, ".codex"));
9407
9903
  break;
9408
9904
  case "kimi":
9409
9905
  case "kimi-sdk":
9410
- roots.push(path13.join(homeDir, ".kimi"));
9906
+ roots.push(path14.join(homeDir, ".kimi"));
9411
9907
  break;
9412
9908
  case "pi":
9413
- roots.push(path13.join(homeDir, ".pi"), path13.join(homeDir, ".pi", "agent"));
9909
+ roots.push(path14.join(homeDir, ".pi"), path14.join(homeDir, ".pi", "agent"));
9414
9910
  break;
9415
9911
  }
9416
9912
  return roots;
@@ -9421,8 +9917,8 @@ async function isPathWithinAllowedRoots(filePath, roots) {
9421
9917
  for (const root of roots) {
9422
9918
  const realRoot = await realpath(root).catch(() => null);
9423
9919
  if (!realRoot) continue;
9424
- const rel = path13.relative(realRoot, real);
9425
- if (!rel.startsWith("..") && !path13.isAbsolute(rel)) return true;
9920
+ const rel = path14.relative(realRoot, real);
9921
+ if (!rel.startsWith("..") && !path14.isAbsolute(rel)) return true;
9426
9922
  }
9427
9923
  return false;
9428
9924
  }
@@ -9469,12 +9965,12 @@ function findSessionJsonl(root, predicate) {
9469
9965
  for (const entry of entries) {
9470
9966
  if (++visited > maxEntries) return null;
9471
9967
  if (!entry.isFile() || !predicate(entry.name)) continue;
9472
- return path13.join(dir, entry.name);
9968
+ return path14.join(dir, entry.name);
9473
9969
  }
9474
9970
  for (const entry of entries) {
9475
9971
  if (++visited > maxEntries) return null;
9476
9972
  if (!entry.isDirectory()) continue;
9477
- const found = visit(path13.join(dir, entry.name), depth - 1);
9973
+ const found = visit(path14.join(dir, entry.name), depth - 1);
9478
9974
  if (found) return found;
9479
9975
  }
9480
9976
  return null;
@@ -9482,7 +9978,7 @@ function findSessionJsonl(root, predicate) {
9482
9978
  return visit(root, maxDepth);
9483
9979
  }
9484
9980
  function findKimiSdkSessionDir(sessionId, agentId, homeDir) {
9485
- const indexPath = path13.join(homeDir, ".kimi", "session_index.jsonl");
9981
+ const indexPath = path14.join(homeDir, ".kimi", "session_index.jsonl");
9486
9982
  try {
9487
9983
  const index = readFileSync6(indexPath, "utf8");
9488
9984
  for (const line of index.split("\n")) {
@@ -9497,12 +9993,12 @@ function findKimiSdkSessionDir(sessionId, agentId, homeDir) {
9497
9993
  }
9498
9994
  } catch {
9499
9995
  }
9500
- const sessionsRoot = path13.join(homeDir, ".kimi", "sessions");
9996
+ const sessionsRoot = path14.join(homeDir, ".kimi", "sessions");
9501
9997
  try {
9502
9998
  const prefix = agentId ? `wd_${agentId}_` : `wd_`;
9503
9999
  for (const entry of readdirSync3(sessionsRoot, { withFileTypes: true })) {
9504
10000
  if (!entry.isDirectory() || !entry.name.startsWith(prefix)) continue;
9505
- const candidate = path13.join(sessionsRoot, entry.name, `session_${sessionId}`);
10001
+ const candidate = path14.join(sessionsRoot, entry.name, `session_${sessionId}`);
9506
10002
  if (existsSync9(candidate)) return candidate;
9507
10003
  }
9508
10004
  } catch {
@@ -9511,16 +10007,16 @@ function findKimiSdkSessionDir(sessionId, agentId, homeDir) {
9511
10007
  }
9512
10008
  function findPiSessionFile2(sessionId, workingDirectory, homeDir) {
9513
10009
  if (workingDirectory) {
9514
- const piSessionsDir = path13.join(workingDirectory, ".pi-sessions");
10010
+ const piSessionsDir = path14.join(workingDirectory, ".pi-sessions");
9515
10011
  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);
10012
+ 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
10013
  if (files[0]) return files[0].path;
9518
10014
  } catch {
9519
10015
  }
9520
10016
  }
9521
10017
  const legacyRoots = [
9522
- path13.join(homeDir, ".pi", "agent"),
9523
- path13.join(homeDir, ".pi")
10018
+ path14.join(homeDir, ".pi", "agent"),
10019
+ path14.join(homeDir, ".pi")
9524
10020
  ];
9525
10021
  for (const root of legacyRoots) {
9526
10022
  const found = findSessionJsonl(root, (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
@@ -9534,9 +10030,9 @@ function safeSessionFilename(value) {
9534
10030
  }
9535
10031
  function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
9536
10032
  try {
9537
- const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
10033
+ const dir = path14.join(fallbackDir, ".slock", "runtime-sessions");
9538
10034
  mkdirSync5(dir, { recursive: true });
9539
- const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
10035
+ const filePath = path14.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
9540
10036
  writeFileSync4(filePath, JSON.stringify({
9541
10037
  type: "runtime_session_handoff",
9542
10038
  runtime,
@@ -9555,27 +10051,40 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
9555
10051
  return null;
9556
10052
  }
9557
10053
  }
9558
- function resolveRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
10054
+ function resolveRuntimeHomeDir(config, defaultHomeDir, workspacePath, opts = {}) {
9559
10055
  if (isClaudeCustomProviderConfig(config)) {
9560
10056
  return getClaudeProviderStatePaths(workspacePath).home;
9561
10057
  }
10058
+ if (config.runtime === "codex") {
10059
+ return resolveCodexHomeRootFromConfig(config, defaultHomeDir, workspacePath, process.env, opts);
10060
+ }
9562
10061
  return defaultHomeDir;
9563
10062
  }
9564
- function ensureRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
10063
+ function ensureRuntimeHomeDir(config, defaultHomeDir, workspacePath, opts = {}) {
9565
10064
  if (isClaudeCustomProviderConfig(config)) {
9566
10065
  return ensureClaudeProviderStatePaths(workspacePath).home;
9567
10066
  }
10067
+ if (config.runtime === "codex") {
10068
+ const home = resolveCodexHomeRootFromConfig(config, defaultHomeDir, workspacePath, process.env, opts);
10069
+ if (opts.agentId) {
10070
+ mkdirSync5(home, { recursive: true });
10071
+ }
10072
+ return home;
10073
+ }
9568
10074
  return defaultHomeDir;
9569
10075
  }
9570
- function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir, opts) {
10076
+ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os7.homedir(), fallbackDir, opts) {
9571
10077
  let resolvedPath = null;
9572
10078
  let lookupMethod = "none";
9573
10079
  if (runtime === "claude") {
9574
10080
  lookupMethod = "claude_jsonl";
9575
- resolvedPath = findSessionJsonl(path13.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`);
10081
+ resolvedPath = findSessionJsonl(path14.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`);
9576
10082
  } else if (runtime === "codex") {
9577
10083
  lookupMethod = "codex_jsonl";
9578
- resolvedPath = findSessionJsonl(path13.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
10084
+ for (const root of codexSessionRootCandidates(homeDir)) {
10085
+ resolvedPath = findSessionJsonl(root, (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
10086
+ if (resolvedPath) break;
10087
+ }
9579
10088
  } else if (runtime === "kimi-sdk" || runtime === "kimi") {
9580
10089
  lookupMethod = "kimi_sdk_index";
9581
10090
  resolvedPath = findKimiSdkSessionDir(sessionId, opts?.agentId, homeDir);
@@ -10312,6 +10821,28 @@ function runtimeDiagnosticTraceAttrs(event) {
10312
10821
  session_id_present: Boolean(event.sessionId)
10313
10822
  };
10314
10823
  }
10824
+ function runtimeRecoveryTrajectoryEntry(event) {
10825
+ const lines = [event.message];
10826
+ if (event.details && event.details !== event.message) {
10827
+ lines.push(event.details);
10828
+ }
10829
+ return {
10830
+ kind: "system",
10831
+ title: "Codex resume recovery",
10832
+ text: lines.join("\n")
10833
+ };
10834
+ }
10835
+ function runtimeRecoveryTraceAttrs(event) {
10836
+ return {
10837
+ kind: event.kind,
10838
+ source: event.source,
10839
+ resume_error_class: event.resumeErrorClass,
10840
+ recovery_action: event.recoveryAction,
10841
+ requested_session_id_present: Boolean(event.requestedSessionId),
10842
+ message_present: Boolean(event.message),
10843
+ details_present: Boolean(event.details)
10844
+ };
10845
+ }
10315
10846
  function currentErrorCandidates(ap) {
10316
10847
  return [
10317
10848
  ap.runtimeErrorSinceProgress ? ap.lastRuntimeError : null,
@@ -10635,6 +11166,7 @@ var AgentProcessManager = class _AgentProcessManager {
10635
11166
  sendToServer;
10636
11167
  daemonApiKey;
10637
11168
  serverUrl;
11169
+ slockHome;
10638
11170
  dataDir;
10639
11171
  runtimeSessionHomeDir;
10640
11172
  driverResolver;
@@ -10664,8 +11196,9 @@ var AgentProcessManager = class _AgentProcessManager {
10664
11196
  this.sendToServer = sendToServer;
10665
11197
  this.daemonApiKey = daemonApiKey;
10666
11198
  this.serverUrl = opts.serverUrl;
10667
- this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
10668
- this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os6.homedir();
11199
+ this.slockHome = opts.slockHome ? path14.resolve(opts.slockHome) : resolveSlockHome();
11200
+ this.dataDir = opts.dataDir || resolveSlockHomePath("agents", this.slockHome);
11201
+ this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os7.homedir();
10669
11202
  this.driverResolver = opts.driverResolver || getDriver;
10670
11203
  this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
10671
11204
  this.tracer = opts.tracer ?? noopTracer;
@@ -10794,6 +11327,70 @@ var AgentProcessManager = class _AgentProcessManager {
10794
11327
  this.sendStdinNotification(agentId);
10795
11328
  }, delayMs);
10796
11329
  }
11330
+ flushAsyncRejectedIdleDelivery(agentId) {
11331
+ const ap = this.agents.get(agentId);
11332
+ if (!ap) return false;
11333
+ const count = ap.notifications.takePendingAndClearTimer();
11334
+ if (count === 0) return false;
11335
+ if (!ap.driver.supportsStdinNotification || !ap.sessionId || ap.inbox.length === 0) {
11336
+ ap.notifications.add(count);
11337
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
11338
+ agentId,
11339
+ runtime: ap.config.runtime,
11340
+ model: ap.config.model,
11341
+ launchId: ap.launchId || void 0,
11342
+ mode: "idle",
11343
+ outcome: "queued_without_stdin",
11344
+ inbox_count: ap.inbox.length,
11345
+ pending_notification_count: ap.notifications.pendingCount,
11346
+ session_id_present: Boolean(ap.sessionId),
11347
+ supports_stdin_notification: ap.driver.supportsStdinNotification
11348
+ });
11349
+ return false;
11350
+ }
11351
+ if (!this.isApmIdle(ap)) {
11352
+ ap.notifications.add(count);
11353
+ return this.sendStdinNotification(agentId, { forceUnsupportedRetry: true });
11354
+ }
11355
+ ap.notifications.pruneContributedToPending(ap.inbox, ap.sessionId);
11356
+ const messages = ap.notifications.filterUncontributedMessages(ap.inbox, ap.sessionId);
11357
+ if (messages.length === 0) {
11358
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
11359
+ agentId,
11360
+ runtime: ap.config.runtime,
11361
+ model: ap.config.model,
11362
+ launchId: ap.launchId || void 0,
11363
+ mode: "idle",
11364
+ outcome: "suppressed_already_contributed",
11365
+ inbox_count: ap.inbox.length,
11366
+ pending_notification_count: count,
11367
+ session_id_present: true
11368
+ });
11369
+ return false;
11370
+ }
11371
+ this.commitApmIdleState(agentId, ap, false);
11372
+ this.startRuntimeTrace(agentId, ap, "async-rejected-idle-delivery-retry", messages);
11373
+ this.broadcastActivity(agentId, "working", "Message received");
11374
+ const accepted = this.deliverInboxUpdateViaStdin(
11375
+ agentId,
11376
+ ap,
11377
+ messages,
11378
+ "idle",
11379
+ "async_rejected_idle_delivery_retry"
11380
+ );
11381
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
11382
+ agentId,
11383
+ runtime: ap.config.runtime,
11384
+ model: ap.config.model,
11385
+ launchId: ap.launchId || void 0,
11386
+ mode: "idle",
11387
+ outcome: accepted ? "written" : "not_written",
11388
+ inbox_count: ap.inbox.length,
11389
+ messages_count: messages.length,
11390
+ session_id_present: true
11391
+ });
11392
+ return accepted;
11393
+ }
10797
11394
  isApmIdle(ap) {
10798
11395
  return ap.gatedSteering.isIdle;
10799
11396
  }
@@ -11252,6 +11849,23 @@ var AgentProcessManager = class _AgentProcessManager {
11252
11849
  ...runtimeDiagnosticTraceAttrs(event)
11253
11850
  });
11254
11851
  }
11852
+ recordRuntimeRecoveryActivity(agentId, ap, event) {
11853
+ this.sendToServer({
11854
+ type: "agent:activity",
11855
+ agentId,
11856
+ activity: ap.lastActivity || "online",
11857
+ detail: ap.lastActivityDetail || "",
11858
+ entries: [runtimeRecoveryTrajectoryEntry(event)],
11859
+ launchId: ap.launchId || void 0,
11860
+ clientSeq: this.nextActivityClientSeq(agentId)
11861
+ });
11862
+ this.recordDaemonTrace("daemon.runtime.recovery.visible", {
11863
+ agentId,
11864
+ launchId: ap.launchId || void 0,
11865
+ runtime: ap.config.runtime,
11866
+ ...runtimeRecoveryTraceAttrs(event)
11867
+ });
11868
+ }
11255
11869
  recordDaemonTrace(name, attrs, status = "ok", parentTraceparent) {
11256
11870
  const span = this.tracer.startSpan(name, {
11257
11871
  parent: parseTraceparent(parentTraceparent),
@@ -11589,26 +12203,26 @@ var AgentProcessManager = class _AgentProcessManager {
11589
12203
  let pendingStartRebind;
11590
12204
  const originalLaunchId = launchId || null;
11591
12205
  try {
11592
- const agentDataDir = path13.join(this.dataDir, agentId);
12206
+ const agentDataDir = path14.join(this.dataDir, agentId);
11593
12207
  await mkdir(agentDataDir, { recursive: true });
11594
12208
  const initialRuntimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
11595
- const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
12209
+ const memoryMdPath = path14.join(agentDataDir, "MEMORY.md");
11596
12210
  try {
11597
12211
  await access(memoryMdPath);
11598
12212
  } catch {
11599
12213
  const initialMemoryMd = buildInitialMemoryMd(initialRuntimeConfig);
11600
12214
  await writeFile(memoryMdPath, initialMemoryMd);
11601
12215
  }
11602
- const notesDir = path13.join(agentDataDir, "notes");
12216
+ const notesDir = path14.join(agentDataDir, "notes");
11603
12217
  await mkdir(notesDir, { recursive: true });
11604
12218
  if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
11605
12219
  const seedFiles = buildOnboardingSeedFiles();
11606
12220
  for (const { relativePath, content } of seedFiles) {
11607
- const fullPath = path13.join(agentDataDir, relativePath);
12221
+ const fullPath = path14.join(agentDataDir, relativePath);
11608
12222
  try {
11609
12223
  await access(fullPath);
11610
12224
  } catch {
11611
- await mkdir(path13.dirname(fullPath), { recursive: true });
12225
+ await mkdir(path14.dirname(fullPath), { recursive: true });
11612
12226
  await writeFile(fullPath, content);
11613
12227
  }
11614
12228
  }
@@ -11762,6 +12376,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
11762
12376
  workingDirectory: agentDataDir,
11763
12377
  slockCliPath: this.slockCliPath,
11764
12378
  daemonApiKey: this.daemonApiKey,
12379
+ slockHome: this.slockHome,
11765
12380
  launchId: effectiveLaunchId,
11766
12381
  agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId),
11767
12382
  cliTransportTraceDir: this.cliTransportTraceDir,
@@ -12921,7 +13536,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12921
13536
  return true;
12922
13537
  }
12923
13538
  async resetWorkspace(agentId) {
12924
- const agentDataDir = path13.join(this.dataDir, agentId);
13539
+ const agentDataDir = path14.join(this.dataDir, agentId);
12925
13540
  try {
12926
13541
  await rm2(agentDataDir, { recursive: true, force: true });
12927
13542
  logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
@@ -12981,9 +13596,9 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12981
13596
  }
12982
13597
  return result;
12983
13598
  }
12984
- buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
12985
- const workspacePath = path13.join(this.dataDir, agentId);
12986
- const runtimeHomeDir = resolveRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspacePath);
13599
+ buildRuntimeProfileReport(agentId, config, sessionId, launchId, observedRuntimeHomeDir) {
13600
+ const workspacePath = path14.join(this.dataDir, agentId);
13601
+ const runtimeHomeDir = observedRuntimeHomeDir || resolveRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspacePath, { agentId, slockHome: this.slockHome });
12987
13602
  return {
12988
13603
  agentId,
12989
13604
  launchId,
@@ -13004,7 +13619,13 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13004
13619
  getAgentRuntimeProfileReport(agentId) {
13005
13620
  const running = this.agents.get(agentId);
13006
13621
  if (running) {
13007
- return this.buildRuntimeProfileReport(agentId, running.config, running.sessionId, running.launchId);
13622
+ return this.buildRuntimeProfileReport(
13623
+ agentId,
13624
+ running.config,
13625
+ running.sessionId,
13626
+ running.launchId,
13627
+ running.runtime.currentRuntimeHomeDir
13628
+ );
13008
13629
  }
13009
13630
  const idle = this.idleAgentConfigs.get(agentId);
13010
13631
  if (idle) {
@@ -13277,7 +13898,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13277
13898
  }
13278
13899
  // Workspace file browsing
13279
13900
  async getFileTree(agentId, dirPath) {
13280
- const agentDir = path13.join(this.dataDir, agentId);
13901
+ const agentDir = path14.join(this.dataDir, agentId);
13281
13902
  try {
13282
13903
  await stat2(agentDir);
13283
13904
  } catch {
@@ -13285,8 +13906,8 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13285
13906
  }
13286
13907
  let targetDir = agentDir;
13287
13908
  if (dirPath) {
13288
- const resolved = path13.resolve(agentDir, dirPath);
13289
- if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
13909
+ const resolved = path14.resolve(agentDir, dirPath);
13910
+ if (!resolved.startsWith(agentDir + path14.sep) && resolved !== agentDir) {
13290
13911
  return [];
13291
13912
  }
13292
13913
  targetDir = resolved;
@@ -13294,14 +13915,14 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13294
13915
  return this.listDirectoryChildren(targetDir, agentDir);
13295
13916
  }
13296
13917
  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) {
13918
+ const agentDir = path14.join(this.dataDir, agentId);
13919
+ const resolved = path14.resolve(agentDir, filePath);
13920
+ if (!resolved.startsWith(agentDir + path14.sep) && resolved !== agentDir) {
13300
13921
  throw new Error("Access denied");
13301
13922
  }
13302
13923
  const info = await stat2(resolved);
13303
13924
  if (info.isDirectory()) throw new Error("Cannot read a directory");
13304
- const ext = path13.extname(resolved).toLowerCase();
13925
+ const ext = path14.extname(resolved).toLowerCase();
13305
13926
  if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
13306
13927
  if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
13307
13928
  const content = await readFile(resolved, "utf-8");
@@ -13352,8 +13973,8 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13352
13973
  };
13353
13974
  }
13354
13975
  const runtime = config.runtime;
13355
- const workspaceDir = path13.join(this.dataDir, agentId);
13356
- const homeDir = ensureRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspaceDir);
13976
+ const workspaceDir = path14.join(this.dataDir, agentId);
13977
+ const homeDir = agent?.runtime.currentRuntimeHomeDir || ensureRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspaceDir, { agentId, slockHome: this.slockHome });
13357
13978
  if (!actualSessionId) {
13358
13979
  return {
13359
13980
  runtime,
@@ -13391,7 +14012,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13391
14012
  let redacted = false;
13392
14013
  if (ref.reachable && ref.path) {
13393
14014
  try {
13394
- const resolved = path13.resolve(ref.path);
14015
+ const resolved = path14.resolve(ref.path);
13395
14016
  const allowedRoots = allowedTranscriptRootsForRuntime(runtime, homeDir, workspaceDir);
13396
14017
  if (!await isPathWithinAllowedRoots(resolved, allowedRoots)) {
13397
14018
  throw new Error("resolved session path is outside allowed runtime directories");
@@ -13402,7 +14023,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13402
14023
  throw new Error("symbolic links are not allowed");
13403
14024
  }
13404
14025
  if (info.isDirectory()) {
13405
- targetPath = path13.join(resolved, "state.json");
14026
+ targetPath = path14.join(resolved, "state.json");
13406
14027
  }
13407
14028
  if (!await isPathWithinAllowedRoots(targetPath, allowedRoots)) {
13408
14029
  throw new Error("resolved session state path is outside allowed runtime directories");
@@ -13514,14 +14135,22 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13514
14135
  const idle = this.idleAgentConfigs.get(agentId);
13515
14136
  const config = agent?.config ?? idle?.config ?? null;
13516
14137
  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();
14138
+ const workspaceDir = path14.join(this.dataDir, agentId);
14139
+ const hostHome = os7.homedir();
14140
+ const home = agent?.runtime.currentRuntimeHomeDir || (config ? ensureRuntimeHomeDir(config, hostHome, workspaceDir, { agentId, slockHome: this.slockHome }) : runtime === "codex" ? resolveCodexHomeRootFromEnv(process.env, { defaultHomeDir: hostHome, cwd: workspaceDir }) : hostHome);
13519
14141
  const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
14142
+ const globalDirs = runtime === "codex" ? [
14143
+ path14.join(home, "skills"),
14144
+ path14.join(home, "skills", ".system"),
14145
+ path14.join(home, ".agents", "skills"),
14146
+ ...hasConfiguredCodexHome(config) ? [] : [path14.join(hostHome, ".agents", "skills")]
14147
+ ] : paths.global.map((p) => path14.join(home, p));
14148
+ const workspaceDirs = paths.workspace.map((p) => path14.join(workspaceDir, p));
13520
14149
  const globalResults = await Promise.all(
13521
- paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
14150
+ globalDirs.map((dir) => this.scanSkillsDir(dir))
13522
14151
  );
13523
14152
  const workspaceResults = await Promise.all(
13524
- paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
14153
+ workspaceDirs.map((dir) => this.scanSkillsDir(dir))
13525
14154
  );
13526
14155
  const dedup = (skills) => {
13527
14156
  const seen = /* @__PURE__ */ new Set();
@@ -13550,7 +14179,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13550
14179
  const skills = [];
13551
14180
  for (const entry of entries) {
13552
14181
  if (entry.isDirectory() || entry.isSymbolicLink()) {
13553
- const skillMd = path13.join(dir, entry.name, "SKILL.md");
14182
+ const skillMd = path14.join(dir, entry.name, "SKILL.md");
13554
14183
  try {
13555
14184
  const content = await readFile(skillMd, "utf-8");
13556
14185
  const skill = this.parseSkillMd(entry.name, content);
@@ -13561,7 +14190,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13561
14190
  } else if (entry.name.endsWith(".md")) {
13562
14191
  const cmdName = entry.name.replace(/\.md$/, "");
13563
14192
  try {
13564
- const content = await readFile(path13.join(dir, entry.name), "utf-8");
14193
+ const content = await readFile(path14.join(dir, entry.name), "utf-8");
13565
14194
  const skill = this.parseSkillMd(cmdName, content);
13566
14195
  skill.sourcePath = dir;
13567
14196
  skills.push(skill);
@@ -13982,6 +14611,43 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13982
14611
  recordRuntimeTraceEvent(agentId, ap, name, attrs) {
13983
14612
  this.startRuntimeTrace(agentId, ap, "runtime-progress").addEvent(name, attrs);
13984
14613
  }
14614
+ restoreRuntimeDeliveryAfterAsyncRejection(agentId, ap, event) {
14615
+ const pendingBefore = ap.notifications.pendingCount;
14616
+ const restoredMessages = ap.inbox.filter(
14617
+ (message) => ap.notifications.hasContributedMessage(message, ap.sessionId)
14618
+ );
14619
+ ap.notifications.clearNoticeFingerprint();
14620
+ const restoredNotificationCount = ap.driver.supportsStdinNotification && ap.sessionId && restoredMessages.length > 0 ? ap.notifications.add(restoredMessages.length) : 0;
14621
+ if (event.requestMethod === "turn/start") {
14622
+ this.commitApmIdleState(agentId, ap, true);
14623
+ }
14624
+ const idleRetryScheduled = event.requestMethod === "turn/start" && restoredNotificationCount > 0 ? ap.notifications.schedule(() => {
14625
+ this.flushAsyncRejectedIdleDelivery(agentId);
14626
+ }, this.stdinNotificationRetryMs) : false;
14627
+ const attrs = {
14628
+ request_method: event.requestMethod,
14629
+ source: event.source,
14630
+ payloadBytes: event.payloadBytes,
14631
+ inbox_count: ap.inbox.length,
14632
+ restored_messages_count: restoredMessages.length,
14633
+ pending_notification_count_before: pendingBefore,
14634
+ pending_notification_count_after: ap.notifications.pendingCount,
14635
+ restored_notification_count: restoredNotificationCount,
14636
+ session_id_present: Boolean(ap.sessionId),
14637
+ supports_stdin_notification: ap.driver.supportsStdinNotification,
14638
+ busy_delivery_mode: ap.driver.busyDeliveryMode,
14639
+ restored_idle_state: event.requestMethod === "turn/start",
14640
+ idle_retry_scheduled: idleRetryScheduled
14641
+ };
14642
+ this.recordRuntimeTraceEvent(agentId, ap, "runtime.delivery.async_rejected", attrs);
14643
+ this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected", {
14644
+ agentId,
14645
+ launchId: ap.launchId || void 0,
14646
+ runtime: ap.config.runtime,
14647
+ model: ap.config.model,
14648
+ ...attrs
14649
+ }, "error");
14650
+ }
13985
14651
  noteRuntimeProgress(ap, eventKind) {
13986
14652
  ap.runtimeProgress.noteRuntimeEvent(eventKind);
13987
14653
  this.invalidateRecoveryErrorView(ap);
@@ -14170,7 +14836,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14170
14836
  if ((ap.driver.startupReadiness ?? "first_event") !== "initial_turn") {
14171
14837
  return true;
14172
14838
  }
14173
- return event.kind !== "session_init" && event.kind !== "internal_progress" && event.kind !== "runtime_diagnostic";
14839
+ return event.kind !== "session_init" && event.kind !== "internal_progress" && event.kind !== "runtime_diagnostic" && event.kind !== "runtime_recovery";
14174
14840
  }
14175
14841
  handleRuntimeStartupTimeout(agentId, ap, timeoutMs) {
14176
14842
  const current = this.agents.get(agentId);
@@ -14366,6 +15032,21 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14366
15032
  if (ap) this.recordRuntimeTelemetry(agentId, ap, event);
14367
15033
  return;
14368
15034
  }
15035
+ if (event.kind === "delivery_error") {
15036
+ if (ap) {
15037
+ this.restoreRuntimeDeliveryAfterAsyncRejection(agentId, ap, event);
15038
+ } else {
15039
+ this.recordDaemonTrace("daemon.agent.delivery_error.received_without_process", {
15040
+ agentId,
15041
+ event_kind: event.kind,
15042
+ runtime: driver.id,
15043
+ request_method: event.requestMethod,
15044
+ source: event.source,
15045
+ payloadBytes: event.payloadBytes
15046
+ });
15047
+ }
15048
+ return;
15049
+ }
14369
15050
  if (ap && isStartupRequestErrorEvent(event)) {
14370
15051
  this.handleRuntimeStartupRequestError(agentId, ap, event);
14371
15052
  return;
@@ -14382,13 +15063,15 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14382
15063
  source: event.source,
14383
15064
  itemType: event.itemType,
14384
15065
  payloadBytes: event.payloadBytes
14385
- } : event.kind === "runtime_diagnostic" ? runtimeDiagnosticTraceAttrs(event) : { kind: event.kind };
15066
+ } : event.kind === "runtime_diagnostic" ? runtimeDiagnosticTraceAttrs(event) : event.kind === "runtime_recovery" ? runtimeRecoveryTraceAttrs(event) : { kind: event.kind };
14386
15067
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.event.received", eventAttrs);
14387
- if (wasStalled) {
15068
+ const recordProgressObservedAfterStall = () => {
15069
+ if (!wasStalled) return;
14388
15070
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.observed", { afterStall: true });
14389
- }
15071
+ };
14390
15072
  if (event.kind === "internal_progress") {
14391
15073
  ap.runtimeProgress.noteInternalProgress();
15074
+ recordProgressObservedAfterStall();
14392
15075
  this.invalidateRecoveryErrorView(ap);
14393
15076
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
14394
15077
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.internal_observed", {
@@ -14405,11 +15088,17 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14405
15088
  }
14406
15089
  if (event.kind === "runtime_diagnostic") {
14407
15090
  this.noteRuntimeProgress(ap, event.kind);
15091
+ recordProgressObservedAfterStall();
14408
15092
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
14409
15093
  this.recordRuntimeDiagnosticActivity(agentId, ap, event);
14410
15094
  return;
14411
15095
  }
15096
+ if (event.kind === "runtime_recovery") {
15097
+ this.recordRuntimeRecoveryActivity(agentId, ap, event);
15098
+ return;
15099
+ }
14412
15100
  this.noteRuntimeProgress(ap, event.kind);
15101
+ recordProgressObservedAfterStall();
14413
15102
  } else if (event.kind !== "internal_progress") {
14414
15103
  this.recordDaemonTrace("daemon.agent.event.received_without_process", {
14415
15104
  agentId,
@@ -15166,8 +15855,8 @@ ${RESPONSE_TARGET_HINT}`);
15166
15855
  const nodes = [];
15167
15856
  for (const entry of entries) {
15168
15857
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
15169
- const fullPath = path13.join(dir, entry.name);
15170
- const relativePath = path13.relative(rootDir, fullPath);
15858
+ const fullPath = path14.join(dir, entry.name);
15859
+ const relativePath = path14.relative(rootDir, fullPath);
15171
15860
  let info;
15172
15861
  try {
15173
15862
  info = await stat2(fullPath);
@@ -15542,8 +16231,8 @@ var ReminderCache = class {
15542
16231
  // src/machineLock.ts
15543
16232
  import { createHash as createHash4, randomUUID as randomUUID6 } from "crypto";
15544
16233
  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";
16234
+ import os8 from "os";
16235
+ import path15 from "path";
15547
16236
  var INCOMPLETE_LOCK_STALE_MS = 3e4;
15548
16237
  var DaemonMachineLockConflictError = class extends Error {
15549
16238
  code = "DAEMON_MACHINE_LOCK_HELD";
@@ -15565,7 +16254,7 @@ function resolveDefaultMachineStateRoot() {
15565
16254
  return resolveSlockHomePath("machines");
15566
16255
  }
15567
16256
  function ownerPath(lockDir) {
15568
- return path14.join(lockDir, "owner.json");
16257
+ return path15.join(lockDir, "owner.json");
15569
16258
  }
15570
16259
  function readOwner(lockDir) {
15571
16260
  try {
@@ -15595,8 +16284,8 @@ function acquireDaemonMachineLock(options) {
15595
16284
  const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
15596
16285
  const fingerprint = apiKeyFingerprint(options.apiKey);
15597
16286
  const lockId = getDaemonMachineLockId(options.apiKey);
15598
- const machineDir = path14.join(rootDir, lockId);
15599
- const lockDir = path14.join(machineDir, "daemon.lock");
16287
+ const machineDir = path15.join(rootDir, lockId);
16288
+ const lockDir = path15.join(machineDir, "daemon.lock");
15600
16289
  const token = randomUUID6();
15601
16290
  mkdirSync6(machineDir, { recursive: true });
15602
16291
  for (let attempt = 0; attempt < 2; attempt += 1) {
@@ -15605,7 +16294,7 @@ function acquireDaemonMachineLock(options) {
15605
16294
  const owner = {
15606
16295
  pid: process.pid,
15607
16296
  token,
15608
- hostname: os7.hostname(),
16297
+ hostname: os8.hostname(),
15609
16298
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
15610
16299
  serverUrl: options.serverUrl,
15611
16300
  apiKeyFingerprint: fingerprint.slice(0, 16)
@@ -15657,7 +16346,7 @@ function acquireDaemonMachineLock(options) {
15657
16346
 
15658
16347
  // src/localTraceSink.ts
15659
16348
  import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
15660
- import path15 from "path";
16349
+ import path16 from "path";
15661
16350
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
15662
16351
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
15663
16352
  var DEFAULT_MAX_FILES = 8;
@@ -15694,7 +16383,7 @@ var LocalRotatingTraceSink = class {
15694
16383
  currentSize = 0;
15695
16384
  sequence = 0;
15696
16385
  constructor(options) {
15697
- this.traceDir = path15.join(options.machineDir, "traces");
16386
+ this.traceDir = path16.join(options.machineDir, "traces");
15698
16387
  this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
15699
16388
  const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
15700
16389
  const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
@@ -15724,7 +16413,7 @@ var LocalRotatingTraceSink = class {
15724
16413
  const nowMs = this.nowMsProvider();
15725
16414
  const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
15726
16415
  if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
15727
- this.currentFile = path15.join(
16416
+ this.currentFile = path16.join(
15728
16417
  this.traceDir,
15729
16418
  `daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
15730
16419
  );
@@ -15739,7 +16428,7 @@ var LocalRotatingTraceSink = class {
15739
16428
  const excess = files.length - this.maxFiles;
15740
16429
  if (excess <= 0) return;
15741
16430
  for (const file of files.slice(0, excess)) {
15742
- rmSync4(path15.join(this.traceDir, file), { force: true });
16431
+ rmSync4(path16.join(this.traceDir, file), { force: true });
15743
16432
  }
15744
16433
  }
15745
16434
  };
@@ -15830,7 +16519,7 @@ function isDiagnosticErrorAttr(key) {
15830
16519
  import { createHash as createHash6, randomUUID as randomUUID7 } from "crypto";
15831
16520
  import { gzipSync as gzipSync2 } from "zlib";
15832
16521
  import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
15833
- import path16 from "path";
16522
+ import path17 from "path";
15834
16523
 
15835
16524
  // src/traceJitter.ts
15836
16525
  import { createHash as createHash5 } from "crypto";
@@ -15929,7 +16618,7 @@ var DaemonTraceBundleUploader = class {
15929
16618
  }, nextMs);
15930
16619
  }
15931
16620
  async findUploadCandidates() {
15932
- const traceDir = path16.join(this.options.machineDir, "traces");
16621
+ const traceDir = path17.join(this.options.machineDir, "traces");
15933
16622
  let names;
15934
16623
  try {
15935
16624
  names = await readdir3(traceDir);
@@ -15941,8 +16630,8 @@ var DaemonTraceBundleUploader = class {
15941
16630
  const currentFile = this.options.currentFileProvider?.();
15942
16631
  const candidates = [];
15943
16632
  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;
16633
+ const file = path17.join(traceDir, name);
16634
+ if (currentFile && path17.resolve(file) === path17.resolve(currentFile)) continue;
15946
16635
  if (await this.isUploaded(file)) continue;
15947
16636
  try {
15948
16637
  const info = await stat3(file);
@@ -16016,8 +16705,8 @@ var DaemonTraceBundleUploader = class {
16016
16705
  }
16017
16706
  }
16018
16707
  uploadStatePath(file) {
16019
- const stateDir = path16.join(this.options.machineDir, "trace-uploads");
16020
- return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
16708
+ const stateDir = path17.join(this.options.machineDir, "trace-uploads");
16709
+ return path17.join(stateDir, `${path17.basename(file)}.uploaded.json`);
16021
16710
  }
16022
16711
  async isUploaded(file) {
16023
16712
  try {
@@ -16029,9 +16718,9 @@ var DaemonTraceBundleUploader = class {
16029
16718
  }
16030
16719
  async markUploaded(file, metadata) {
16031
16720
  const stateFile = this.uploadStatePath(file);
16032
- await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
16721
+ await mkdir2(path17.dirname(stateFile), { recursive: true, mode: 448 });
16033
16722
  await writeFile2(stateFile, `${JSON.stringify({
16034
- file: path16.basename(file),
16723
+ file: path17.basename(file),
16035
16724
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
16036
16725
  ...metadata
16037
16726
  }, null, 2)}
@@ -16180,13 +16869,13 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
16180
16869
  }
16181
16870
  }
16182
16871
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
16183
- const thisDir = path17.dirname(fileURLToPath(moduleUrl));
16184
- const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
16872
+ const thisDir = path18.dirname(fileURLToPath(moduleUrl));
16873
+ const bundledDistPath = path18.resolve(thisDir, "cli", "index.js");
16185
16874
  try {
16186
16875
  accessSync(bundledDistPath);
16187
16876
  return bundledDistPath;
16188
16877
  } catch {
16189
- const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
16878
+ const workspaceDistPath = path18.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
16190
16879
  accessSync(workspaceDistPath);
16191
16880
  return workspaceDistPath;
16192
16881
  }
@@ -16200,11 +16889,12 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
16200
16889
  }
16201
16890
  async function runBundledSlockCli(argv) {
16202
16891
  process.argv = [process.execPath, "slock", ...argv];
16203
- await import("./dist-DSRBN3VD.js");
16892
+ await import("./dist-5BEASCX5.js");
16204
16893
  }
16205
16894
  function detectRuntimes(tracer = noopTracer) {
16206
16895
  const ids = [];
16207
16896
  const versions = {};
16897
+ const diagnostics = {};
16208
16898
  const span = tracer.startSpan("daemon.runtime.detect", {
16209
16899
  surface: "daemon",
16210
16900
  kind: "internal",
@@ -16220,20 +16910,26 @@ function detectRuntimes(tracer = noopTracer) {
16220
16910
  const probe = driver.probe();
16221
16911
  if (!probe.available) {
16222
16912
  if (probe.version) versions[runtime.id] = probe.version;
16913
+ if (probe.diagnostic) diagnostics[runtime.id] = probe.diagnostic;
16223
16914
  span.addEvent("daemon.runtime.detect.checked", {
16224
16915
  runtime: runtime.id,
16225
16916
  outcome: "unavailable",
16226
16917
  version_present: Boolean(probe.version),
16918
+ diagnostic_present: Boolean(probe.diagnostic),
16919
+ ...probe.diagnostic ? { diagnostic: probe.diagnostic } : {},
16227
16920
  binary_path_present: false
16228
16921
  });
16229
16922
  continue;
16230
16923
  }
16231
16924
  ids.push(runtime.id);
16232
16925
  if (probe.version) versions[runtime.id] = probe.version;
16926
+ if (probe.diagnostic) diagnostics[runtime.id] = probe.diagnostic;
16233
16927
  span.addEvent("daemon.runtime.detect.checked", {
16234
16928
  runtime: runtime.id,
16235
16929
  outcome: "available",
16236
16930
  version_present: Boolean(probe.version),
16931
+ diagnostic_present: Boolean(probe.diagnostic),
16932
+ ...probe.diagnostic ? { diagnostic: probe.diagnostic } : {},
16237
16933
  binary_path_present: false
16238
16934
  });
16239
16935
  continue;
@@ -16276,7 +16972,7 @@ function detectRuntimes(tracer = noopTracer) {
16276
16972
  detected_runtime_count: ids.length
16277
16973
  }
16278
16974
  });
16279
- return { ids, versions };
16975
+ return { ids, versions, ...Object.keys(diagnostics).length > 0 ? { diagnostics } : {} };
16280
16976
  }
16281
16977
  function readPositiveIntegerEnv3(name, fallback) {
16282
16978
  const raw = process.env[name];
@@ -16398,7 +17094,7 @@ var DaemonCore = class {
16398
17094
  }
16399
17095
  resolveMachineStateRoot() {
16400
17096
  if (this.options.machineStateDir) return this.options.machineStateDir;
16401
- if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
17097
+ if (this.options.dataDir) return path18.join(path18.dirname(this.options.dataDir), "machines");
16402
17098
  return resolveDefaultMachineStateRoot();
16403
17099
  }
16404
17100
  shouldEnableLocalTrace() {
@@ -16425,7 +17121,7 @@ var DaemonCore = class {
16425
17121
  sink: this.localTraceSink
16426
17122
  }));
16427
17123
  this.agentManager.setTracer(this.tracer);
16428
- this.agentManager.setCliTransportTraceDir(path17.join(machineDir, "traces"));
17124
+ this.agentManager.setCliTransportTraceDir(path18.join(machineDir, "traces"));
16429
17125
  }
16430
17126
  installTraceBundleUploader(machineDir) {
16431
17127
  if (!this.shouldEnableLocalTrace()) return;
@@ -16973,9 +17669,12 @@ var DaemonCore = class {
16973
17669
  });
16974
17670
  }
16975
17671
  handleConnect() {
16976
- const { ids: runtimes, versions: runtimeVersions } = this.runtimeDetector();
17672
+ const { ids: runtimes, versions: runtimeVersions, diagnostics: runtimeDiagnostics = {} } = this.runtimeDetector();
16977
17673
  const runtimeInfo = runtimes.map((id) => runtimeVersions[id] ? `${id} (${runtimeVersions[id]})` : id);
16978
17674
  logger.info(`[Daemon] Detected runtimes: ${runtimeInfo.join(", ") || "none"}`);
17675
+ for (const [runtime, diagnostic] of Object.entries(runtimeDiagnostics)) {
17676
+ logger.warn(`[Daemon] Runtime ${runtime} diagnostic: ${diagnostic}`);
17677
+ }
16979
17678
  if (!this.opencliWrappersRegenerated) {
16980
17679
  this.opencliWrappersRegenerated = true;
16981
17680
  try {
@@ -16995,8 +17694,8 @@ var DaemonCore = class {
16995
17694
  capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
16996
17695
  runtimes,
16997
17696
  runningAgents: runningAgentIds,
16998
- hostname: this.options.hostname ?? os8.hostname(),
16999
- os: this.options.osDescription ?? `${os8.platform()} ${os8.arch()}`,
17697
+ hostname: this.options.hostname ?? os9.hostname(),
17698
+ os: this.options.osDescription ?? `${os9.platform()} ${os9.arch()}`,
17000
17699
  daemonVersion: this.daemonVersion,
17001
17700
  ...this.computerVersion ? { computerVersion: this.computerVersion } : {}
17002
17701
  });