@adhdev/daemon-core 0.8.25 → 0.8.28

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.
Files changed (47) hide show
  1. package/dist/agent-stream/provider-adapter.d.ts +1 -0
  2. package/dist/boot/daemon-lifecycle.d.ts +2 -0
  3. package/dist/cli-adapters/pty-transport.d.ts +3 -0
  4. package/dist/commands/handler.d.ts +1 -0
  5. package/dist/commands/router.d.ts +24 -0
  6. package/dist/commands/stream-commands.d.ts +1 -1
  7. package/dist/detection/cli-detector.d.ts +6 -2
  8. package/dist/detection/ide-detector.d.ts +2 -1
  9. package/dist/index.js +824 -369
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +822 -367
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/providers/acp-provider-instance.d.ts +1 -1
  14. package/dist/providers/cli-provider-instance.d.ts +1 -0
  15. package/dist/providers/extension-provider-instance.d.ts +7 -0
  16. package/dist/providers/provider-loader.d.ts +26 -0
  17. package/dist/shared-types.d.ts +2 -0
  18. package/dist/status/normalize.js +14 -2
  19. package/dist/status/normalize.js.map +1 -1
  20. package/dist/status/normalize.mjs +14 -2
  21. package/dist/status/normalize.mjs.map +1 -1
  22. package/dist/status/snapshot.d.ts +8 -2
  23. package/node_modules/@adhdev/session-host-core/dist/index.d.mts +72 -1
  24. package/node_modules/@adhdev/session-host-core/dist/index.d.ts +72 -1
  25. package/node_modules/@adhdev/session-host-core/dist/index.js.map +1 -1
  26. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  27. package/package.json +1 -1
  28. package/src/agent-stream/provider-adapter.ts +45 -3
  29. package/src/boot/daemon-lifecycle.ts +31 -1
  30. package/src/cli-adapters/provider-cli-adapter.ts +14 -3
  31. package/src/cli-adapters/pty-transport.ts +3 -0
  32. package/src/cli-adapters/session-host-transport.ts +8 -0
  33. package/src/commands/chat-commands.ts +38 -9
  34. package/src/commands/cli-manager.ts +2 -2
  35. package/src/commands/handler.ts +26 -3
  36. package/src/commands/router.ts +144 -1
  37. package/src/commands/stream-commands.ts +6 -3
  38. package/src/detection/cli-detector.ts +72 -29
  39. package/src/detection/ide-detector.ts +24 -8
  40. package/src/launch.ts +1 -1
  41. package/src/providers/acp-provider-instance.ts +19 -10
  42. package/src/providers/cli-provider-instance.ts +29 -1
  43. package/src/providers/extension-provider-instance.ts +24 -1
  44. package/src/providers/provider-loader.ts +144 -11
  45. package/src/shared-types.ts +2 -0
  46. package/src/status/normalize.ts +19 -2
  47. package/src/status/snapshot.ts +25 -14
package/dist/index.js CHANGED
@@ -251,13 +251,13 @@ function getDaemonLogDir() {
251
251
  return LOG_DIR;
252
252
  }
253
253
  function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
254
- return path4.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
254
+ return path6.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
255
255
  }
256
256
  function checkDateRotation() {
257
257
  const today = getDateStr();
258
258
  if (today !== currentDate) {
259
259
  currentDate = today;
260
- currentLogFile = path4.join(LOG_DIR, `daemon-${currentDate}.log`);
260
+ currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
261
261
  cleanOldLogs();
262
262
  }
263
263
  }
@@ -271,7 +271,7 @@ function cleanOldLogs() {
271
271
  const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
272
272
  if (dateMatch && dateMatch[1] < cutoffStr) {
273
273
  try {
274
- fs2.unlinkSync(path4.join(LOG_DIR, file));
274
+ fs2.unlinkSync(path6.join(LOG_DIR, file));
275
275
  } catch {
276
276
  }
277
277
  }
@@ -388,17 +388,17 @@ function installGlobalInterceptor() {
388
388
  writeToFile(`Log file: ${currentLogFile}`);
389
389
  writeToFile(`Log level: ${currentLevel}`);
390
390
  }
391
- var fs2, path4, os4, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH;
391
+ var fs2, path6, os4, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH;
392
392
  var init_logger = __esm({
393
393
  "src/logging/logger.ts"() {
394
394
  "use strict";
395
395
  fs2 = __toESM(require("fs"));
396
- path4 = __toESM(require("path"));
396
+ path6 = __toESM(require("path"));
397
397
  os4 = __toESM(require("os"));
398
398
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
399
399
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
400
400
  currentLevel = "info";
401
- LOG_DIR = process.platform === "win32" ? path4.join(process.env.LOCALAPPDATA || process.env.APPDATA || path4.join(os4.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path4.join(os4.homedir(), "Library", "Logs", "adhdev") : path4.join(os4.homedir(), ".local", "share", "adhdev", "logs");
401
+ LOG_DIR = process.platform === "win32" ? path6.join(process.env.LOCALAPPDATA || process.env.APPDATA || path6.join(os4.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path6.join(os4.homedir(), "Library", "Logs", "adhdev") : path6.join(os4.homedir(), ".local", "share", "adhdev", "logs");
402
402
  MAX_LOG_SIZE = 5 * 1024 * 1024;
403
403
  MAX_LOG_DAYS = 7;
404
404
  try {
@@ -406,16 +406,16 @@ var init_logger = __esm({
406
406
  } catch {
407
407
  }
408
408
  currentDate = getDateStr();
409
- currentLogFile = path4.join(LOG_DIR, `daemon-${currentDate}.log`);
409
+ currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
410
410
  cleanOldLogs();
411
411
  try {
412
- const oldLog = path4.join(LOG_DIR, "daemon.log");
412
+ const oldLog = path6.join(LOG_DIR, "daemon.log");
413
413
  if (fs2.existsSync(oldLog)) {
414
414
  const stat = fs2.statSync(oldLog);
415
415
  const oldDate = stat.mtime.toISOString().slice(0, 10);
416
- fs2.renameSync(oldLog, path4.join(LOG_DIR, `daemon-${oldDate}.log`));
416
+ fs2.renameSync(oldLog, path6.join(LOG_DIR, `daemon-${oldDate}.log`));
417
417
  }
418
- const oldLogBackup = path4.join(LOG_DIR, "daemon.log.old");
418
+ const oldLogBackup = path6.join(LOG_DIR, "daemon.log.old");
419
419
  if (fs2.existsSync(oldLogBackup)) {
420
420
  fs2.unlinkSync(oldLogBackup);
421
421
  }
@@ -447,7 +447,7 @@ var init_logger = __esm({
447
447
  }
448
448
  };
449
449
  interceptorInstalled = false;
450
- LOG_PATH = path4.join(LOG_DIR, `daemon-${getDateStr()}.log`);
450
+ LOG_PATH = path6.join(LOG_DIR, `daemon-${getDateStr()}.log`);
451
451
  }
452
452
  });
453
453
 
@@ -872,16 +872,22 @@ function computeTerminalQueryTail(buffer) {
872
872
  return "";
873
873
  }
874
874
  function findBinary(name) {
875
+ const trimmed = String(name || "").trim();
876
+ if (!trimmed) return trimmed;
877
+ const expanded = trimmed.startsWith("~") ? path9.join(os8.homedir(), trimmed.slice(1)) : trimmed;
878
+ if (path9.isAbsolute(expanded) || expanded.includes("/") || expanded.includes("\\")) {
879
+ return path9.isAbsolute(expanded) ? expanded : path9.resolve(expanded);
880
+ }
875
881
  const isWin = os8.platform() === "win32";
876
882
  try {
877
- const cmd = isWin ? `where ${name}` : `which ${name}`;
883
+ const cmd = isWin ? `where ${trimmed}` : `which ${trimmed}`;
878
884
  return (0, import_child_process4.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n")[0].trim();
879
885
  } catch {
880
- return isWin ? `${name}.cmd` : name;
886
+ return isWin ? `${trimmed}.cmd` : trimmed;
881
887
  }
882
888
  }
883
889
  function isScriptBinary(binaryPath) {
884
- if (!path7.isAbsolute(binaryPath)) return false;
890
+ if (!path9.isAbsolute(binaryPath)) return false;
885
891
  try {
886
892
  const fs15 = require("fs");
887
893
  const resolved = fs15.realpathSync(binaryPath);
@@ -897,7 +903,7 @@ function isScriptBinary(binaryPath) {
897
903
  }
898
904
  }
899
905
  function looksLikeMachOOrElf(filePath) {
900
- if (!path7.isAbsolute(filePath)) return false;
906
+ if (!path9.isAbsolute(filePath)) return false;
901
907
  try {
902
908
  const fs15 = require("fs");
903
909
  const resolved = fs15.realpathSync(filePath);
@@ -1021,12 +1027,12 @@ function normalizeCliProviderForRuntime(raw) {
1021
1027
  }
1022
1028
  };
1023
1029
  }
1024
- var os8, path7, import_child_process4, buildCliSpawnEnv, ProviderCliAdapter;
1030
+ var os8, path9, import_child_process4, buildCliSpawnEnv, ProviderCliAdapter;
1025
1031
  var init_provider_cli_adapter = __esm({
1026
1032
  "src/cli-adapters/provider-cli-adapter.ts"() {
1027
1033
  "use strict";
1028
1034
  os8 = __toESM(require("os"));
1029
- path7 = __toESM(require("path"));
1035
+ path9 = __toESM(require("path"));
1030
1036
  import_child_process4 = require("child_process");
1031
1037
  init_logger();
1032
1038
  init_terminal_screen();
@@ -1381,16 +1387,17 @@ var init_provider_cli_adapter = __esm({
1381
1387
  async spawn() {
1382
1388
  if (this.ptyProcess) return;
1383
1389
  const { spawn: spawnConfig } = this.provider;
1384
- const binaryPath = findBinary(spawnConfig.command);
1390
+ const configuredCommand = typeof this.runtimeSettings.executablePath === "string" && this.runtimeSettings.executablePath.trim() ? this.runtimeSettings.executablePath.trim() : spawnConfig.command;
1391
+ const binaryPath = findBinary(configuredCommand);
1385
1392
  const isWin = os8.platform() === "win32";
1386
1393
  const allArgs = [...spawnConfig.args, ...this.extraArgs];
1387
1394
  LOG.info("CLI", `[${this.cliType}] Spawning in ${this.workingDir}`);
1388
1395
  this.resetTraceSession();
1389
1396
  let shellCmd;
1390
1397
  let shellArgs;
1391
- const useShellUnix = !isWin && (!!spawnConfig.shell || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
1398
+ const useShellUnix = !isWin && (!!spawnConfig.shell || !path9.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
1392
1399
  const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
1393
- const useShellWin = !!spawnConfig.shell || isCmdShim || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
1400
+ const useShellWin = !!spawnConfig.shell || isCmdShim || !path9.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
1394
1401
  const useShell = isWin ? useShellWin : useShellUnix;
1395
1402
  if (useShell) {
1396
1403
  if (!spawnConfig.shell && !isWin) {
@@ -1764,7 +1771,7 @@ var init_provider_cli_adapter = __esm({
1764
1771
  `[${this.cliType}] Waiting for interactive prompt: hasPrompt=${hasPrompt} stableMs=${stableMs} recentOutputMs=${recentlyOutput} status=${status} startup=${startupLikelyActive} screen=${JSON.stringify(this.summarizeTraceText(screenText, 220)).slice(0, 260)}`
1765
1772
  );
1766
1773
  }
1767
- await new Promise((resolve9) => setTimeout(resolve9, 50));
1774
+ await new Promise((resolve12) => setTimeout(resolve12, 50));
1768
1775
  }
1769
1776
  const finalScreenText = this.terminalScreen.getText() || "";
1770
1777
  LOG.warn(
@@ -2275,7 +2282,7 @@ ${data.message || ""}`.trim();
2275
2282
  const deadline = Date.now() + 1e4;
2276
2283
  while (this.startupParseGate && Date.now() < deadline) {
2277
2284
  this.resolveStartupState("send_wait");
2278
- await new Promise((resolve9) => setTimeout(resolve9, 50));
2285
+ await new Promise((resolve12) => setTimeout(resolve12, 50));
2279
2286
  }
2280
2287
  }
2281
2288
  await this.waitForInteractivePrompt();
@@ -2500,17 +2507,17 @@ ${data.message || ""}`.trim();
2500
2507
  }
2501
2508
  }
2502
2509
  waitForStopped(timeoutMs) {
2503
- return new Promise((resolve9) => {
2510
+ return new Promise((resolve12) => {
2504
2511
  const startedAt = Date.now();
2505
2512
  const timer = setInterval(() => {
2506
2513
  if (!this.ptyProcess || this.currentStatus === "stopped") {
2507
2514
  clearInterval(timer);
2508
- resolve9(true);
2515
+ resolve12(true);
2509
2516
  return;
2510
2517
  }
2511
2518
  if (Date.now() - startedAt >= timeoutMs) {
2512
2519
  clearInterval(timer);
2513
- resolve9(false);
2520
+ resolve12(false);
2514
2521
  }
2515
2522
  }, 100);
2516
2523
  });
@@ -3209,6 +3216,7 @@ function resetState() {
3209
3216
  var import_child_process = require("child_process");
3210
3217
  var import_fs3 = require("fs");
3211
3218
  var import_os2 = require("os");
3219
+ var path4 = __toESM(require("path"));
3212
3220
  var BUILTIN_IDE_DEFINITIONS = [];
3213
3221
  var registeredIDEs = /* @__PURE__ */ new Map();
3214
3222
  function registerIDEDefinition(def) {
@@ -3225,9 +3233,16 @@ function getMergedDefinitions() {
3225
3233
  return [...merged.values()];
3226
3234
  }
3227
3235
  function findCliCommand(command) {
3236
+ const trimmed = String(command || "").trim();
3237
+ if (!trimmed) return null;
3238
+ if (path4.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
3239
+ const candidate = trimmed.startsWith("~") ? path4.join((0, import_os2.homedir)(), trimmed.slice(1)) : trimmed;
3240
+ const resolved = path4.isAbsolute(candidate) ? candidate : path4.resolve(candidate);
3241
+ return (0, import_fs3.existsSync)(resolved) ? resolved : null;
3242
+ }
3228
3243
  try {
3229
3244
  const result = (0, import_child_process.execSync)(
3230
- (0, import_os2.platform)() === "win32" ? `where ${command}` : `which ${command}`,
3245
+ (0, import_os2.platform)() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
3231
3246
  { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
3232
3247
  ).trim();
3233
3248
  return result.split("\n")[0] || null;
@@ -3250,23 +3265,23 @@ function getIdeVersion(cliCommand) {
3250
3265
  function checkPathExists(paths) {
3251
3266
  const home = (0, import_os2.homedir)();
3252
3267
  for (const p of paths) {
3253
- if (p.includes("*")) {
3268
+ const normalized = p.startsWith("~") ? path4.join(home, p.slice(1)) : p;
3269
+ if (normalized.includes("*")) {
3254
3270
  const username = home.split(/[\\/]/).pop() || "";
3255
- const resolved = p.replace("*", username);
3271
+ const resolved = normalized.replace("*", username);
3256
3272
  if ((0, import_fs3.existsSync)(resolved)) return resolved;
3257
3273
  } else {
3258
- if ((0, import_fs3.existsSync)(p)) return p;
3274
+ if ((0, import_fs3.existsSync)(normalized)) return normalized;
3259
3275
  }
3260
3276
  }
3261
3277
  return null;
3262
3278
  }
3263
- async function detectIDEs() {
3279
+ async function detectIDEs(providerLoader) {
3264
3280
  const os18 = (0, import_os2.platform)();
3265
3281
  const results = [];
3266
3282
  for (const def of getMergedDefinitions()) {
3267
- const cliPath = findCliCommand(def.cli);
3268
- const appPath = checkPathExists(def.paths[os18] || []);
3269
- const installed = !!(cliPath || appPath);
3283
+ const cliPath = findCliCommand(providerLoader?.getIdeCliCommand(def.id, def.cli) || def.cli);
3284
+ const appPath = checkPathExists(providerLoader?.getIdePathCandidates(def.id, def.paths[os18] || []) || []);
3270
3285
  let resolvedCli = cliPath;
3271
3286
  if (!resolvedCli && appPath && os18 === "darwin") {
3272
3287
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
@@ -3289,6 +3304,7 @@ async function detectIDEs() {
3289
3304
  }
3290
3305
  }
3291
3306
  }
3307
+ const installed = os18 === "darwin" ? !!(resolvedCli || appPath) : !!resolvedCli;
3292
3308
  const version = resolvedCli ? getIdeVersion(resolvedCli) : null;
3293
3309
  results.push({
3294
3310
  id: def.id,
@@ -3307,48 +3323,77 @@ async function detectIDEs() {
3307
3323
  // src/detection/cli-detector.ts
3308
3324
  var import_child_process2 = require("child_process");
3309
3325
  var os2 = __toESM(require("os"));
3326
+ var path5 = __toESM(require("path"));
3327
+ var import_fs4 = require("fs");
3310
3328
  function parseVersion(raw) {
3311
3329
  const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
3312
3330
  return match ? match[1] : raw.split("\n")[0].slice(0, 100);
3313
3331
  }
3332
+ function shellQuote(value) {
3333
+ if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
3334
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
3335
+ }
3336
+ function expandHome(value) {
3337
+ const trimmed = value.trim();
3338
+ if (!trimmed.startsWith("~")) return trimmed;
3339
+ return path5.join(os2.homedir(), trimmed.slice(1));
3340
+ }
3341
+ function isExplicitCommandPath(command) {
3342
+ const trimmed = command.trim();
3343
+ return path5.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
3344
+ }
3345
+ function resolveCommandPath(command) {
3346
+ const trimmed = command.trim();
3347
+ if (!trimmed) return null;
3348
+ if (isExplicitCommandPath(trimmed)) {
3349
+ const expanded = expandHome(trimmed);
3350
+ const candidate = path5.isAbsolute(expanded) ? expanded : path5.resolve(expanded);
3351
+ return (0, import_fs4.existsSync)(candidate) ? candidate : null;
3352
+ }
3353
+ return null;
3354
+ }
3314
3355
  function execAsync(cmd, timeoutMs = 5e3) {
3315
- return new Promise((resolve9) => {
3356
+ return new Promise((resolve12) => {
3316
3357
  const child = (0, import_child_process2.exec)(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout) => {
3317
3358
  if (err || !stdout?.trim()) {
3318
- resolve9(null);
3359
+ resolve12(null);
3319
3360
  } else {
3320
- resolve9(stdout.trim());
3361
+ resolve12(stdout.trim());
3321
3362
  }
3322
3363
  });
3323
- child.on("error", () => resolve9(null));
3364
+ child.on("error", () => resolve12(null));
3324
3365
  });
3325
3366
  }
3326
- async function detectCLIs(providerLoader) {
3367
+ async function detectCLIs(providerLoader, options) {
3327
3368
  const platform9 = os2.platform();
3328
3369
  const whichCmd = platform9 === "win32" ? "where" : "which";
3370
+ const includeVersion = options?.includeVersion !== false;
3329
3371
  const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
3330
3372
  const results = await Promise.all(
3331
3373
  cliList.map(async (cli) => {
3332
3374
  try {
3333
- const pathResult = await execAsync(`${whichCmd} ${cli.command}`);
3375
+ const explicitPath = resolveCommandPath(cli.command);
3376
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
3334
3377
  if (!pathResult) return { ...cli, installed: false };
3335
- const firstPath = pathResult.split("\n")[0];
3378
+ const firstPath = explicitPath || pathResult.split("\n")[0];
3336
3379
  let version;
3337
- try {
3380
+ if (includeVersion) {
3338
3381
  const versionCommands = [
3339
- cli.versionCommand,
3340
- `${cli.command} --version`,
3341
- `${cli.command} -V`,
3342
- `${cli.command} -v`
3382
+ `"${firstPath}" --version`,
3383
+ `"${firstPath}" -V`,
3384
+ `"${firstPath}" -v`,
3385
+ cli.versionCommand
3343
3386
  ].filter((v) => !!v);
3344
- for (const versionCommand of versionCommands) {
3345
- const versionResult = await execAsync(versionCommand, 3e3);
3346
- if (versionResult) {
3347
- version = parseVersion(versionResult);
3348
- break;
3387
+ try {
3388
+ for (const versionCommand of versionCommands) {
3389
+ const versionResult = await execAsync(versionCommand, 3e3);
3390
+ if (versionResult) {
3391
+ version = parseVersion(versionResult);
3392
+ break;
3393
+ }
3349
3394
  }
3395
+ } catch {
3350
3396
  }
3351
- } catch {
3352
3397
  }
3353
3398
  return { ...cli, installed: true, version, path: firstPath };
3354
3399
  } catch {
@@ -3358,7 +3403,7 @@ async function detectCLIs(providerLoader) {
3358
3403
  );
3359
3404
  return results;
3360
3405
  }
3361
- async function detectCLI(cliId, providerLoader) {
3406
+ async function detectCLI(cliId, providerLoader, options) {
3362
3407
  const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
3363
3408
  if (providerLoader) {
3364
3409
  const cliList = providerLoader.getCliDetectionList();
@@ -3367,25 +3412,28 @@ async function detectCLI(cliId, providerLoader) {
3367
3412
  const platform9 = os2.platform();
3368
3413
  const whichCmd = platform9 === "win32" ? "where" : "which";
3369
3414
  try {
3370
- const pathResult = await execAsync(`${whichCmd} ${target.command}`);
3415
+ const explicitPath = resolveCommandPath(target.command);
3416
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
3371
3417
  if (!pathResult) return null;
3372
- const firstPath = pathResult.split("\n")[0];
3418
+ const firstPath = explicitPath || pathResult.split("\n")[0];
3373
3419
  let version;
3374
- try {
3420
+ if (options?.includeVersion !== false) {
3375
3421
  const versionCommands = [
3376
- target.versionCommand,
3377
- `${target.command} --version`,
3378
- `${target.command} -V`,
3379
- `${target.command} -v`
3422
+ `"${firstPath}" --version`,
3423
+ `"${firstPath}" -V`,
3424
+ `"${firstPath}" -v`,
3425
+ target.versionCommand
3380
3426
  ].filter((v) => !!v);
3381
- for (const versionCommand of versionCommands) {
3382
- const versionResult = await execAsync(versionCommand, 3e3);
3383
- if (versionResult) {
3384
- version = parseVersion(versionResult);
3385
- break;
3427
+ try {
3428
+ for (const versionCommand of versionCommands) {
3429
+ const versionResult = await execAsync(versionCommand, 3e3);
3430
+ if (versionResult) {
3431
+ version = parseVersion(versionResult);
3432
+ break;
3433
+ }
3386
3434
  }
3435
+ } catch {
3387
3436
  }
3388
- } catch {
3389
3437
  }
3390
3438
  return { ...target, installed: true, version, path: firstPath };
3391
3439
  } catch {
@@ -3393,7 +3441,7 @@ async function detectCLI(cliId, providerLoader) {
3393
3441
  }
3394
3442
  }
3395
3443
  }
3396
- const all = await detectCLIs(providerLoader);
3444
+ const all = await detectCLIs(providerLoader, options);
3397
3445
  return all.find((c) => c.id === resolvedId && c.installed) || null;
3398
3446
  }
3399
3447
 
@@ -3520,7 +3568,7 @@ var DaemonCdpManager = class {
3520
3568
  * Returns multiple entries if multiple IDE windows are open on same port
3521
3569
  */
3522
3570
  static listAllTargets(port) {
3523
- return new Promise((resolve9) => {
3571
+ return new Promise((resolve12) => {
3524
3572
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
3525
3573
  let data = "";
3526
3574
  res.on("data", (chunk) => data += chunk.toString());
@@ -3536,16 +3584,16 @@ var DaemonCdpManager = class {
3536
3584
  (t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
3537
3585
  );
3538
3586
  const fallbackPages = pages.filter((t) => !isNonMain(t.title || ""));
3539
- resolve9(mainPages.length > 0 ? mainPages : fallbackPages);
3587
+ resolve12(mainPages.length > 0 ? mainPages : fallbackPages);
3540
3588
  } catch {
3541
- resolve9([]);
3589
+ resolve12([]);
3542
3590
  }
3543
3591
  });
3544
3592
  });
3545
- req.on("error", () => resolve9([]));
3593
+ req.on("error", () => resolve12([]));
3546
3594
  req.setTimeout(2e3, () => {
3547
3595
  req.destroy();
3548
- resolve9([]);
3596
+ resolve12([]);
3549
3597
  });
3550
3598
  });
3551
3599
  }
@@ -3585,7 +3633,7 @@ var DaemonCdpManager = class {
3585
3633
  }
3586
3634
  }
3587
3635
  findTargetOnPort(port) {
3588
- return new Promise((resolve9) => {
3636
+ return new Promise((resolve12) => {
3589
3637
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
3590
3638
  let data = "";
3591
3639
  res.on("data", (chunk) => data += chunk.toString());
@@ -3596,7 +3644,7 @@ var DaemonCdpManager = class {
3596
3644
  (t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
3597
3645
  );
3598
3646
  if (pages.length === 0) {
3599
- resolve9(targets.find((t) => t.webSocketDebuggerUrl) || null);
3647
+ resolve12(targets.find((t) => t.webSocketDebuggerUrl) || null);
3600
3648
  return;
3601
3649
  }
3602
3650
  const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
@@ -3606,24 +3654,24 @@ var DaemonCdpManager = class {
3606
3654
  const specific = list.find((t) => t.id === this._targetId);
3607
3655
  if (specific) {
3608
3656
  this._pageTitle = specific.title || "";
3609
- resolve9(specific);
3657
+ resolve12(specific);
3610
3658
  } else {
3611
3659
  this.log(`[CDP] Target ${this._targetId} not found in page list`);
3612
- resolve9(null);
3660
+ resolve12(null);
3613
3661
  }
3614
3662
  return;
3615
3663
  }
3616
3664
  this._pageTitle = list[0]?.title || "";
3617
- resolve9(list[0]);
3665
+ resolve12(list[0]);
3618
3666
  } catch {
3619
- resolve9(null);
3667
+ resolve12(null);
3620
3668
  }
3621
3669
  });
3622
3670
  });
3623
- req.on("error", () => resolve9(null));
3671
+ req.on("error", () => resolve12(null));
3624
3672
  req.setTimeout(2e3, () => {
3625
3673
  req.destroy();
3626
- resolve9(null);
3674
+ resolve12(null);
3627
3675
  });
3628
3676
  });
3629
3677
  }
@@ -3634,7 +3682,7 @@ var DaemonCdpManager = class {
3634
3682
  this.extensionProviders = providers;
3635
3683
  }
3636
3684
  connectToTarget(wsUrl) {
3637
- return new Promise((resolve9) => {
3685
+ return new Promise((resolve12) => {
3638
3686
  this.ws = new import_ws.default(wsUrl);
3639
3687
  this.ws.on("open", async () => {
3640
3688
  this._connected = true;
@@ -3644,17 +3692,17 @@ var DaemonCdpManager = class {
3644
3692
  }
3645
3693
  this.connectBrowserWs().catch(() => {
3646
3694
  });
3647
- resolve9(true);
3695
+ resolve12(true);
3648
3696
  });
3649
3697
  this.ws.on("message", (data) => {
3650
3698
  try {
3651
3699
  const msg = JSON.parse(data.toString());
3652
3700
  if (msg.id && this.pending.has(msg.id)) {
3653
- const { resolve: resolve10, reject } = this.pending.get(msg.id);
3701
+ const { resolve: resolve13, reject } = this.pending.get(msg.id);
3654
3702
  this.pending.delete(msg.id);
3655
3703
  this.failureCount = 0;
3656
3704
  if (msg.error) reject(new Error(msg.error.message));
3657
- else resolve10(msg.result);
3705
+ else resolve13(msg.result);
3658
3706
  } else if (msg.method === "Runtime.executionContextCreated") {
3659
3707
  this.contexts.add(msg.params.context.id);
3660
3708
  } else if (msg.method === "Runtime.executionContextDestroyed") {
@@ -3677,7 +3725,7 @@ var DaemonCdpManager = class {
3677
3725
  this.ws.on("error", (err) => {
3678
3726
  this.log(`[CDP] WebSocket error: ${err.message}`);
3679
3727
  this._connected = false;
3680
- resolve9(false);
3728
+ resolve12(false);
3681
3729
  });
3682
3730
  });
3683
3731
  }
@@ -3691,7 +3739,7 @@ var DaemonCdpManager = class {
3691
3739
  return;
3692
3740
  }
3693
3741
  this.log(`[CDP] Connecting browser WS for target discovery...`);
3694
- await new Promise((resolve9, reject) => {
3742
+ await new Promise((resolve12, reject) => {
3695
3743
  this.browserWs = new import_ws.default(browserWsUrl);
3696
3744
  this.browserWs.on("open", async () => {
3697
3745
  this._browserConnected = true;
@@ -3701,16 +3749,16 @@ var DaemonCdpManager = class {
3701
3749
  } catch (e) {
3702
3750
  this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
3703
3751
  }
3704
- resolve9();
3752
+ resolve12();
3705
3753
  });
3706
3754
  this.browserWs.on("message", (data) => {
3707
3755
  try {
3708
3756
  const msg = JSON.parse(data.toString());
3709
3757
  if (msg.id && this.browserPending.has(msg.id)) {
3710
- const { resolve: resolve10, reject: reject2 } = this.browserPending.get(msg.id);
3758
+ const { resolve: resolve13, reject: reject2 } = this.browserPending.get(msg.id);
3711
3759
  this.browserPending.delete(msg.id);
3712
3760
  if (msg.error) reject2(new Error(msg.error.message));
3713
- else resolve10(msg.result);
3761
+ else resolve13(msg.result);
3714
3762
  }
3715
3763
  } catch {
3716
3764
  }
@@ -3730,31 +3778,31 @@ var DaemonCdpManager = class {
3730
3778
  }
3731
3779
  }
3732
3780
  getBrowserWsUrl() {
3733
- return new Promise((resolve9) => {
3781
+ return new Promise((resolve12) => {
3734
3782
  const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
3735
3783
  let data = "";
3736
3784
  res.on("data", (chunk) => data += chunk.toString());
3737
3785
  res.on("end", () => {
3738
3786
  try {
3739
3787
  const info = JSON.parse(data);
3740
- resolve9(info.webSocketDebuggerUrl || null);
3788
+ resolve12(info.webSocketDebuggerUrl || null);
3741
3789
  } catch {
3742
- resolve9(null);
3790
+ resolve12(null);
3743
3791
  }
3744
3792
  });
3745
3793
  });
3746
- req.on("error", () => resolve9(null));
3794
+ req.on("error", () => resolve12(null));
3747
3795
  req.setTimeout(3e3, () => {
3748
3796
  req.destroy();
3749
- resolve9(null);
3797
+ resolve12(null);
3750
3798
  });
3751
3799
  });
3752
3800
  }
3753
3801
  sendBrowser(method, params = {}, timeoutMs = 15e3) {
3754
- return new Promise((resolve9, reject) => {
3802
+ return new Promise((resolve12, reject) => {
3755
3803
  if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
3756
3804
  const id = this.browserMsgId++;
3757
- this.browserPending.set(id, { resolve: resolve9, reject });
3805
+ this.browserPending.set(id, { resolve: resolve12, reject });
3758
3806
  this.browserWs.send(JSON.stringify({ id, method, params }));
3759
3807
  setTimeout(() => {
3760
3808
  if (this.browserPending.has(id)) {
@@ -3794,11 +3842,11 @@ var DaemonCdpManager = class {
3794
3842
  }
3795
3843
  // ─── CDP Protocol ────────────────────────────────────────
3796
3844
  sendInternal(method, params = {}, timeoutMs = 15e3) {
3797
- return new Promise((resolve9, reject) => {
3845
+ return new Promise((resolve12, reject) => {
3798
3846
  if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
3799
3847
  if (this.ws.readyState !== import_ws.default.OPEN) return reject(new Error("WebSocket not open"));
3800
3848
  const id = this.msgId++;
3801
- this.pending.set(id, { resolve: resolve9, reject });
3849
+ this.pending.set(id, { resolve: resolve12, reject });
3802
3850
  this.ws.send(JSON.stringify({ id, method, params }));
3803
3851
  setTimeout(() => {
3804
3852
  if (this.pending.has(id)) {
@@ -4047,7 +4095,7 @@ var DaemonCdpManager = class {
4047
4095
  const browserWs = this.browserWs;
4048
4096
  let msgId = this.browserMsgId;
4049
4097
  const sendWs = (method, params = {}, sessionId) => {
4050
- return new Promise((resolve9, reject) => {
4098
+ return new Promise((resolve12, reject) => {
4051
4099
  const mid = msgId++;
4052
4100
  this.browserMsgId = msgId;
4053
4101
  const handler = (raw) => {
@@ -4056,7 +4104,7 @@ var DaemonCdpManager = class {
4056
4104
  if (msg.id === mid) {
4057
4105
  browserWs.removeListener("message", handler);
4058
4106
  if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
4059
- else resolve9(msg.result);
4107
+ else resolve12(msg.result);
4060
4108
  }
4061
4109
  } catch {
4062
4110
  }
@@ -4247,14 +4295,14 @@ var DaemonCdpManager = class {
4247
4295
  if (!ws || ws.readyState !== import_ws.default.OPEN) {
4248
4296
  throw new Error("CDP not connected");
4249
4297
  }
4250
- return new Promise((resolve9, reject) => {
4298
+ return new Promise((resolve12, reject) => {
4251
4299
  const id = getNextId();
4252
4300
  pendingMap.set(id, {
4253
4301
  resolve: (result) => {
4254
4302
  if (result?.result?.subtype === "error") {
4255
4303
  reject(new Error(result.result.description));
4256
4304
  } else {
4257
- resolve9(result?.result?.value);
4305
+ resolve12(result?.result?.value);
4258
4306
  }
4259
4307
  },
4260
4308
  reject
@@ -4286,10 +4334,10 @@ var DaemonCdpManager = class {
4286
4334
  throw new Error("CDP not connected");
4287
4335
  }
4288
4336
  const sendViaSession = (method, params = {}) => {
4289
- return new Promise((resolve9, reject) => {
4337
+ return new Promise((resolve12, reject) => {
4290
4338
  const pendingMap = this._browserConnected ? this.browserPending : this.pending;
4291
4339
  const id = this._browserConnected ? this.browserMsgId++ : this.msgId++;
4292
- pendingMap.set(id, { resolve: resolve9, reject });
4340
+ pendingMap.set(id, { resolve: resolve12, reject });
4293
4341
  ws.send(JSON.stringify({ id, sessionId, method, params }));
4294
4342
  setTimeout(() => {
4295
4343
  if (pendingMap.has(id)) {
@@ -4917,9 +4965,9 @@ function normalizeControlValue(value) {
4917
4965
 
4918
4966
  // src/config/chat-history.ts
4919
4967
  var fs3 = __toESM(require("fs"));
4920
- var path5 = __toESM(require("path"));
4968
+ var path7 = __toESM(require("path"));
4921
4969
  var os5 = __toESM(require("os"));
4922
- var HISTORY_DIR = path5.join(os5.homedir(), ".adhdev", "history");
4970
+ var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
4923
4971
  var RETAIN_DAYS = 30;
4924
4972
  var ChatHistoryWriter = class {
4925
4973
  /** Last seen message count per agent (deduplication) */
@@ -4964,11 +5012,11 @@ var ChatHistoryWriter = class {
4964
5012
  });
4965
5013
  }
4966
5014
  if (newMessages.length === 0) return;
4967
- const dir = path5.join(HISTORY_DIR, this.sanitize(agentType));
5015
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
4968
5016
  fs3.mkdirSync(dir, { recursive: true });
4969
5017
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4970
5018
  const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
4971
- const filePath = path5.join(dir, `${filePrefix}${date}.jsonl`);
5019
+ const filePath = path7.join(dir, `${filePrefix}${date}.jsonl`);
4972
5020
  const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
4973
5021
  fs3.appendFileSync(filePath, lines, "utf-8");
4974
5022
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
@@ -5022,14 +5070,14 @@ var ChatHistoryWriter = class {
5022
5070
  this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
5023
5071
  this.lastSeenCounts.delete(fromDedupKey);
5024
5072
  }
5025
- const dir = path5.join(HISTORY_DIR, this.sanitize(agentType));
5073
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
5026
5074
  if (!fs3.existsSync(dir)) return;
5027
5075
  const fromPrefix = `${this.sanitize(fromId)}_`;
5028
5076
  const toPrefix = `${this.sanitize(toId)}_`;
5029
5077
  const files = fs3.readdirSync(dir).filter((file) => file.startsWith(fromPrefix) && file.endsWith(".jsonl"));
5030
5078
  for (const file of files) {
5031
- const sourcePath = path5.join(dir, file);
5032
- const targetPath = path5.join(dir, `${toPrefix}${file.slice(fromPrefix.length)}`);
5079
+ const sourcePath = path7.join(dir, file);
5080
+ const targetPath = path7.join(dir, `${toPrefix}${file.slice(fromPrefix.length)}`);
5033
5081
  const sourceLines = fs3.readFileSync(sourcePath, "utf-8").split("\n").filter(Boolean);
5034
5082
  const rewritten = sourceLines.map((line) => {
5035
5083
  try {
@@ -5070,10 +5118,10 @@ var ChatHistoryWriter = class {
5070
5118
  const cutoff = Date.now() - RETAIN_DAYS * 24 * 60 * 60 * 1e3;
5071
5119
  const agentDirs = fs3.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
5072
5120
  for (const dir of agentDirs) {
5073
- const dirPath = path5.join(HISTORY_DIR, dir.name);
5121
+ const dirPath = path7.join(HISTORY_DIR, dir.name);
5074
5122
  const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
5075
5123
  for (const file of files) {
5076
- const filePath = path5.join(dirPath, file);
5124
+ const filePath = path7.join(dirPath, file);
5077
5125
  const stat = fs3.statSync(filePath);
5078
5126
  if (stat.mtimeMs < cutoff) {
5079
5127
  fs3.unlinkSync(filePath);
@@ -5091,7 +5139,7 @@ var ChatHistoryWriter = class {
5091
5139
  function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
5092
5140
  try {
5093
5141
  const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
5094
- const dir = path5.join(HISTORY_DIR, sanitized);
5142
+ const dir = path7.join(HISTORY_DIR, sanitized);
5095
5143
  if (!fs3.existsSync(dir)) return { messages: [], hasMore: false };
5096
5144
  const sanitizedInstance = historySessionId?.replace(/[^a-zA-Z0-9_-]/g, "_");
5097
5145
  const files = fs3.readdirSync(dir).filter((f) => {
@@ -5105,7 +5153,7 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
5105
5153
  const needed = offset + limit + 1;
5106
5154
  for (const file of files) {
5107
5155
  if (allMessages.length >= needed) break;
5108
- const filePath = path5.join(dir, file);
5156
+ const filePath = path7.join(dir, file);
5109
5157
  const content = fs3.readFileSync(filePath, "utf-8");
5110
5158
  const lines = content.trim().split("\n").filter(Boolean);
5111
5159
  for (let i = lines.length - 1; i >= 0; i--) {
@@ -5127,7 +5175,7 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
5127
5175
  function listSavedHistorySessions(agentType, options = {}) {
5128
5176
  try {
5129
5177
  const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
5130
- const dir = path5.join(HISTORY_DIR, sanitized);
5178
+ const dir = path7.join(HISTORY_DIR, sanitized);
5131
5179
  if (!fs3.existsSync(dir)) return { sessions: [], hasMore: false };
5132
5180
  const groupedFiles = /* @__PURE__ */ new Map();
5133
5181
  const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
@@ -5148,7 +5196,7 @@ function listSavedHistorySessions(agentType, options = {}) {
5148
5196
  let sessionTitle = "";
5149
5197
  let preview = "";
5150
5198
  for (const file of files.sort()) {
5151
- const filePath = path5.join(dir, file);
5199
+ const filePath = path7.join(dir, file);
5152
5200
  const content = fs3.readFileSync(filePath, "utf-8");
5153
5201
  const lines = content.split("\n").filter(Boolean);
5154
5202
  for (const line of lines) {
@@ -5201,6 +5249,7 @@ var ExtensionProviderInstance = class {
5201
5249
  currentStatus = "idle";
5202
5250
  agentStreams = [];
5203
5251
  messages = [];
5252
+ prevMessageHashes = /* @__PURE__ */ new Map();
5204
5253
  activeModal = null;
5205
5254
  currentModel = "";
5206
5255
  currentMode = "";
@@ -5266,7 +5315,7 @@ var ExtensionProviderInstance = class {
5266
5315
  onEvent(event, data) {
5267
5316
  if (event === "stream_update") {
5268
5317
  if (data?.streams) this.agentStreams = data.streams;
5269
- if (data?.messages) this.messages = data.messages;
5318
+ if (data?.messages) this.messages = this.assignReceivedAt(data.messages);
5270
5319
  if (data?.activeModal !== void 0) this.activeModal = data.activeModal;
5271
5320
  if (data?.model) this.currentModel = data.model;
5272
5321
  if (data?.mode) this.currentMode = data.mode;
@@ -5292,6 +5341,7 @@ var ExtensionProviderInstance = class {
5292
5341
  dispose() {
5293
5342
  this.agentStreams = [];
5294
5343
  this.messages = [];
5344
+ this.prevMessageHashes.clear();
5295
5345
  this.monitor.reset();
5296
5346
  this.appliedEffectKeys.clear();
5297
5347
  this.runtimeMessages = [];
@@ -5447,6 +5497,23 @@ var ExtensionProviderInstance = class {
5447
5497
  this.chatId || this.instanceId
5448
5498
  );
5449
5499
  }
5500
+ /**
5501
+ * Assign stable receivedAt to extension messages.
5502
+ * Same pattern as IdeProviderInstance.readChat() prevByHash —
5503
+ * preserves first-seen timestamp across polling cycles.
5504
+ */
5505
+ assignReceivedAt(messages) {
5506
+ const now = Date.now();
5507
+ const nextHashes = /* @__PURE__ */ new Map();
5508
+ for (const msg of messages) {
5509
+ const hash = `${msg.role}:${(msg.content || "").slice(0, 100)}`;
5510
+ const prevTime = this.prevMessageHashes.get(hash);
5511
+ msg.receivedAt = prevTime || now;
5512
+ nextHashes.set(hash, msg.receivedAt);
5513
+ }
5514
+ this.prevMessageHashes = nextHashes;
5515
+ return messages;
5516
+ }
5450
5517
  mergeConversationMessages(messages) {
5451
5518
  if (this.runtimeMessages.length === 0) return messages;
5452
5519
  return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
@@ -5503,6 +5570,7 @@ ${effect.notification.body || ""}`.trim();
5503
5570
  }
5504
5571
  this.agentStreams = [];
5505
5572
  this.messages = [];
5573
+ this.prevMessageHashes.clear();
5506
5574
  this.activeModal = null;
5507
5575
  this.currentModel = "";
5508
5576
  this.currentMode = "";
@@ -6488,16 +6556,28 @@ function trimMessageForStatus(message, stringLimit) {
6488
6556
  if (!message || typeof message !== "object") return message;
6489
6557
  return trimStructuredStrings(message, stringLimit);
6490
6558
  }
6559
+ function normalizeMessageTime(message) {
6560
+ if (!message || typeof message !== "object") return message;
6561
+ const msg = message;
6562
+ if (msg.receivedAt == null) {
6563
+ const fallback = msg.timestamp ?? msg.createdAt;
6564
+ if (fallback != null) {
6565
+ const ts2 = typeof fallback === "string" ? Date.parse(fallback) : Number(fallback);
6566
+ if (Number.isFinite(ts2) && ts2 > 0) msg.receivedAt = ts2;
6567
+ }
6568
+ }
6569
+ return msg;
6570
+ }
6491
6571
  function trimMessagesForStatus(messages) {
6492
6572
  if (!Array.isArray(messages) || messages.length === 0) return [];
6493
6573
  const recent = messages.slice(-STATUS_ACTIVE_CHAT_MESSAGE_LIMIT);
6494
6574
  const kept = [];
6495
6575
  let totalBytes = 0;
6496
6576
  for (let i = recent.length - 1; i >= 0; i -= 1) {
6497
- let normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT);
6577
+ let normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT));
6498
6578
  let size = estimateBytes(normalized);
6499
6579
  if (size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
6500
- normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT);
6580
+ normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT));
6501
6581
  size = estimateBytes(normalized);
6502
6582
  }
6503
6583
  if (kept.length > 0 && totalBytes + size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
@@ -7120,7 +7200,7 @@ async function handleSendChat(h, args) {
7120
7200
  if (isExtensionTransport(transport)) {
7121
7201
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
7122
7202
  try {
7123
- const evalResult = await h.evaluateProviderScript("sendMessage", { MESSAGE: text }, 3e4);
7203
+ const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
7124
7204
  if (evalResult?.result) {
7125
7205
  const parsed = parseMaybeJson(evalResult.result);
7126
7206
  if (didProviderConfirmSend(parsed)) {
@@ -7151,7 +7231,7 @@ async function handleSendChat(h, args) {
7151
7231
  return { success: false, error: `CDP for ${managerKey || "unknown"} not connected` };
7152
7232
  }
7153
7233
  _log(`Targeting IDE: ${getCurrentManagerKey(h)}`);
7154
- const sendScript = h.getProviderScript("sendMessage", { MESSAGE: text });
7234
+ const sendScript = h.getProviderScript("sendMessage", { message: text });
7155
7235
  if (sendScript) {
7156
7236
  try {
7157
7237
  const result = await targetCdp.evaluate(sendScript, 3e4);
@@ -7291,6 +7371,14 @@ async function handleListChats(h, args) {
7291
7371
  } catch {
7292
7372
  }
7293
7373
  }
7374
+ if (parsed?.sessions && Array.isArray(parsed.sessions)) {
7375
+ LOG.info("Command", `[list_chats] OK: ${parsed.sessions.length} chats`);
7376
+ return { success: true, chats: parsed.sessions };
7377
+ }
7378
+ if (parsed?.chats && Array.isArray(parsed.chats)) {
7379
+ LOG.info("Command", `[list_chats] OK: ${parsed.chats.length} chats`);
7380
+ return { success: true, chats: parsed.chats };
7381
+ }
7294
7382
  if (Array.isArray(parsed)) {
7295
7383
  LOG.info("Command", `[list_chats] OK: ${parsed.length} chats`);
7296
7384
  return { success: true, chats: parsed };
@@ -7360,7 +7448,13 @@ async function handleSwitchChat(h, args) {
7360
7448
  } catch (e) {
7361
7449
  return { success: false, error: `webviewSwitchSession failed: ${e.message}` };
7362
7450
  }
7363
- const script = h.getProviderScript("switchSession", { SESSION_ID: JSON.stringify(sessionId) }) || h.getProviderScript("switch_session", { SESSION_ID: JSON.stringify(sessionId) });
7451
+ const switchParams = {
7452
+ sessionId,
7453
+ title: sessionId,
7454
+ id: sessionId,
7455
+ SESSION_ID: JSON.stringify(sessionId)
7456
+ };
7457
+ const script = h.getProviderScript("switchSession", switchParams) || h.getProviderScript("switch_session", switchParams);
7364
7458
  if (!script) return { success: false, error: "switch_session script not available" };
7365
7459
  try {
7366
7460
  const raw = await cdp.evaluate(script, 15e3);
@@ -7439,8 +7533,8 @@ async function handleSetMode(h, args) {
7439
7533
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
7440
7534
  if (adapter) {
7441
7535
  const acpInstance = adapter._acpInstance;
7442
- if (acpInstance && typeof acpInstance.onEvent === "function") {
7443
- acpInstance.onEvent("set_mode", { mode });
7536
+ if (acpInstance && typeof acpInstance.setMode === "function") {
7537
+ await acpInstance.setMode(mode);
7444
7538
  return { success: true, mode };
7445
7539
  }
7446
7540
  }
@@ -7497,9 +7591,9 @@ async function handleChangeModel(h, args) {
7497
7591
  LOG.info("Command", `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
7498
7592
  if (adapter) {
7499
7593
  const acpInstance = adapter._acpInstance;
7500
- if (acpInstance && typeof acpInstance.onEvent === "function") {
7501
- acpInstance.onEvent("change_model", { model });
7502
- LOG.info("Command", `[change_model] Dispatched change_model event to ACP instance`);
7594
+ if (acpInstance && typeof acpInstance.setConfigOption === "function") {
7595
+ await acpInstance.setConfigOption("model", model);
7596
+ LOG.info("Command", `[change_model] Updated ACP model to ${model}`);
7503
7597
  return { success: true, model };
7504
7598
  }
7505
7599
  }
@@ -7619,6 +7713,18 @@ async function handleResolveAction(h, args) {
7619
7713
  const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
7620
7714
  return { success: ok };
7621
7715
  }
7716
+ if (transport === "acp") {
7717
+ const adapter = getTargetedCliAdapter(h, args, provider?.type);
7718
+ const acpInstance = adapter?._acpInstance;
7719
+ if (!acpInstance) return { success: false, error: "ACP instance not found" };
7720
+ try {
7721
+ await acpInstance.resolvePermission(action === "approve" || action === "accept" || action === "always");
7722
+ LOG.info("Command", `[resolveAction] ACP \u2192 ${action}`);
7723
+ return { success: true, action };
7724
+ } catch (e) {
7725
+ return { success: false, error: e?.message || "ACP resolve action failed" };
7726
+ }
7727
+ }
7622
7728
  if (provider?.scripts?.webviewResolveAction || provider?.scripts?.webview_resolve_action) {
7623
7729
  const script = h.getProviderScript("webviewResolveAction", { action, button, buttonText: button }) || h.getProviderScript("webview_resolve_action", { action, button, buttonText: button });
7624
7730
  if (script) {
@@ -7697,7 +7803,7 @@ async function handleResolveAction(h, args) {
7697
7803
 
7698
7804
  // src/commands/cdp-commands.ts
7699
7805
  var fs4 = __toESM(require("fs"));
7700
- var path6 = __toESM(require("path"));
7806
+ var path8 = __toESM(require("path"));
7701
7807
  var os6 = __toESM(require("os"));
7702
7808
  var KEY_TO_VK = {
7703
7809
  Backspace: 8,
@@ -7949,25 +8055,25 @@ function resolveSafePath(requestedPath) {
7949
8055
  const inputPath = rawPath || ".";
7950
8056
  const home = os6.homedir();
7951
8057
  if (inputPath.startsWith("~")) {
7952
- return path6.resolve(path6.join(home, inputPath.slice(1)));
8058
+ return path8.resolve(path8.join(home, inputPath.slice(1)));
7953
8059
  }
7954
8060
  if (process.platform === "win32") {
7955
8061
  const normalized = normalizeWindowsRequestedPath(inputPath);
7956
- if (path6.win32.isAbsolute(normalized)) {
7957
- return path6.win32.normalize(normalized);
8062
+ if (path8.win32.isAbsolute(normalized)) {
8063
+ return path8.win32.normalize(normalized);
7958
8064
  }
7959
- return path6.win32.resolve(normalized);
8065
+ return path8.win32.resolve(normalized);
7960
8066
  }
7961
- if (path6.isAbsolute(inputPath)) {
7962
- return path6.normalize(inputPath);
8067
+ if (path8.isAbsolute(inputPath)) {
8068
+ return path8.normalize(inputPath);
7963
8069
  }
7964
- return path6.resolve(inputPath);
8070
+ return path8.resolve(inputPath);
7965
8071
  }
7966
8072
  function listDirectoryEntriesSafe(dirPath) {
7967
8073
  const entries = fs4.readdirSync(dirPath, { withFileTypes: true });
7968
8074
  const files = [];
7969
8075
  for (const entry of entries) {
7970
- const entryPath = path6.join(dirPath, entry.name);
8076
+ const entryPath = path8.join(dirPath, entry.name);
7971
8077
  try {
7972
8078
  if (entry.isDirectory()) {
7973
8079
  files.push({ name: entry.name, type: "directory" });
@@ -8006,7 +8112,7 @@ async function handleFileRead(h, args) {
8006
8112
  async function handleFileWrite(h, args) {
8007
8113
  try {
8008
8114
  const filePath = resolveSafePath(args?.path);
8009
- fs4.mkdirSync(path6.dirname(filePath), { recursive: true });
8115
+ fs4.mkdirSync(path8.dirname(filePath), { recursive: true });
8010
8116
  fs4.writeFileSync(filePath, args?.content || "", "utf-8");
8011
8117
  return { success: true, path: filePath };
8012
8118
  } catch (e) {
@@ -8094,7 +8200,7 @@ function handleGetProviderSettings(h, args) {
8094
8200
  }
8095
8201
  return { success: true, settings: allSettings, values: allValues };
8096
8202
  }
8097
- function handleSetProviderSetting(h, args) {
8203
+ async function handleSetProviderSetting(h, args) {
8098
8204
  const loader = h.ctx.providerLoader;
8099
8205
  const { providerType, key, value } = args || {};
8100
8206
  if (!providerType || !key || value === void 0) {
@@ -8107,6 +8213,7 @@ function handleSetProviderSetting(h, args) {
8107
8213
  const updated = h.ctx.instanceManager.updateInstanceSettings(providerType, allSettings);
8108
8214
  LOG.info("Command", `[set_provider_setting] ${providerType}.${key}=${JSON.stringify(value)} \u2192 ${updated} instance(s) updated`);
8109
8215
  }
8216
+ await h.ctx.onProviderSettingChanged?.(providerType, key, value);
8110
8217
  return { success: true, providerType, key, value };
8111
8218
  }
8112
8219
  return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
@@ -8144,10 +8251,10 @@ function getCliScriptCommand(payload) {
8144
8251
  }
8145
8252
  const command = payload.command;
8146
8253
  if (!command || typeof command !== "object") return null;
8147
- if (command.type !== "send_message") return null;
8254
+ if (command.type !== "send_message" && command.type !== "pty_write") return null;
8148
8255
  const text = typeof command.text === "string" ? command.text.trim() : typeof command.message === "string" ? command.message.trim() : "";
8149
8256
  if (!text) return null;
8150
- return { type: "send_message", text };
8257
+ return { type: command.type, text };
8151
8258
  }
8152
8259
  function applyProviderPatch(h, args, payload) {
8153
8260
  if (!payload || typeof payload !== "object") return;
@@ -8188,6 +8295,8 @@ async function executeProviderScript(h, args, scriptName) {
8188
8295
  const cliCommand = getCliScriptCommand(parsed.payload);
8189
8296
  if (cliCommand?.type === "send_message" && cliCommand.text) {
8190
8297
  await adapter.sendMessage(cliCommand.text);
8298
+ } else if (cliCommand?.type === "pty_write" && cliCommand.text && adapter.writeRaw) {
8299
+ adapter.writeRaw(cliCommand.text + "\r");
8191
8300
  }
8192
8301
  applyProviderPatch(h, args, parsed.payload);
8193
8302
  return { success: true, ...parsed.payload && typeof parsed.payload === "object" ? parsed.payload : { result: parsed.payload } };
@@ -8553,9 +8662,26 @@ var DaemonCommandHandler = class {
8553
8662
  if (provider?.scripts) {
8554
8663
  const fn = provider.scripts[scriptName];
8555
8664
  if (typeof fn === "function") {
8556
- const firstVal = params ? Object.values(params)[0] : void 0;
8557
- const script = firstVal ? fn(firstVal) : fn();
8558
- if (script) return script;
8665
+ if (params && Object.keys(params).length > 0) {
8666
+ const firstVal = Object.values(params)[0];
8667
+ if (scriptName === "sendMessage" && typeof firstVal === "string") {
8668
+ const legacyScript = fn(firstVal);
8669
+ if (legacyScript) return legacyScript;
8670
+ }
8671
+ const script = fn(params);
8672
+ if (script) {
8673
+ const likelyLegacyObjectLeak = typeof script === "string" && script.includes("[object Object]") && typeof firstVal === "string";
8674
+ if (!likelyLegacyObjectLeak) return script;
8675
+ }
8676
+ if (firstVal !== void 0) {
8677
+ const legacyScript = fn(firstVal);
8678
+ if (legacyScript) return legacyScript;
8679
+ }
8680
+ if (script) return script;
8681
+ } else {
8682
+ const script = fn();
8683
+ if (script) return script;
8684
+ }
8559
8685
  }
8560
8686
  }
8561
8687
  return null;
@@ -8849,7 +8975,7 @@ var DaemonCommandHandler = class {
8849
8975
  try {
8850
8976
  const http3 = await import("http");
8851
8977
  const postData = JSON.stringify(body);
8852
- const result = await new Promise((resolve9, reject) => {
8978
+ const result = await new Promise((resolve12, reject) => {
8853
8979
  const req = http3.request({
8854
8980
  hostname: "127.0.0.1",
8855
8981
  port: 19280,
@@ -8861,9 +8987,9 @@ var DaemonCommandHandler = class {
8861
8987
  res.on("data", (chunk) => data += chunk);
8862
8988
  res.on("end", () => {
8863
8989
  try {
8864
- resolve9(JSON.parse(data));
8990
+ resolve12(JSON.parse(data));
8865
8991
  } catch {
8866
- resolve9({ raw: data });
8992
+ resolve12({ raw: data });
8867
8993
  }
8868
8994
  });
8869
8995
  });
@@ -8881,15 +9007,15 @@ var DaemonCommandHandler = class {
8881
9007
  if (!providerType) return { success: false, error: "providerType required" };
8882
9008
  try {
8883
9009
  const http3 = await import("http");
8884
- const result = await new Promise((resolve9, reject) => {
9010
+ const result = await new Promise((resolve12, reject) => {
8885
9011
  http3.get(`http://127.0.0.1:19280/api/providers/${providerType}/${endpoint}`, (res) => {
8886
9012
  let data = "";
8887
9013
  res.on("data", (chunk) => data += chunk);
8888
9014
  res.on("end", () => {
8889
9015
  try {
8890
- resolve9(JSON.parse(data));
9016
+ resolve12(JSON.parse(data));
8891
9017
  } catch {
8892
- resolve9({ raw: data });
9018
+ resolve12({ raw: data });
8893
9019
  }
8894
9020
  });
8895
9021
  }).on("error", reject);
@@ -8903,7 +9029,7 @@ var DaemonCommandHandler = class {
8903
9029
  try {
8904
9030
  const http3 = await import("http");
8905
9031
  const postData = JSON.stringify(args || {});
8906
- const result = await new Promise((resolve9, reject) => {
9032
+ const result = await new Promise((resolve12, reject) => {
8907
9033
  const req = http3.request({
8908
9034
  hostname: "127.0.0.1",
8909
9035
  port: 19280,
@@ -8915,9 +9041,9 @@ var DaemonCommandHandler = class {
8915
9041
  res.on("data", (chunk) => data += chunk);
8916
9042
  res.on("end", () => {
8917
9043
  try {
8918
- resolve9(JSON.parse(data));
9044
+ resolve12(JSON.parse(data));
8919
9045
  } catch {
8920
- resolve9({ raw: data });
9046
+ resolve12({ raw: data });
8921
9047
  }
8922
9048
  });
8923
9049
  });
@@ -8934,7 +9060,7 @@ var DaemonCommandHandler = class {
8934
9060
 
8935
9061
  // src/commands/cli-manager.ts
8936
9062
  var os10 = __toESM(require("os"));
8937
- var path9 = __toESM(require("path"));
9063
+ var path11 = __toESM(require("path"));
8938
9064
  var crypto4 = __toESM(require("crypto"));
8939
9065
  var import_chalk = __toESM(require("chalk"));
8940
9066
  init_provider_cli_adapter();
@@ -8942,7 +9068,7 @@ init_config();
8942
9068
 
8943
9069
  // src/providers/cli-provider-instance.ts
8944
9070
  var os9 = __toESM(require("os"));
8945
- var path8 = __toESM(require("path"));
9071
+ var path10 = __toESM(require("path"));
8946
9072
  var crypto3 = __toESM(require("crypto"));
8947
9073
  var fs5 = __toESM(require("fs"));
8948
9074
  var import_node_module = require("module");
@@ -8951,7 +9077,7 @@ init_logger();
8951
9077
  var CachedDatabaseSync = null;
8952
9078
  function getDatabaseSync() {
8953
9079
  if (CachedDatabaseSync) return CachedDatabaseSync;
8954
- const requireFn = typeof require === "function" ? require : (0, import_node_module.createRequire)(path8.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
9080
+ const requireFn = typeof require === "function" ? require : (0, import_node_module.createRequire)(path10.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
8955
9081
  const sqliteModule = requireFn(`node:${"sqlite"}`);
8956
9082
  CachedDatabaseSync = sqliteModule.DatabaseSync;
8957
9083
  if (!CachedDatabaseSync) {
@@ -9016,6 +9142,7 @@ var CliProviderInstance = class {
9016
9142
  this.detectStatusTransition();
9017
9143
  });
9018
9144
  await this.adapter.spawn();
9145
+ this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
9019
9146
  if (this.providerSessionId) {
9020
9147
  const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
9021
9148
  if (restoredHistory.messages.length > 0) {
@@ -9110,6 +9237,7 @@ var CliProviderInstance = class {
9110
9237
  this.promoteProviderSessionId(parsedProviderSessionId);
9111
9238
  }
9112
9239
  const runtime = this.adapter.getRuntimeMetadata();
9240
+ this.maybeAppendRuntimeRecoveryMessage(runtime);
9113
9241
  const parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
9114
9242
  const controlValues = extractProviderControlValues(this.provider.controls, parsedStatus);
9115
9243
  if (controlValues) {
@@ -9436,6 +9564,28 @@ ${effect.notification.body || ""}`.trim();
9436
9564
  const pad = (value) => String(value).padStart(2, "0");
9437
9565
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
9438
9566
  }
9567
+ maybeAppendRuntimeRecoveryMessage(runtime) {
9568
+ if (!runtime?.restoredFromStorage || !runtime.runtimeId) return;
9569
+ const recoveryState = String(runtime.recoveryState || "").trim();
9570
+ if (!recoveryState) return;
9571
+ let content = "";
9572
+ if (recoveryState === "auto_resumed") {
9573
+ content = "Session host restored this CLI after restart and reattached it from a saved snapshot.";
9574
+ } else if (recoveryState === "resume_failed") {
9575
+ const errorSuffix = runtime.recoveryError ? ` Resume failed: ${runtime.recoveryError}` : "";
9576
+ content = `Session host found this CLI after restart, but automatic resume failed.${errorSuffix}`;
9577
+ } else if (recoveryState === "host_restart_interrupted") {
9578
+ content = "Session host found this CLI in interrupted state after restart and is attempting to resume it.";
9579
+ } else if (recoveryState === "orphan_snapshot") {
9580
+ content = "Session host restored the last snapshot for this CLI, but the original runtime was not resumed automatically.";
9581
+ } else {
9582
+ content = `Session host restored this CLI after restart (${recoveryState}).`;
9583
+ }
9584
+ this.appendRuntimeSystemMessage(
9585
+ content,
9586
+ `runtime_recovery:${runtime.runtimeId}:${recoveryState}`
9587
+ );
9588
+ }
9439
9589
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
9440
9590
  const normalizedContent = String(content || "").trim();
9441
9591
  if (!normalizedContent) return;
@@ -9756,8 +9906,9 @@ var AcpProviderInstance = class {
9756
9906
  async setConfigOption(category, value) {
9757
9907
  const opt = this.configOptions.find((c) => c.category === category);
9758
9908
  if (!opt) {
9759
- this.log.warn(`[${this.type}] No config option for category: ${category}`);
9760
- return;
9909
+ const message = `[${this.type}] No config option for category: ${category}`;
9910
+ this.log.warn(message);
9911
+ throw new Error(message);
9761
9912
  }
9762
9913
  if (this.useStaticConfig) {
9763
9914
  opt.currentValue = value;
@@ -9769,8 +9920,9 @@ var AcpProviderInstance = class {
9769
9920
  return;
9770
9921
  }
9771
9922
  if (!this.connection || !this.sessionId) {
9772
- this.log.warn(`[${this.type}] Cannot set config: no active connection/session`);
9773
- return;
9923
+ const message = `[${this.type}] Cannot set config: no active connection/session`;
9924
+ this.log.warn(message);
9925
+ throw new Error(message);
9774
9926
  }
9775
9927
  try {
9776
9928
  this.log.info(`[${this.type}] Sending session/set_config_option: configId=${opt.configId} value=${value} sessionId=${this.sessionId}`);
@@ -9784,7 +9936,9 @@ var AcpProviderInstance = class {
9784
9936
  if (result?.configOptions) this.parseConfigOptions(result.configOptions);
9785
9937
  this.log.info(`[${this.type}] Config ${category} set to: ${value} | response: ${JSON.stringify(result)?.slice(0, 300)}`);
9786
9938
  } catch (e) {
9787
- this.log.warn(`[${this.type}] set_config_option failed: ${e?.message}`);
9939
+ const message = e?.message || "Unknown ACP config error";
9940
+ this.log.warn(`[${this.type}] set_config_option failed: ${message}`);
9941
+ throw new Error(message);
9788
9942
  }
9789
9943
  }
9790
9944
  async setMode(modeId) {
@@ -9800,8 +9954,9 @@ var AcpProviderInstance = class {
9800
9954
  return;
9801
9955
  }
9802
9956
  if (!this.connection || !this.sessionId) {
9803
- this.log.warn(`[${this.type}] Cannot set mode: no active connection/session`);
9804
- return;
9957
+ const message = `[${this.type}] Cannot set mode: no active connection/session`;
9958
+ this.log.warn(message);
9959
+ throw new Error(message);
9805
9960
  }
9806
9961
  try {
9807
9962
  await this.connection.setSessionMode({
@@ -9811,7 +9966,9 @@ var AcpProviderInstance = class {
9811
9966
  this.currentMode = modeId;
9812
9967
  this.log.info(`[${this.type}] Mode set to: ${modeId}`);
9813
9968
  } catch (e) {
9814
- this.log.warn(`[${this.type}] set_mode failed: ${e?.message}`);
9969
+ const message = e?.message || "Unknown ACP mode error";
9970
+ this.log.warn(`[${this.type}] set_mode failed: ${message}`);
9971
+ throw new Error(message);
9815
9972
  }
9816
9973
  }
9817
9974
  /** Static config: kill process and restart with new args */
@@ -9859,7 +10016,7 @@ var AcpProviderInstance = class {
9859
10016
  if (!spawnConfig) {
9860
10017
  throw new Error(`[ACP:${this.type}] No spawn config defined`);
9861
10018
  }
9862
- const command = spawnConfig.command;
10019
+ const command = typeof this.settings.executablePath === "string" && this.settings.executablePath.trim() ? this.settings.executablePath.trim() : spawnConfig.command;
9863
10020
  let baseArgs = spawnConfig.args || [];
9864
10021
  if (this.provider.spawnArgBuilder && Object.keys(this.selectedConfig).length > 0) {
9865
10022
  baseArgs = this.provider.spawnArgBuilder(this.selectedConfig);
@@ -9975,13 +10132,13 @@ var AcpProviderInstance = class {
9975
10132
  }
9976
10133
  this.currentStatus = "waiting_approval";
9977
10134
  this.detectStatusTransition();
9978
- const approved = await new Promise((resolve9) => {
9979
- this.permissionResolvers.push(resolve9);
10135
+ const approved = await new Promise((resolve12) => {
10136
+ this.permissionResolvers.push(resolve12);
9980
10137
  setTimeout(() => {
9981
- const idx = this.permissionResolvers.indexOf(resolve9);
10138
+ const idx = this.permissionResolvers.indexOf(resolve12);
9982
10139
  if (idx >= 0) {
9983
10140
  this.permissionResolvers.splice(idx, 1);
9984
- resolve9(false);
10141
+ resolve12(false);
9985
10142
  }
9986
10143
  }, 3e5);
9987
10144
  });
@@ -10688,7 +10845,7 @@ var DaemonCliManager = class {
10688
10845
  async startSession(cliType, workingDir, cliArgs, initialModel, options) {
10689
10846
  const trimmed = (workingDir || "").trim();
10690
10847
  if (!trimmed) throw new Error("working directory required");
10691
- const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path9.resolve(trimmed);
10848
+ const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path11.resolve(trimmed);
10692
10849
  const normalizedType = this.providerLoader.resolveAlias(cliType);
10693
10850
  const provider = this.providerLoader.getByAlias(cliType);
10694
10851
  const key = crypto4.randomUUID();
@@ -10775,10 +10932,10 @@ ${installInfo}`
10775
10932
  if (!cliInfo) {
10776
10933
  const installHint = provider?.install || "";
10777
10934
  const displayName = provider?.displayName || provider?.name || cliType;
10778
- const spawnCmd = provider?.spawn?.command || cliType;
10935
+ const spawnCmd = this.providerLoader.getSpawnCommand(normalizedType, provider?.spawn?.command || cliType);
10779
10936
  throw new Error(
10780
10937
  `${displayName} is not installed.
10781
- Command '${spawnCmd}' not found on PATH.
10938
+ Command '${spawnCmd}' is not available.
10782
10939
  ` + (installHint ? `
10783
10940
  ${installHint}
10784
10941
  ` : "") + `
@@ -11138,16 +11295,17 @@ Run 'adhdev doctor' for detailed diagnostics.`
11138
11295
  var import_child_process6 = require("child_process");
11139
11296
  var net = __toESM(require("net"));
11140
11297
  var os12 = __toESM(require("os"));
11141
- var path11 = __toESM(require("path"));
11298
+ var path13 = __toESM(require("path"));
11142
11299
 
11143
11300
  // src/providers/provider-loader.ts
11144
11301
  var fs6 = __toESM(require("fs"));
11145
- var path10 = __toESM(require("path"));
11302
+ var path12 = __toESM(require("path"));
11146
11303
  var os11 = __toESM(require("os"));
11147
11304
  var chokidar = __toESM(require("chokidar"));
11148
11305
  init_logger();
11149
11306
  var ProviderLoader = class _ProviderLoader {
11150
11307
  providers = /* @__PURE__ */ new Map();
11308
+ providerAvailability = /* @__PURE__ */ new Map();
11151
11309
  userDir;
11152
11310
  upstreamDir;
11153
11311
  disableUpstream;
@@ -11163,12 +11321,12 @@ var ProviderLoader = class _ProviderLoader {
11163
11321
  static META_FILE = ".meta.json";
11164
11322
  constructor(options) {
11165
11323
  this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
11166
- const defaultProvidersDir = path10.join(os11.homedir(), ".adhdev", "providers");
11324
+ const defaultProvidersDir = path12.join(os11.homedir(), ".adhdev", "providers");
11167
11325
  if (options?.userDir) {
11168
11326
  this.userDir = options.userDir;
11169
11327
  this.log(`Config 'providerDir' applied: ${this.userDir}`);
11170
11328
  } else {
11171
- const localRepoPath = path10.resolve(__dirname, "../../../../../adhdev-providers");
11329
+ const localRepoPath = path12.resolve(__dirname, "../../../../../adhdev-providers");
11172
11330
  if (fs6.existsSync(localRepoPath)) {
11173
11331
  this.userDir = localRepoPath;
11174
11332
  this.log(`Auto-detected local public repository: ${this.userDir} (Dev workspace speedup)`);
@@ -11177,7 +11335,7 @@ var ProviderLoader = class _ProviderLoader {
11177
11335
  this.log(`Using default user providers directory: ${this.userDir}`);
11178
11336
  }
11179
11337
  }
11180
- this.upstreamDir = path10.join(defaultProvidersDir, ".upstream");
11338
+ this.upstreamDir = path12.join(defaultProvidersDir, ".upstream");
11181
11339
  this.disableUpstream = options?.disableUpstream ?? false;
11182
11340
  }
11183
11341
  log(msg) {
@@ -11207,7 +11365,7 @@ var ProviderLoader = class _ProviderLoader {
11207
11365
  * Canonical provider directory shape for a given root.
11208
11366
  */
11209
11367
  getProviderDir(root, category, type) {
11210
- return path10.join(root, category, type);
11368
+ return path12.join(root, category, type);
11211
11369
  }
11212
11370
  /**
11213
11371
  * Canonical user override directory for a provider.
@@ -11234,7 +11392,7 @@ var ProviderLoader = class _ProviderLoader {
11234
11392
  resolveProviderFile(type, ...segments) {
11235
11393
  const dir = this.findProviderDirInternal(type);
11236
11394
  if (!dir) return null;
11237
- return path10.join(dir, ...segments);
11395
+ return path12.join(dir, ...segments);
11238
11396
  }
11239
11397
  /**
11240
11398
  * Load all providers (3-tier priority)
@@ -11245,6 +11403,7 @@ var ProviderLoader = class _ProviderLoader {
11245
11403
  */
11246
11404
  loadAll() {
11247
11405
  this.providers.clear();
11406
+ this.providerAvailability.clear();
11248
11407
  let upstreamCount = 0;
11249
11408
  if (!this.disableUpstream && fs6.existsSync(this.upstreamDir)) {
11250
11409
  upstreamCount = this.loadDir(this.upstreamDir);
@@ -11272,7 +11431,7 @@ var ProviderLoader = class _ProviderLoader {
11272
11431
  if (!fs6.existsSync(this.upstreamDir)) return false;
11273
11432
  try {
11274
11433
  return fs6.readdirSync(this.upstreamDir).some(
11275
- (d) => fs6.statSync(path10.join(this.upstreamDir, d)).isDirectory()
11434
+ (d) => fs6.statSync(path12.join(this.upstreamDir, d)).isDirectory()
11276
11435
  );
11277
11436
  } catch {
11278
11437
  return false;
@@ -11315,11 +11474,12 @@ var ProviderLoader = class _ProviderLoader {
11315
11474
  if ((p.category === "cli" || p.category === "acp") && p.spawn?.command) {
11316
11475
  const verCmdConfig = p.versionCommand;
11317
11476
  const versionCommand = typeof verCmdConfig === "object" && verCmdConfig !== null ? verCmdConfig[process.platform] : verCmdConfig;
11477
+ const command = this.getSpawnCommand(p.type, p.spawn.command);
11318
11478
  result.push({
11319
11479
  id: p.type,
11320
11480
  displayName: p.displayName || p.name,
11321
11481
  icon: p.icon || "\u{1F527}",
11322
- command: p.spawn.command,
11482
+ command,
11323
11483
  category: p.category,
11324
11484
  ...typeof versionCommand === "string" && versionCommand.trim() ? { versionCommand: versionCommand.trim() } : {}
11325
11485
  });
@@ -11448,6 +11608,71 @@ var ProviderLoader = class _ProviderLoader {
11448
11608
  getAvailableIdeTypes() {
11449
11609
  return [...this.providers.values()].filter((p) => p.category === "ide" && p.cdpPorts).map((p) => p.type);
11450
11610
  }
11611
+ getSpawnCommand(type, fallback) {
11612
+ const override = this.getOptionalStringSetting(type, "executablePath");
11613
+ if (override) return override;
11614
+ return fallback || this.providers.get(type)?.spawn?.command || type;
11615
+ }
11616
+ getIdeCliCommand(type, fallback) {
11617
+ const override = this.getOptionalStringSetting(type, "cliPathOverride");
11618
+ if (override) return override;
11619
+ return fallback || this.providers.get(type)?.cli || null;
11620
+ }
11621
+ getIdePathCandidates(type, fallback) {
11622
+ const override = this.getOptionalStringSetting(type, "appPathOverride");
11623
+ if (override) return [override];
11624
+ if (fallback && fallback.length > 0) return fallback;
11625
+ const osPaths = this.providers.get(type)?.paths?.[process.platform];
11626
+ return Array.isArray(osPaths) ? [...osPaths] : [];
11627
+ }
11628
+ setProviderAvailability(type, state) {
11629
+ this.providerAvailability.set(type, {
11630
+ installed: !!state.installed,
11631
+ detectedPath: state.detectedPath ?? null
11632
+ });
11633
+ }
11634
+ setCliDetectionResults(results, replace = true) {
11635
+ if (replace) {
11636
+ for (const provider of this.providers.values()) {
11637
+ if (provider.category === "cli" || provider.category === "acp") {
11638
+ this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
11639
+ }
11640
+ }
11641
+ }
11642
+ for (const result of results) {
11643
+ this.setProviderAvailability(result.id, {
11644
+ installed: !!result.installed,
11645
+ detectedPath: result.path || null
11646
+ });
11647
+ }
11648
+ }
11649
+ setIdeDetectionResults(results, replace = true) {
11650
+ if (replace) {
11651
+ for (const provider of this.providers.values()) {
11652
+ if (provider.category === "ide") {
11653
+ this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
11654
+ }
11655
+ }
11656
+ }
11657
+ for (const result of results) {
11658
+ this.setProviderAvailability(result.id, {
11659
+ installed: !!result.installed,
11660
+ detectedPath: result.cliCommand || result.path || null
11661
+ });
11662
+ }
11663
+ }
11664
+ getAvailableProviderInfos() {
11665
+ return this.getAll().map((provider) => {
11666
+ const availability = this.providerAvailability.get(provider.type);
11667
+ return {
11668
+ ...provider,
11669
+ ...availability ? {
11670
+ installed: availability.installed,
11671
+ detectedPath: availability.detectedPath
11672
+ } : {}
11673
+ };
11674
+ });
11675
+ }
11451
11676
  /**
11452
11677
  * Register IDE providers to core/detector registry
11453
11678
  * → Enables detectIDEs() to detect provider.js-based IDEs
@@ -11522,8 +11747,8 @@ var ProviderLoader = class _ProviderLoader {
11522
11747
  resolved._resolvedScriptDir = entry.scriptDir;
11523
11748
  resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
11524
11749
  if (providerDir) {
11525
- const fullDir = path10.join(providerDir, entry.scriptDir);
11526
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
11750
+ const fullDir = path12.join(providerDir, entry.scriptDir);
11751
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11527
11752
  }
11528
11753
  matched = true;
11529
11754
  }
@@ -11538,8 +11763,8 @@ var ProviderLoader = class _ProviderLoader {
11538
11763
  resolved._resolvedScriptDir = base.defaultScriptDir;
11539
11764
  resolved._resolvedScriptsSource = "defaultScriptDir:version_miss";
11540
11765
  if (providerDir) {
11541
- const fullDir = path10.join(providerDir, base.defaultScriptDir);
11542
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
11766
+ const fullDir = path12.join(providerDir, base.defaultScriptDir);
11767
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11543
11768
  }
11544
11769
  }
11545
11770
  resolved._versionWarning = `Version ${currentVersion} not in compatibility matrix. Using default scripts.`;
@@ -11556,8 +11781,8 @@ var ProviderLoader = class _ProviderLoader {
11556
11781
  resolved._resolvedScriptDir = dirOverride;
11557
11782
  resolved._resolvedScriptsSource = `versions:${range}`;
11558
11783
  if (providerDir) {
11559
- const fullDir = path10.join(providerDir, dirOverride);
11560
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
11784
+ const fullDir = path12.join(providerDir, dirOverride);
11785
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11561
11786
  }
11562
11787
  }
11563
11788
  } else if (override.scripts) {
@@ -11573,8 +11798,8 @@ var ProviderLoader = class _ProviderLoader {
11573
11798
  resolved._resolvedScriptDir = base.defaultScriptDir;
11574
11799
  resolved._resolvedScriptsSource = "defaultScriptDir:no_version";
11575
11800
  if (providerDir) {
11576
- const fullDir = path10.join(providerDir, base.defaultScriptDir);
11577
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
11801
+ const fullDir = path12.join(providerDir, base.defaultScriptDir);
11802
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11578
11803
  }
11579
11804
  }
11580
11805
  }
@@ -11599,14 +11824,14 @@ var ProviderLoader = class _ProviderLoader {
11599
11824
  this.log(` [loadScriptsFromDir] ${type}: providerDir not found`);
11600
11825
  return null;
11601
11826
  }
11602
- const dir = path10.join(providerDir, scriptDir);
11827
+ const dir = path12.join(providerDir, scriptDir);
11603
11828
  if (!fs6.existsSync(dir)) {
11604
11829
  this.log(` [loadScriptsFromDir] ${type}: dir not found: ${dir}`);
11605
11830
  return null;
11606
11831
  }
11607
11832
  const cached = this.scriptsCache.get(dir);
11608
11833
  if (cached) return cached;
11609
- const scriptsJs = path10.join(dir, "scripts.js");
11834
+ const scriptsJs = path12.join(dir, "scripts.js");
11610
11835
  if (fs6.existsSync(scriptsJs)) {
11611
11836
  try {
11612
11837
  delete require.cache[require.resolve(scriptsJs)];
@@ -11648,7 +11873,7 @@ var ProviderLoader = class _ProviderLoader {
11648
11873
  return;
11649
11874
  }
11650
11875
  if (filePath.endsWith(".js") || filePath.endsWith(".json")) {
11651
- this.log(`File changed: ${path10.basename(filePath)}, reloading...`);
11876
+ this.log(`File changed: ${path12.basename(filePath)}, reloading...`);
11652
11877
  this.reload();
11653
11878
  }
11654
11879
  };
@@ -11703,7 +11928,7 @@ var ProviderLoader = class _ProviderLoader {
11703
11928
  }
11704
11929
  const https = require("https");
11705
11930
  const { execSync: execSync7 } = require("child_process");
11706
- const metaPath = path10.join(this.upstreamDir, _ProviderLoader.META_FILE);
11931
+ const metaPath = path12.join(this.upstreamDir, _ProviderLoader.META_FILE);
11707
11932
  let prevEtag = "";
11708
11933
  let prevTimestamp = 0;
11709
11934
  try {
@@ -11720,7 +11945,7 @@ var ProviderLoader = class _ProviderLoader {
11720
11945
  return { updated: false };
11721
11946
  }
11722
11947
  try {
11723
- const etag = await new Promise((resolve9, reject) => {
11948
+ const etag = await new Promise((resolve12, reject) => {
11724
11949
  const options = {
11725
11950
  method: "HEAD",
11726
11951
  hostname: "github.com",
@@ -11738,7 +11963,7 @@ var ProviderLoader = class _ProviderLoader {
11738
11963
  headers: { "User-Agent": "adhdev-launcher" },
11739
11964
  timeout: 1e4
11740
11965
  }, (res2) => {
11741
- resolve9(res2.headers.etag || res2.headers["last-modified"] || "");
11966
+ resolve12(res2.headers.etag || res2.headers["last-modified"] || "");
11742
11967
  });
11743
11968
  req2.on("error", reject);
11744
11969
  req2.on("timeout", () => {
@@ -11747,7 +11972,7 @@ var ProviderLoader = class _ProviderLoader {
11747
11972
  });
11748
11973
  req2.end();
11749
11974
  } else {
11750
- resolve9(res.headers.etag || res.headers["last-modified"] || "");
11975
+ resolve12(res.headers.etag || res.headers["last-modified"] || "");
11751
11976
  }
11752
11977
  });
11753
11978
  req.on("error", reject);
@@ -11763,17 +11988,17 @@ var ProviderLoader = class _ProviderLoader {
11763
11988
  return { updated: false };
11764
11989
  }
11765
11990
  this.log("Downloading latest providers from GitHub...");
11766
- const tmpTar = path10.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
11767
- const tmpExtract = path10.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
11991
+ const tmpTar = path12.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
11992
+ const tmpExtract = path12.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
11768
11993
  await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
11769
11994
  fs6.mkdirSync(tmpExtract, { recursive: true });
11770
11995
  execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
11771
11996
  const extracted = fs6.readdirSync(tmpExtract);
11772
11997
  const rootDir = extracted.find(
11773
- (d) => fs6.statSync(path10.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
11998
+ (d) => fs6.statSync(path12.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
11774
11999
  );
11775
12000
  if (!rootDir) throw new Error("Unexpected tarball structure");
11776
- const sourceDir = path10.join(tmpExtract, rootDir);
12001
+ const sourceDir = path12.join(tmpExtract, rootDir);
11777
12002
  const backupDir = this.upstreamDir + ".bak";
11778
12003
  if (fs6.existsSync(this.upstreamDir)) {
11779
12004
  if (fs6.existsSync(backupDir)) fs6.rmSync(backupDir, { recursive: true, force: true });
@@ -11811,7 +12036,7 @@ var ProviderLoader = class _ProviderLoader {
11811
12036
  downloadFile(url, destPath) {
11812
12037
  const https = require("https");
11813
12038
  const http3 = require("http");
11814
- return new Promise((resolve9, reject) => {
12039
+ return new Promise((resolve12, reject) => {
11815
12040
  const doRequest = (reqUrl, redirectCount = 0) => {
11816
12041
  if (redirectCount > 5) {
11817
12042
  reject(new Error("Too many redirects"));
@@ -11831,7 +12056,7 @@ var ProviderLoader = class _ProviderLoader {
11831
12056
  res.pipe(ws);
11832
12057
  ws.on("finish", () => {
11833
12058
  ws.close();
11834
- resolve9();
12059
+ resolve12();
11835
12060
  });
11836
12061
  ws.on("error", reject);
11837
12062
  });
@@ -11848,8 +12073,8 @@ var ProviderLoader = class _ProviderLoader {
11848
12073
  copyDirRecursive(src, dest) {
11849
12074
  fs6.mkdirSync(dest, { recursive: true });
11850
12075
  for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
11851
- const srcPath = path10.join(src, entry.name);
11852
- const destPath = path10.join(dest, entry.name);
12076
+ const srcPath = path12.join(src, entry.name);
12077
+ const destPath = path12.join(dest, entry.name);
11853
12078
  if (entry.isDirectory()) {
11854
12079
  this.copyDirRecursive(srcPath, destPath);
11855
12080
  } else {
@@ -11860,7 +12085,7 @@ var ProviderLoader = class _ProviderLoader {
11860
12085
  /** .meta.json save */
11861
12086
  writeMeta(metaPath, etag, timestamp) {
11862
12087
  try {
11863
- fs6.mkdirSync(path10.dirname(metaPath), { recursive: true });
12088
+ fs6.mkdirSync(path12.dirname(metaPath), { recursive: true });
11864
12089
  fs6.writeFileSync(metaPath, JSON.stringify({
11865
12090
  etag,
11866
12091
  timestamp,
@@ -11877,7 +12102,7 @@ var ProviderLoader = class _ProviderLoader {
11877
12102
  const scan = (d) => {
11878
12103
  try {
11879
12104
  for (const entry of fs6.readdirSync(d, { withFileTypes: true })) {
11880
- if (entry.isDirectory()) scan(path10.join(d, entry.name));
12105
+ if (entry.isDirectory()) scan(path12.join(d, entry.name));
11881
12106
  else if (entry.name === "provider.json") count++;
11882
12107
  }
11883
12108
  } catch {
@@ -11891,9 +12116,8 @@ var ProviderLoader = class _ProviderLoader {
11891
12116
  * Get public settings schema for a provider (for dashboard UI rendering)
11892
12117
  */
11893
12118
  getPublicSettings(type) {
11894
- const provider = this.providers.get(type);
11895
- if (!provider?.settings) return [];
11896
- return Object.entries(provider.settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
12119
+ const settings = this.getSettingsSchema(type);
12120
+ return Object.entries(settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
11897
12121
  }
11898
12122
  /**
11899
12123
  * Get public settings schema for all providers
@@ -11910,8 +12134,7 @@ var ProviderLoader = class _ProviderLoader {
11910
12134
  * Resolved setting value for a provider (default + user override)
11911
12135
  */
11912
12136
  getSettingValue(type, key) {
11913
- const provider = this.providers.get(type);
11914
- const schemaDef = provider?.settings?.[key];
12137
+ const schemaDef = this.getSettingsSchema(type)[key];
11915
12138
  const defaultVal = schemaDef ? schemaDef.default : void 0;
11916
12139
  try {
11917
12140
  const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
@@ -11926,10 +12149,9 @@ var ProviderLoader = class _ProviderLoader {
11926
12149
  * All resolved settings for a provider (default + user override)
11927
12150
  */
11928
12151
  getSettings(type) {
11929
- const provider = this.providers.get(type);
11930
- if (!provider?.settings) return {};
12152
+ const settings = this.getSettingsSchema(type);
11931
12153
  const result = {};
11932
- for (const [key, def] of Object.entries(provider.settings)) {
12154
+ for (const [key] of Object.entries(settings)) {
11933
12155
  result[key] = this.getSettingValue(type, key);
11934
12156
  }
11935
12157
  return result;
@@ -11938,11 +12160,11 @@ var ProviderLoader = class _ProviderLoader {
11938
12160
  * Save provider setting value (writes to config.json)
11939
12161
  */
11940
12162
  setSetting(type, key, value) {
11941
- const provider = this.providers.get(type);
11942
- const schemaDef = provider?.settings?.[key];
12163
+ const schemaDef = this.getSettingsSchema(type)[key];
11943
12164
  if (!schemaDef) return false;
11944
12165
  if (!schemaDef.public) return false;
11945
12166
  if (schemaDef.type === "boolean" && typeof value !== "boolean") return false;
12167
+ if (schemaDef.type === "string" && typeof value !== "string") return false;
11946
12168
  if (schemaDef.type === "number") {
11947
12169
  if (typeof value !== "number") return false;
11948
12170
  if (schemaDef.min !== void 0 && value < schemaDef.min) return false;
@@ -11963,6 +12185,53 @@ var ProviderLoader = class _ProviderLoader {
11963
12185
  return false;
11964
12186
  }
11965
12187
  }
12188
+ getOptionalStringSetting(type, key) {
12189
+ const value = this.getSettingValue(type, key);
12190
+ if (typeof value !== "string") return null;
12191
+ const trimmed = value.trim();
12192
+ return trimmed ? trimmed : null;
12193
+ }
12194
+ getSettingsSchema(type) {
12195
+ const provider = this.providers.get(type);
12196
+ if (!provider) return {};
12197
+ return {
12198
+ ...this.getSyntheticSettings(type, provider),
12199
+ ...provider.settings || {}
12200
+ };
12201
+ }
12202
+ getSyntheticSettings(type, provider) {
12203
+ const result = {};
12204
+ if ((provider.category === "cli" || provider.category === "acp") && provider.spawn?.command && !provider.settings?.executablePath) {
12205
+ result.executablePath = {
12206
+ type: "string",
12207
+ default: "",
12208
+ public: true,
12209
+ label: "Executable path",
12210
+ description: "Optional absolute path for this provider binary. Leave blank to use the default PATH lookup."
12211
+ };
12212
+ }
12213
+ if (provider.category === "ide") {
12214
+ if (provider.cli && !provider.settings?.cliPathOverride) {
12215
+ result.cliPathOverride = {
12216
+ type: "string",
12217
+ default: "",
12218
+ public: true,
12219
+ label: "CLI path override",
12220
+ description: "Optional absolute path for the IDE CLI launcher. Leave blank to use the detected default."
12221
+ };
12222
+ }
12223
+ if (provider.paths && !provider.settings?.appPathOverride) {
12224
+ result.appPathOverride = {
12225
+ type: "string",
12226
+ default: "",
12227
+ public: true,
12228
+ label: "App path override",
12229
+ description: "Optional absolute path for the IDE app bundle or executable. Leave blank to use the default install locations."
12230
+ };
12231
+ }
12232
+ }
12233
+ return result;
12234
+ }
11966
12235
  // ─── Private ───────────────────────────────────
11967
12236
  /**
11968
12237
  * Find the on-disk directory for a provider by type.
@@ -11976,17 +12245,17 @@ var ProviderLoader = class _ProviderLoader {
11976
12245
  for (const root of searchRoots) {
11977
12246
  if (!fs6.existsSync(root)) continue;
11978
12247
  const candidate = this.getProviderDir(root, cat, type);
11979
- if (fs6.existsSync(path10.join(candidate, "provider.json"))) return candidate;
11980
- const catDir = path10.join(root, cat);
12248
+ if (fs6.existsSync(path12.join(candidate, "provider.json"))) return candidate;
12249
+ const catDir = path12.join(root, cat);
11981
12250
  if (fs6.existsSync(catDir)) {
11982
12251
  try {
11983
12252
  for (const entry of fs6.readdirSync(catDir, { withFileTypes: true })) {
11984
12253
  if (!entry.isDirectory()) continue;
11985
- const jsonPath = path10.join(catDir, entry.name, "provider.json");
12254
+ const jsonPath = path12.join(catDir, entry.name, "provider.json");
11986
12255
  if (fs6.existsSync(jsonPath)) {
11987
12256
  try {
11988
12257
  const data = JSON.parse(fs6.readFileSync(jsonPath, "utf-8"));
11989
- if (data.type === type) return path10.join(catDir, entry.name);
12258
+ if (data.type === type) return path12.join(catDir, entry.name);
11990
12259
  } catch {
11991
12260
  }
11992
12261
  }
@@ -12003,7 +12272,7 @@ var ProviderLoader = class _ProviderLoader {
12003
12272
  * (template substitution is NOT applied here — scripts.js handles that)
12004
12273
  */
12005
12274
  buildScriptWrappersFromDir(dir) {
12006
- const scriptsJs = path10.join(dir, "scripts.js");
12275
+ const scriptsJs = path12.join(dir, "scripts.js");
12007
12276
  if (fs6.existsSync(scriptsJs)) {
12008
12277
  try {
12009
12278
  delete require.cache[require.resolve(scriptsJs)];
@@ -12017,7 +12286,7 @@ var ProviderLoader = class _ProviderLoader {
12017
12286
  for (const file of fs6.readdirSync(dir)) {
12018
12287
  if (!file.endsWith(".js")) continue;
12019
12288
  const scriptName = toCamel(file.replace(".js", ""));
12020
- const filePath = path10.join(dir, file);
12289
+ const filePath = path12.join(dir, file);
12021
12290
  result[scriptName] = (...args) => {
12022
12291
  try {
12023
12292
  let content = fs6.readFileSync(filePath, "utf-8");
@@ -12077,7 +12346,7 @@ var ProviderLoader = class _ProviderLoader {
12077
12346
  }
12078
12347
  const hasJson = entries.some((e) => e.name === "provider.json");
12079
12348
  if (hasJson) {
12080
- const jsonPath = path10.join(d, "provider.json");
12349
+ const jsonPath = path12.join(d, "provider.json");
12081
12350
  try {
12082
12351
  const raw = fs6.readFileSync(jsonPath, "utf-8");
12083
12352
  const mod = JSON.parse(raw);
@@ -12090,7 +12359,7 @@ var ProviderLoader = class _ProviderLoader {
12090
12359
  delete mod.extensionIdPattern_flags;
12091
12360
  }
12092
12361
  const hasCompatibility = Array.isArray(mod.compatibility);
12093
- const scriptsPath = path10.join(d, "scripts.js");
12362
+ const scriptsPath = path12.join(d, "scripts.js");
12094
12363
  if (!hasCompatibility && fs6.existsSync(scriptsPath)) {
12095
12364
  try {
12096
12365
  delete require.cache[require.resolve(scriptsPath)];
@@ -12116,7 +12385,7 @@ var ProviderLoader = class _ProviderLoader {
12116
12385
  if (!entry.isDirectory()) continue;
12117
12386
  if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
12118
12387
  if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
12119
- scan(path10.join(d, entry.name));
12388
+ scan(path12.join(d, entry.name));
12120
12389
  }
12121
12390
  }
12122
12391
  };
@@ -12212,17 +12481,17 @@ async function findFreePort(ports) {
12212
12481
  throw new Error("No free port found");
12213
12482
  }
12214
12483
  function checkPortFree(port) {
12215
- return new Promise((resolve9) => {
12484
+ return new Promise((resolve12) => {
12216
12485
  const server = net.createServer();
12217
12486
  server.unref();
12218
- server.on("error", () => resolve9(false));
12487
+ server.on("error", () => resolve12(false));
12219
12488
  server.listen(port, "127.0.0.1", () => {
12220
- server.close(() => resolve9(true));
12489
+ server.close(() => resolve12(true));
12221
12490
  });
12222
12491
  });
12223
12492
  }
12224
12493
  async function isCdpActive(port) {
12225
- return new Promise((resolve9) => {
12494
+ return new Promise((resolve12) => {
12226
12495
  const req = require("http").get(`http://127.0.0.1:${port}/json/version`, {
12227
12496
  timeout: 2e3
12228
12497
  }, (res) => {
@@ -12231,16 +12500,16 @@ async function isCdpActive(port) {
12231
12500
  res.on("end", () => {
12232
12501
  try {
12233
12502
  const info = JSON.parse(data);
12234
- resolve9(!!info["WebKit-Version"] || !!info["Browser"]);
12503
+ resolve12(!!info["WebKit-Version"] || !!info["Browser"]);
12235
12504
  } catch {
12236
- resolve9(false);
12505
+ resolve12(false);
12237
12506
  }
12238
12507
  });
12239
12508
  });
12240
- req.on("error", () => resolve9(false));
12509
+ req.on("error", () => resolve12(false));
12241
12510
  req.on("timeout", () => {
12242
12511
  req.destroy();
12243
- resolve9(false);
12512
+ resolve12(false);
12244
12513
  });
12245
12514
  });
12246
12515
  }
@@ -12374,8 +12643,8 @@ function detectCurrentWorkspace(ideId) {
12374
12643
  const appNameMap = getMacAppIdentifiers();
12375
12644
  const appName = appNameMap[ideId];
12376
12645
  if (appName) {
12377
- const storagePath = path11.join(
12378
- process.env.APPDATA || path11.join(os12.homedir(), "AppData", "Roaming"),
12646
+ const storagePath = path13.join(
12647
+ process.env.APPDATA || path13.join(os12.homedir(), "AppData", "Roaming"),
12379
12648
  appName,
12380
12649
  "storage.json"
12381
12650
  );
@@ -12399,7 +12668,7 @@ function detectCurrentWorkspace(ideId) {
12399
12668
  async function launchWithCdp(options = {}) {
12400
12669
  const platform9 = os12.platform();
12401
12670
  let targetIde;
12402
- const ides = await detectIDEs();
12671
+ const ides = await detectIDEs(getProviderLoader());
12403
12672
  if (options.ideId) {
12404
12673
  targetIde = ides.find((i) => i.id === options.ideId && i.installed);
12405
12674
  if (!targetIde) {
@@ -12553,9 +12822,9 @@ init_logger();
12553
12822
 
12554
12823
  // src/logging/command-log.ts
12555
12824
  var fs7 = __toESM(require("fs"));
12556
- var path12 = __toESM(require("path"));
12825
+ var path14 = __toESM(require("path"));
12557
12826
  var os13 = __toESM(require("os"));
12558
- var LOG_DIR2 = process.platform === "win32" ? path12.join(process.env.LOCALAPPDATA || process.env.APPDATA || path12.join(os13.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path12.join(os13.homedir(), "Library", "Logs", "adhdev") : path12.join(os13.homedir(), ".local", "share", "adhdev", "logs");
12827
+ var LOG_DIR2 = process.platform === "win32" ? path14.join(process.env.LOCALAPPDATA || process.env.APPDATA || path14.join(os13.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path14.join(os13.homedir(), "Library", "Logs", "adhdev") : path14.join(os13.homedir(), ".local", "share", "adhdev", "logs");
12559
12828
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
12560
12829
  var MAX_DAYS = 7;
12561
12830
  try {
@@ -12593,13 +12862,13 @@ function getDateStr2() {
12593
12862
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
12594
12863
  }
12595
12864
  var currentDate2 = getDateStr2();
12596
- var currentFile = path12.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
12865
+ var currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
12597
12866
  var writeCount2 = 0;
12598
12867
  function checkRotation() {
12599
12868
  const today = getDateStr2();
12600
12869
  if (today !== currentDate2) {
12601
12870
  currentDate2 = today;
12602
- currentFile = path12.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
12871
+ currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
12603
12872
  cleanOldFiles();
12604
12873
  }
12605
12874
  }
@@ -12613,7 +12882,7 @@ function cleanOldFiles() {
12613
12882
  const dateMatch = file.match(/commands-(\d{4}-\d{2}-\d{2})/);
12614
12883
  if (dateMatch && dateMatch[1] < cutoffStr) {
12615
12884
  try {
12616
- fs7.unlinkSync(path12.join(LOG_DIR2, file));
12885
+ fs7.unlinkSync(path14.join(LOG_DIR2, file));
12617
12886
  } catch {
12618
12887
  }
12619
12888
  }
@@ -12705,12 +12974,15 @@ function buildDetectedIdeInfos(detectedIdes, cdpManagers) {
12705
12974
  }));
12706
12975
  }
12707
12976
  function buildAvailableProviders(providerLoader) {
12708
- return providerLoader.getAll().map((provider) => ({
12977
+ const providers = providerLoader.getAvailableProviderInfos?.() || providerLoader.getAll();
12978
+ return providers.map((provider) => ({
12709
12979
  type: provider.type,
12710
12980
  name: provider.displayName || provider.type,
12711
12981
  displayName: provider.displayName || provider.type,
12712
12982
  icon: provider.icon || "\u{1F4BB}",
12713
- category: provider.category
12983
+ category: provider.category,
12984
+ ...provider.installed !== void 0 ? { installed: provider.installed } : {},
12985
+ ...provider.detectedPath !== void 0 ? { detectedPath: provider.detectedPath } : {}
12714
12986
  }));
12715
12987
  }
12716
12988
  function parseMessageTime(value) {
@@ -12724,17 +12996,17 @@ function parseMessageTime(value) {
12724
12996
  function getSessionMessageUpdatedAt(session) {
12725
12997
  const lastMessage = session.activeChat?.messages?.at?.(-1);
12726
12998
  if (!lastMessage) return 0;
12727
- return parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt) || 0;
12999
+ return parseMessageTime(lastMessage.receivedAt) || 0;
12728
13000
  }
12729
13001
  function getSessionCompletionMarker(session) {
12730
13002
  const lastMessage = session.activeChat?.messages?.at?.(-1);
12731
13003
  if (!lastMessage) return "";
12732
13004
  const role = typeof lastMessage.role === "string" ? lastMessage.role : "";
12733
- if (role === "user" || role === "human") return "";
13005
+ if (role === "user" || role === "human" || role === "system") return "";
12734
13006
  if (typeof lastMessage._turnKey === "string" && lastMessage._turnKey) return `turn:${lastMessage._turnKey}`;
12735
13007
  if (typeof lastMessage.id === "string" && lastMessage.id) return `id:${lastMessage.id}`;
12736
13008
  if (typeof lastMessage.index === "number" && Number.isFinite(lastMessage.index)) return `idx:${lastMessage.index}`;
12737
- const timestamp = parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt);
13009
+ const timestamp = parseMessageTime(lastMessage.receivedAt);
12738
13010
  return timestamp > 0 ? `ts:${timestamp}` : "";
12739
13011
  }
12740
13012
  function getSessionLastUsedAt(session) {
@@ -12751,7 +13023,7 @@ function getUnreadState(hasContentChange, status, lastUsedAt, lastSeenAt, lastRo
12751
13023
  if (status === "generating" || status === "starting") {
12752
13024
  return { unread: false, inboxBucket: "working" };
12753
13025
  }
12754
- const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human";
13026
+ const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human" && lastRole !== "system";
12755
13027
  return { unread, inboxBucket: unread ? "task_complete" : "idle" };
12756
13028
  }
12757
13029
  function buildRecentLaunches(recentActivity) {
@@ -12838,13 +13110,13 @@ var import_child_process7 = require("child_process");
12838
13110
  var import_child_process8 = require("child_process");
12839
13111
  var fs8 = __toESM(require("fs"));
12840
13112
  var os15 = __toESM(require("os"));
12841
- var path13 = __toESM(require("path"));
13113
+ var path15 = __toESM(require("path"));
12842
13114
  var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
12843
13115
  function getUpgradeLogPath() {
12844
13116
  const home = os15.homedir();
12845
- const dir = path13.join(home, ".adhdev");
13117
+ const dir = path15.join(home, ".adhdev");
12846
13118
  fs8.mkdirSync(dir, { recursive: true });
12847
- return path13.join(dir, "daemon-upgrade.log");
13119
+ return path15.join(dir, "daemon-upgrade.log");
12848
13120
  }
12849
13121
  function appendUpgradeLog(message) {
12850
13122
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
@@ -12877,14 +13149,14 @@ async function waitForPidExit(pid, timeoutMs) {
12877
13149
  while (Date.now() - start < timeoutMs) {
12878
13150
  try {
12879
13151
  process.kill(pid, 0);
12880
- await new Promise((resolve9) => setTimeout(resolve9, 250));
13152
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
12881
13153
  } catch {
12882
13154
  return;
12883
13155
  }
12884
13156
  }
12885
13157
  }
12886
13158
  function stopSessionHostProcesses(appName) {
12887
- const pidFile = path13.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
13159
+ const pidFile = path15.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
12888
13160
  try {
12889
13161
  if (fs8.existsSync(pidFile)) {
12890
13162
  const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
@@ -12913,7 +13185,7 @@ function stopSessionHostProcesses(appName) {
12913
13185
  }
12914
13186
  }
12915
13187
  function removeDaemonPidFile() {
12916
- const pidFile = path13.join(os15.homedir(), ".adhdev", "daemon.pid");
13188
+ const pidFile = path15.join(os15.homedir(), ".adhdev", "daemon.pid");
12917
13189
  try {
12918
13190
  fs8.unlinkSync(pidFile);
12919
13191
  } catch {
@@ -12924,7 +13196,7 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
12924
13196
  const npmRoot = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
12925
13197
  if (!npmRoot) return;
12926
13198
  const npmPrefix = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
12927
- const binDir = process.platform === "win32" ? npmPrefix : path13.join(npmPrefix, "bin");
13199
+ const binDir = process.platform === "win32" ? npmPrefix : path15.join(npmPrefix, "bin");
12928
13200
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
12929
13201
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
12930
13202
  if (pkgName === "@adhdev/daemon-standalone") {
@@ -12932,25 +13204,25 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
12932
13204
  }
12933
13205
  if (pkgName.startsWith("@")) {
12934
13206
  const [scope, name] = pkgName.split("/");
12935
- const scopeDir = path13.join(npmRoot, scope);
13207
+ const scopeDir = path15.join(npmRoot, scope);
12936
13208
  if (!fs8.existsSync(scopeDir)) return;
12937
13209
  for (const entry of fs8.readdirSync(scopeDir)) {
12938
13210
  if (!entry.startsWith(`.${name}-`)) continue;
12939
- fs8.rmSync(path13.join(scopeDir, entry), { recursive: true, force: true });
12940
- appendUpgradeLog(`Removed stale scoped staging dir: ${path13.join(scopeDir, entry)}`);
13211
+ fs8.rmSync(path15.join(scopeDir, entry), { recursive: true, force: true });
13212
+ appendUpgradeLog(`Removed stale scoped staging dir: ${path15.join(scopeDir, entry)}`);
12941
13213
  }
12942
13214
  } else {
12943
13215
  for (const entry of fs8.readdirSync(npmRoot)) {
12944
13216
  if (!entry.startsWith(`.${pkgName}-`)) continue;
12945
- fs8.rmSync(path13.join(npmRoot, entry), { recursive: true, force: true });
12946
- appendUpgradeLog(`Removed stale staging dir: ${path13.join(npmRoot, entry)}`);
13217
+ fs8.rmSync(path15.join(npmRoot, entry), { recursive: true, force: true });
13218
+ appendUpgradeLog(`Removed stale staging dir: ${path15.join(npmRoot, entry)}`);
12947
13219
  }
12948
13220
  }
12949
13221
  if (fs8.existsSync(binDir)) {
12950
13222
  for (const entry of fs8.readdirSync(binDir)) {
12951
13223
  if (![...binNames].some((name) => entry.startsWith(`.${name}-`))) continue;
12952
- fs8.rmSync(path13.join(binDir, entry), { recursive: true, force: true });
12953
- appendUpgradeLog(`Removed stale bin staging entry: ${path13.join(binDir, entry)}`);
13224
+ fs8.rmSync(path15.join(binDir, entry), { recursive: true, force: true });
13225
+ appendUpgradeLog(`Removed stale bin staging entry: ${path15.join(binDir, entry)}`);
12954
13226
  }
12955
13227
  }
12956
13228
  }
@@ -12992,7 +13264,7 @@ async function runDaemonUpgradeHelper(payload) {
12992
13264
  appendUpgradeLog(installOutput.trim());
12993
13265
  }
12994
13266
  if (process.platform === "win32") {
12995
- await new Promise((resolve9) => setTimeout(resolve9, 500));
13267
+ await new Promise((resolve12) => setTimeout(resolve12, 500));
12996
13268
  cleanupStaleGlobalInstallDirs(payload.packageName);
12997
13269
  appendUpgradeLog("Post-install staging cleanup complete");
12998
13270
  }
@@ -13036,6 +13308,25 @@ var CHAT_COMMANDS = [
13036
13308
  "change_model"
13037
13309
  ];
13038
13310
  var READ_DEBUG_ENABLED2 = process.argv.includes("--dev") || process.env.ADHDEV_READ_DEBUG === "1";
13311
+ function toHostedCliRuntimeDescriptor(record) {
13312
+ if (!record || typeof record !== "object") return null;
13313
+ const runtimeId = typeof record.sessionId === "string" ? record.sessionId : "";
13314
+ const cliType = typeof record.providerType === "string" ? record.providerType : "";
13315
+ const workspace = typeof record.workspace === "string" ? record.workspace : "";
13316
+ if (!runtimeId || !cliType || !workspace) return null;
13317
+ return {
13318
+ runtimeId,
13319
+ runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
13320
+ displayName: typeof record.displayName === "string" ? record.displayName : void 0,
13321
+ workspaceLabel: typeof record.workspaceLabel === "string" ? record.workspaceLabel : void 0,
13322
+ lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
13323
+ recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
13324
+ cliType,
13325
+ workspace,
13326
+ cliArgs: Array.isArray(record.meta?.cliArgs) ? record.meta.cliArgs : [],
13327
+ providerSessionId: typeof record.meta?.providerSessionId === "string" ? String(record.meta.providerSessionId) : void 0
13328
+ };
13329
+ }
13039
13330
  var DaemonCommandRouter = class {
13040
13331
  deps;
13041
13332
  constructor(deps) {
@@ -13109,6 +13400,90 @@ var DaemonCommandRouter = class {
13109
13400
  return { success: false, error: e.message };
13110
13401
  }
13111
13402
  }
13403
+ case "session_host_get_diagnostics": {
13404
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13405
+ const diagnostics = await this.deps.sessionHostControl.getDiagnostics({
13406
+ includeSessions: args?.includeSessions !== false,
13407
+ limit: Number(args?.limit) || void 0
13408
+ });
13409
+ return { success: true, diagnostics };
13410
+ }
13411
+ case "session_host_list_sessions": {
13412
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13413
+ const sessions = await this.deps.sessionHostControl.listSessions();
13414
+ return { success: true, sessions };
13415
+ }
13416
+ case "session_host_stop_session": {
13417
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13418
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13419
+ if (!sessionId) return { success: false, error: "sessionId required" };
13420
+ const record = await this.deps.sessionHostControl.stopSession(sessionId);
13421
+ return { success: true, record };
13422
+ }
13423
+ case "session_host_resume_session": {
13424
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13425
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13426
+ if (!sessionId) return { success: false, error: "sessionId required" };
13427
+ const record = await this.deps.sessionHostControl.resumeSession(sessionId);
13428
+ const hosted = toHostedCliRuntimeDescriptor(record);
13429
+ if (hosted) {
13430
+ await this.deps.cliManager.restoreHostedSessions([hosted]);
13431
+ }
13432
+ return { success: true, record };
13433
+ }
13434
+ case "session_host_restart_session": {
13435
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13436
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13437
+ if (!sessionId) return { success: false, error: "sessionId required" };
13438
+ const record = await this.deps.sessionHostControl.restartSession(sessionId);
13439
+ const hosted = toHostedCliRuntimeDescriptor(record);
13440
+ if (hosted) {
13441
+ await this.deps.cliManager.restoreHostedSessions([hosted]);
13442
+ }
13443
+ return { success: true, record };
13444
+ }
13445
+ case "session_host_send_signal": {
13446
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13447
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13448
+ const signal = typeof args?.signal === "string" ? args.signal : "";
13449
+ if (!sessionId) return { success: false, error: "sessionId required" };
13450
+ if (!signal) return { success: false, error: "signal required" };
13451
+ const record = await this.deps.sessionHostControl.sendSignal(sessionId, signal);
13452
+ return { success: true, record };
13453
+ }
13454
+ case "session_host_force_detach_client": {
13455
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13456
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13457
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
13458
+ if (!sessionId) return { success: false, error: "sessionId required" };
13459
+ if (!clientId) return { success: false, error: "clientId required" };
13460
+ const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
13461
+ return { success: true, record };
13462
+ }
13463
+ case "session_host_acquire_write": {
13464
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13465
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13466
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
13467
+ const ownerType = args?.ownerType === "agent" ? "agent" : "user";
13468
+ if (!sessionId) return { success: false, error: "sessionId required" };
13469
+ if (!clientId) return { success: false, error: "clientId required" };
13470
+ const record = await this.deps.sessionHostControl.acquireWrite({
13471
+ sessionId,
13472
+ clientId,
13473
+ ownerType,
13474
+ force: args?.force !== false
13475
+ });
13476
+ return { success: true, record };
13477
+ }
13478
+ case "session_host_release_write": {
13479
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13480
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13481
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
13482
+ if (!sessionId) return { success: false, error: "sessionId required" };
13483
+ if (!clientId) return { success: false, error: "clientId required" };
13484
+ const record = await this.deps.sessionHostControl.releaseWrite({ sessionId, clientId });
13485
+ return { success: true, record };
13486
+ }
13112
13487
  case "list_saved_sessions": {
13113
13488
  const providerType = typeof args?.providerType === "string" ? args.providerType.trim() : typeof args?.agentType === "string" ? args.agentType.trim() : "";
13114
13489
  const kind = args?.kind === "acp" ? "acp" : "cli";
@@ -13167,6 +13542,12 @@ var DaemonCommandRouter = class {
13167
13542
  if (!ideType) throw new Error("ideType required");
13168
13543
  const killProcess = args?.killProcess !== false;
13169
13544
  await this.stopIde(ideType, killProcess);
13545
+ try {
13546
+ const results = await detectIDEs(this.deps.providerLoader);
13547
+ this.deps.detectedIdes.value = results;
13548
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
13549
+ } catch {
13550
+ }
13170
13551
  return { success: true, ideType, stopped: true, processKilled: killProcess };
13171
13552
  }
13172
13553
  // ─── IDE restart ───
@@ -13209,6 +13590,12 @@ var DaemonCommandRouter = class {
13209
13590
  }
13210
13591
  }
13211
13592
  this.deps.onIdeConnected?.();
13593
+ try {
13594
+ const results = await detectIDEs(this.deps.providerLoader);
13595
+ this.deps.detectedIdes.value = results;
13596
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
13597
+ } catch {
13598
+ }
13212
13599
  if (result.success && resolvedWorkspace) {
13213
13600
  try {
13214
13601
  const next = appendRecentActivity(loadState(), {
@@ -13236,8 +13623,9 @@ var DaemonCommandRouter = class {
13236
13623
  }
13237
13624
  // ─── Detect IDEs ───
13238
13625
  case "detect_ides": {
13239
- const results = await detectIDEs();
13626
+ const results = await detectIDEs(this.deps.providerLoader);
13240
13627
  this.deps.detectedIdes.value = results;
13628
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
13241
13629
  return { success: true, detectedInfo: results };
13242
13630
  }
13243
13631
  // ─── Set User Name ───
@@ -13637,6 +14025,14 @@ var ProviderStreamAdapter = class {
13637
14025
  hasScript(name) {
13638
14026
  return typeof this.provider.scripts?.[name] === "function";
13639
14027
  }
14028
+ parseMaybeJson(raw) {
14029
+ if (typeof raw !== "string") return raw;
14030
+ try {
14031
+ return JSON.parse(raw);
14032
+ } catch {
14033
+ return raw;
14034
+ }
14035
+ }
13640
14036
  summarizeRaw(raw) {
13641
14037
  try {
13642
14038
  if (typeof raw === "string") return raw.replace(/\s+/g, " ").trim().slice(0, 240);
@@ -13697,12 +14093,30 @@ var ProviderStreamAdapter = class {
13697
14093
  }
13698
14094
  }
13699
14095
  async sendMessage(evaluate, text) {
13700
- const script = this.callScript("sendMessage", text);
14096
+ const params = { message: text };
14097
+ const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
13701
14098
  if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
13702
14099
  const result = await evaluate(script);
13703
14100
  if (result && typeof result === "string" && result.startsWith("error:")) {
13704
14101
  throw new Error(`[${this.agentName}] sendMessage failed: ${result}`);
13705
14102
  }
14103
+ const parsed = this.parseMaybeJson(result);
14104
+ if (parsed === true) return;
14105
+ if (typeof parsed === "string") {
14106
+ const normalized = parsed.trim().toLowerCase();
14107
+ if (normalized === "ok" || normalized === "sent" || normalized === "success" || normalized === "true") {
14108
+ return;
14109
+ }
14110
+ }
14111
+ if (parsed && typeof parsed === "object") {
14112
+ if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
14113
+ return;
14114
+ }
14115
+ if (typeof parsed.error === "string" && parsed.error.trim()) {
14116
+ throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
14117
+ }
14118
+ }
14119
+ throw new Error(`[${this.agentName}] sendMessage was not confirmed`);
13706
14120
  }
13707
14121
  async resolveAction(evaluate, action, button) {
13708
14122
  const script = this.callScript("resolveAction", { action, button });
@@ -13725,7 +14139,10 @@ var ProviderStreamAdapter = class {
13725
14139
  const raw = await evaluate(script, 1e4);
13726
14140
  const data = typeof raw === "string" ? JSON.parse(raw) : raw;
13727
14141
  if (data?.error) return [];
13728
- return Array.isArray(data) ? data : [];
14142
+ if (Array.isArray(data)) return data;
14143
+ if (Array.isArray(data?.sessions)) return data.sessions;
14144
+ if (Array.isArray(data?.chats)) return data.chats;
14145
+ return [];
13729
14146
  } catch {
13730
14147
  return [];
13731
14148
  }
@@ -13733,7 +14150,17 @@ var ProviderStreamAdapter = class {
13733
14150
  async switchSession(evaluate, sessionId) {
13734
14151
  const script = this.callScript("switchSession", sessionId);
13735
14152
  if (!script) return false;
13736
- return await evaluate(script, 1e4) === true;
14153
+ const raw = await evaluate(script, 1e4);
14154
+ const data = this.parseMaybeJson(raw);
14155
+ if (data === true) return true;
14156
+ if (typeof data === "string") {
14157
+ const normalized = data.trim().toLowerCase();
14158
+ return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
14159
+ }
14160
+ if (data && typeof data === "object") {
14161
+ return data.switched === true || data.success === true || data.ok === true;
14162
+ }
14163
+ return false;
13737
14164
  }
13738
14165
  async focusEditor(evaluate) {
13739
14166
  const script = this.callScript("focusEditor");
@@ -14423,11 +14850,11 @@ var ProviderInstanceManager = class {
14423
14850
 
14424
14851
  // src/providers/version-archive.ts
14425
14852
  var fs10 = __toESM(require("fs"));
14426
- var path14 = __toESM(require("path"));
14853
+ var path16 = __toESM(require("path"));
14427
14854
  var os16 = __toESM(require("os"));
14428
14855
  var import_child_process9 = require("child_process");
14429
14856
  var import_os3 = require("os");
14430
- var ARCHIVE_PATH = path14.join(os16.homedir(), ".adhdev", "version-history.json");
14857
+ var ARCHIVE_PATH = path16.join(os16.homedir(), ".adhdev", "version-history.json");
14431
14858
  var MAX_ENTRIES_PER_PROVIDER = 20;
14432
14859
  var VersionArchive = class {
14433
14860
  history = {};
@@ -14474,7 +14901,7 @@ var VersionArchive = class {
14474
14901
  }
14475
14902
  save() {
14476
14903
  try {
14477
- fs10.mkdirSync(path14.dirname(ARCHIVE_PATH), { recursive: true });
14904
+ fs10.mkdirSync(path16.dirname(ARCHIVE_PATH), { recursive: true });
14478
14905
  fs10.writeFileSync(ARCHIVE_PATH, JSON.stringify(this.history, null, 2));
14479
14906
  } catch {
14480
14907
  }
@@ -14515,7 +14942,7 @@ function checkPathExists2(paths) {
14515
14942
  for (const p of paths) {
14516
14943
  if (p.includes("*")) {
14517
14944
  const home = os16.homedir();
14518
- const resolved = p.replace(/\*/g, home.split(path14.sep).pop() || "");
14945
+ const resolved = p.replace(/\*/g, home.split(path16.sep).pop() || "");
14519
14946
  if (fs10.existsSync(resolved)) return resolved;
14520
14947
  } else {
14521
14948
  if (fs10.existsSync(p)) return p;
@@ -14525,7 +14952,7 @@ function checkPathExists2(paths) {
14525
14952
  }
14526
14953
  function getMacAppVersion(appPath) {
14527
14954
  if ((0, import_os3.platform)() !== "darwin" || !appPath.endsWith(".app")) return null;
14528
- const plistPath = path14.join(appPath, "Contents", "Info.plist");
14955
+ const plistPath = path16.join(appPath, "Contents", "Info.plist");
14529
14956
  if (!fs10.existsSync(plistPath)) return null;
14530
14957
  const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
14531
14958
  return raw || null;
@@ -14552,7 +14979,7 @@ async function detectAllVersions(loader, archive) {
14552
14979
  const cliBin = provider.cli ? findBinary2(provider.cli) : null;
14553
14980
  let resolvedBin = cliBin;
14554
14981
  if (!resolvedBin && appPath && currentOs === "darwin") {
14555
- const bundled = path14.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
14982
+ const bundled = path16.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
14556
14983
  if (provider.cli && fs10.existsSync(bundled)) resolvedBin = bundled;
14557
14984
  }
14558
14985
  info.installed = !!(appPath || resolvedBin);
@@ -14593,7 +15020,7 @@ async function detectAllVersions(loader, archive) {
14593
15020
  // src/daemon/dev-server.ts
14594
15021
  var http2 = __toESM(require("http"));
14595
15022
  var fs14 = __toESM(require("fs"));
14596
- var path18 = __toESM(require("path"));
15023
+ var path20 = __toESM(require("path"));
14597
15024
 
14598
15025
  // src/daemon/scaffold-template.ts
14599
15026
  function generateFiles(type, name, category, opts = {}) {
@@ -14929,7 +15356,7 @@ init_logger();
14929
15356
 
14930
15357
  // src/daemon/dev-cdp-handlers.ts
14931
15358
  var fs11 = __toESM(require("fs"));
14932
- var path15 = __toESM(require("path"));
15359
+ var path17 = __toESM(require("path"));
14933
15360
  init_logger();
14934
15361
  async function handleCdpEvaluate(ctx, req, res) {
14935
15362
  const body = await ctx.readBody(req);
@@ -15108,17 +15535,17 @@ async function handleScriptHints(ctx, type, _req, res) {
15108
15535
  return;
15109
15536
  }
15110
15537
  let scriptsPath = "";
15111
- const directScripts = path15.join(dir, "scripts.js");
15538
+ const directScripts = path17.join(dir, "scripts.js");
15112
15539
  if (fs11.existsSync(directScripts)) {
15113
15540
  scriptsPath = directScripts;
15114
15541
  } else {
15115
- const scriptsDir = path15.join(dir, "scripts");
15542
+ const scriptsDir = path17.join(dir, "scripts");
15116
15543
  if (fs11.existsSync(scriptsDir)) {
15117
15544
  const versions = fs11.readdirSync(scriptsDir).filter((d) => {
15118
- return fs11.statSync(path15.join(scriptsDir, d)).isDirectory();
15545
+ return fs11.statSync(path17.join(scriptsDir, d)).isDirectory();
15119
15546
  }).sort().reverse();
15120
15547
  for (const ver of versions) {
15121
- const p = path15.join(scriptsDir, ver, "scripts.js");
15548
+ const p = path17.join(scriptsDir, ver, "scripts.js");
15122
15549
  if (fs11.existsSync(p)) {
15123
15550
  scriptsPath = p;
15124
15551
  break;
@@ -15937,7 +16364,7 @@ async function handleDomContext(ctx, type, req, res) {
15937
16364
 
15938
16365
  // src/daemon/dev-cli-debug.ts
15939
16366
  var fs12 = __toESM(require("fs"));
15940
- var path16 = __toESM(require("path"));
16367
+ var path18 = __toESM(require("path"));
15941
16368
  function slugifyFixtureName(value) {
15942
16369
  const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
15943
16370
  return normalized || `fixture-${Date.now()}`;
@@ -15947,11 +16374,11 @@ function getCliFixtureDir(ctx, type) {
15947
16374
  if (!providerDir) {
15948
16375
  throw new Error(`Provider directory not found for '${type}'`);
15949
16376
  }
15950
- return path16.join(providerDir, "fixtures");
16377
+ return path18.join(providerDir, "fixtures");
15951
16378
  }
15952
16379
  function readCliFixture(ctx, type, name) {
15953
16380
  const fixtureDir = getCliFixtureDir(ctx, type);
15954
- const filePath = path16.join(fixtureDir, `${name}.json`);
16381
+ const filePath = path18.join(fixtureDir, `${name}.json`);
15955
16382
  if (!fs12.existsSync(filePath)) {
15956
16383
  throw new Error(`Fixture not found: ${filePath}`);
15957
16384
  }
@@ -16111,7 +16538,7 @@ function getCliTargetBundle(ctx, type, instanceId) {
16111
16538
  return { target, instance, adapter };
16112
16539
  }
16113
16540
  function sleep(ms) {
16114
- return new Promise((resolve9) => setTimeout(resolve9, ms));
16541
+ return new Promise((resolve12) => setTimeout(resolve12, ms));
16115
16542
  }
16116
16543
  async function waitForCliReady(ctx, type, instanceId, timeoutMs) {
16117
16544
  const startedAt = Date.now();
@@ -16710,7 +17137,7 @@ async function handleCliFixtureCapture(ctx, req, res) {
16710
17137
  },
16711
17138
  notes: typeof body?.notes === "string" ? body.notes : void 0
16712
17139
  };
16713
- const filePath = path16.join(fixtureDir, `${name}.json`);
17140
+ const filePath = path18.join(fixtureDir, `${name}.json`);
16714
17141
  fs12.writeFileSync(filePath, JSON.stringify(fixture, null, 2));
16715
17142
  ctx.json(res, 200, {
16716
17143
  saved: true,
@@ -16734,7 +17161,7 @@ async function handleCliFixtureList(ctx, type, _req, res) {
16734
17161
  return;
16735
17162
  }
16736
17163
  const fixtures = fs12.readdirSync(fixtureDir).filter((file) => file.endsWith(".json")).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" })).map((file) => {
16737
- const fullPath = path16.join(fixtureDir, file);
17164
+ const fullPath = path18.join(fixtureDir, file);
16738
17165
  try {
16739
17166
  const raw = JSON.parse(fs12.readFileSync(fullPath, "utf-8"));
16740
17167
  return {
@@ -16870,7 +17297,7 @@ async function handleCliRaw(ctx, req, res) {
16870
17297
 
16871
17298
  // src/daemon/dev-auto-implement.ts
16872
17299
  var fs13 = __toESM(require("fs"));
16873
- var path17 = __toESM(require("path"));
17300
+ var path19 = __toESM(require("path"));
16874
17301
  var os17 = __toESM(require("os"));
16875
17302
  function getAutoImplPid(ctx) {
16876
17303
  const proc = ctx.autoImplProcess;
@@ -16910,22 +17337,22 @@ function getLatestScriptVersionDir(scriptsDir) {
16910
17337
  if (!fs13.existsSync(scriptsDir)) return null;
16911
17338
  const versions = fs13.readdirSync(scriptsDir).filter((d) => {
16912
17339
  try {
16913
- return fs13.statSync(path17.join(scriptsDir, d)).isDirectory();
17340
+ return fs13.statSync(path19.join(scriptsDir, d)).isDirectory();
16914
17341
  } catch {
16915
17342
  return false;
16916
17343
  }
16917
17344
  }).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
16918
17345
  if (versions.length === 0) return null;
16919
- return path17.join(scriptsDir, versions[0]);
17346
+ return path19.join(scriptsDir, versions[0]);
16920
17347
  }
16921
17348
  function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
16922
- const canonicalUserDir = path17.resolve(ctx.providerLoader.getUserProviderDir(category, type));
16923
- const desiredDir = requestedDir ? path17.resolve(requestedDir) : canonicalUserDir;
16924
- const upstreamRoot = path17.resolve(ctx.providerLoader.getUpstreamDir());
16925
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path17.sep}`)) {
17349
+ const canonicalUserDir = path19.resolve(ctx.providerLoader.getUserProviderDir(category, type));
17350
+ const desiredDir = requestedDir ? path19.resolve(requestedDir) : canonicalUserDir;
17351
+ const upstreamRoot = path19.resolve(ctx.providerLoader.getUpstreamDir());
17352
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path19.sep}`)) {
16926
17353
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
16927
17354
  }
16928
- if (path17.basename(desiredDir) !== type) {
17355
+ if (path19.basename(desiredDir) !== type) {
16929
17356
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
16930
17357
  }
16931
17358
  const sourceDir = ctx.findProviderDir(type);
@@ -16933,11 +17360,11 @@ function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
16933
17360
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
16934
17361
  }
16935
17362
  if (!fs13.existsSync(desiredDir)) {
16936
- fs13.mkdirSync(path17.dirname(desiredDir), { recursive: true });
17363
+ fs13.mkdirSync(path19.dirname(desiredDir), { recursive: true });
16937
17364
  fs13.cpSync(sourceDir, desiredDir, { recursive: true });
16938
17365
  ctx.log(`Auto-implement writable copy created: ${desiredDir}`);
16939
17366
  }
16940
- const providerJson = path17.join(desiredDir, "provider.json");
17367
+ const providerJson = path19.join(desiredDir, "provider.json");
16941
17368
  if (!fs13.existsSync(providerJson)) {
16942
17369
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
16943
17370
  }
@@ -16960,13 +17387,13 @@ function loadAutoImplReferenceScripts(ctx, referenceType) {
16960
17387
  const refDir = ctx.findProviderDir(referenceType);
16961
17388
  if (!refDir || !fs13.existsSync(refDir)) return {};
16962
17389
  const referenceScripts = {};
16963
- const scriptsDir = path17.join(refDir, "scripts");
17390
+ const scriptsDir = path19.join(refDir, "scripts");
16964
17391
  const latestDir = getLatestScriptVersionDir(scriptsDir);
16965
17392
  if (!latestDir) return referenceScripts;
16966
17393
  for (const file of fs13.readdirSync(latestDir)) {
16967
17394
  if (!file.endsWith(".js")) continue;
16968
17395
  try {
16969
- referenceScripts[file] = fs13.readFileSync(path17.join(latestDir, file), "utf-8");
17396
+ referenceScripts[file] = fs13.readFileSync(path19.join(latestDir, file), "utf-8");
16970
17397
  } catch {
16971
17398
  }
16972
17399
  }
@@ -17074,9 +17501,9 @@ async function handleAutoImplement(ctx, type, req, res) {
17074
17501
  });
17075
17502
  const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
17076
17503
  const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
17077
- const tmpDir = path17.join(os17.tmpdir(), "adhdev-autoimpl");
17504
+ const tmpDir = path19.join(os17.tmpdir(), "adhdev-autoimpl");
17078
17505
  if (!fs13.existsSync(tmpDir)) fs13.mkdirSync(tmpDir, { recursive: true });
17079
- const promptFile = path17.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
17506
+ const promptFile = path19.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
17080
17507
  fs13.writeFileSync(promptFile, prompt, "utf-8");
17081
17508
  ctx.log(`Auto-implement prompt written to ${promptFile} (${prompt.length} chars)`);
17082
17509
  const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
@@ -17503,7 +17930,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17503
17930
  setMode: "set_mode.js"
17504
17931
  };
17505
17932
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
17506
- const scriptsDir = path17.join(providerDir, "scripts");
17933
+ const scriptsDir = path19.join(providerDir, "scripts");
17507
17934
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
17508
17935
  if (latestScriptsDir) {
17509
17936
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -17514,7 +17941,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17514
17941
  for (const file of fs13.readdirSync(latestScriptsDir)) {
17515
17942
  if (file.endsWith(".js") && targetFileNames.has(file)) {
17516
17943
  try {
17517
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
17944
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17518
17945
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
17519
17946
  lines.push("```javascript");
17520
17947
  lines.push(content);
@@ -17531,7 +17958,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17531
17958
  lines.push("");
17532
17959
  for (const file of refFiles) {
17533
17960
  try {
17534
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
17961
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17535
17962
  lines.push(`### \`${file}\` \u{1F512}`);
17536
17963
  lines.push("```javascript");
17537
17964
  lines.push(content);
@@ -17572,10 +17999,10 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17572
17999
  lines.push("");
17573
18000
  }
17574
18001
  }
17575
- const docsDir = path17.join(providerDir, "../../docs");
18002
+ const docsDir = path19.join(providerDir, "../../docs");
17576
18003
  const loadGuide = (name) => {
17577
18004
  try {
17578
- const p = path17.join(docsDir, name);
18005
+ const p = path19.join(docsDir, name);
17579
18006
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
17580
18007
  } catch {
17581
18008
  }
@@ -17810,7 +18237,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17810
18237
  parseApproval: "parse_approval.js"
17811
18238
  };
17812
18239
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
17813
- const scriptsDir = path17.join(providerDir, "scripts");
18240
+ const scriptsDir = path19.join(providerDir, "scripts");
17814
18241
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
17815
18242
  if (latestScriptsDir) {
17816
18243
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -17822,7 +18249,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17822
18249
  if (!file.endsWith(".js")) continue;
17823
18250
  if (!targetFileNames.has(file)) continue;
17824
18251
  try {
17825
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
18252
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17826
18253
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
17827
18254
  lines.push("```javascript");
17828
18255
  lines.push(content);
@@ -17838,7 +18265,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17838
18265
  lines.push("");
17839
18266
  for (const file of refFiles) {
17840
18267
  try {
17841
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
18268
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17842
18269
  lines.push(`### \`${file}\` \u{1F512}`);
17843
18270
  lines.push("```javascript");
17844
18271
  lines.push(content);
@@ -17871,10 +18298,10 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17871
18298
  lines.push("");
17872
18299
  }
17873
18300
  }
17874
- const docsDir = path17.join(providerDir, "../../docs");
18301
+ const docsDir = path19.join(providerDir, "../../docs");
17875
18302
  const loadGuide = (name) => {
17876
18303
  try {
17877
- const p = path17.join(docsDir, name);
18304
+ const p = path19.join(docsDir, name);
17878
18305
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
17879
18306
  } catch {
17880
18307
  }
@@ -18285,8 +18712,8 @@ var DevServer = class _DevServer {
18285
18712
  }
18286
18713
  getEndpointList() {
18287
18714
  return this.routes.map((r) => {
18288
- const path19 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
18289
- return `${r.method.padEnd(5)} ${path19}`;
18715
+ const path21 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
18716
+ return `${r.method.padEnd(5)} ${path21}`;
18290
18717
  });
18291
18718
  }
18292
18719
  async start(port = DEV_SERVER_PORT) {
@@ -18317,15 +18744,15 @@ var DevServer = class _DevServer {
18317
18744
  this.json(res, 500, { error: e.message });
18318
18745
  }
18319
18746
  });
18320
- return new Promise((resolve9, reject) => {
18747
+ return new Promise((resolve12, reject) => {
18321
18748
  this.server.listen(port, "127.0.0.1", () => {
18322
18749
  this.log(`Dev server listening on http://127.0.0.1:${port}`);
18323
- resolve9();
18750
+ resolve12();
18324
18751
  });
18325
18752
  this.server.on("error", (e) => {
18326
18753
  if (e.code === "EADDRINUSE") {
18327
18754
  this.log(`Port ${port} in use, skipping dev server`);
18328
- resolve9();
18755
+ resolve12();
18329
18756
  } else {
18330
18757
  reject(e);
18331
18758
  }
@@ -18408,20 +18835,20 @@ var DevServer = class _DevServer {
18408
18835
  child.stderr?.on("data", (d) => {
18409
18836
  stderr += d.toString().slice(0, 2e3);
18410
18837
  });
18411
- await new Promise((resolve9) => {
18838
+ await new Promise((resolve12) => {
18412
18839
  const timer = setTimeout(() => {
18413
18840
  child.kill();
18414
- resolve9();
18841
+ resolve12();
18415
18842
  }, 3e3);
18416
18843
  child.on("exit", () => {
18417
18844
  clearTimeout(timer);
18418
- resolve9();
18845
+ resolve12();
18419
18846
  });
18420
18847
  child.stdout?.once("data", () => {
18421
18848
  setTimeout(() => {
18422
18849
  child.kill();
18423
18850
  clearTimeout(timer);
18424
- resolve9();
18851
+ resolve12();
18425
18852
  }, 500);
18426
18853
  });
18427
18854
  });
@@ -18568,12 +18995,12 @@ var DevServer = class _DevServer {
18568
18995
  // ─── DevConsole SPA ───
18569
18996
  getConsoleDistDir() {
18570
18997
  const candidates = [
18571
- path18.resolve(__dirname, "../../web-devconsole/dist"),
18572
- path18.resolve(__dirname, "../../../web-devconsole/dist"),
18573
- path18.join(process.cwd(), "packages/web-devconsole/dist")
18998
+ path20.resolve(__dirname, "../../web-devconsole/dist"),
18999
+ path20.resolve(__dirname, "../../../web-devconsole/dist"),
19000
+ path20.join(process.cwd(), "packages/web-devconsole/dist")
18574
19001
  ];
18575
19002
  for (const dir of candidates) {
18576
- if (fs14.existsSync(path18.join(dir, "index.html"))) return dir;
19003
+ if (fs14.existsSync(path20.join(dir, "index.html"))) return dir;
18577
19004
  }
18578
19005
  return null;
18579
19006
  }
@@ -18583,7 +19010,7 @@ var DevServer = class _DevServer {
18583
19010
  this.json(res, 500, { error: "DevConsole not found. Run: npm run build -w packages/web-devconsole" });
18584
19011
  return;
18585
19012
  }
18586
- const htmlPath = path18.join(distDir, "index.html");
19013
+ const htmlPath = path20.join(distDir, "index.html");
18587
19014
  try {
18588
19015
  const html = fs14.readFileSync(htmlPath, "utf-8");
18589
19016
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
@@ -18608,15 +19035,15 @@ var DevServer = class _DevServer {
18608
19035
  this.json(res, 404, { error: "Not found" });
18609
19036
  return;
18610
19037
  }
18611
- const safePath = path18.normalize(pathname).replace(/^\.\.\//, "");
18612
- const filePath = path18.join(distDir, safePath);
19038
+ const safePath = path20.normalize(pathname).replace(/^\.\.\//, "");
19039
+ const filePath = path20.join(distDir, safePath);
18613
19040
  if (!filePath.startsWith(distDir)) {
18614
19041
  this.json(res, 403, { error: "Forbidden" });
18615
19042
  return;
18616
19043
  }
18617
19044
  try {
18618
19045
  const content = fs14.readFileSync(filePath);
18619
- const ext = path18.extname(filePath);
19046
+ const ext = path20.extname(filePath);
18620
19047
  const contentType = _DevServer.MIME_MAP[ext] || "application/octet-stream";
18621
19048
  res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=31536000, immutable" });
18622
19049
  res.end(content);
@@ -18729,9 +19156,9 @@ var DevServer = class _DevServer {
18729
19156
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
18730
19157
  if (entry.isDirectory()) {
18731
19158
  files.push({ path: rel, size: 0, type: "dir" });
18732
- scan(path18.join(d, entry.name), rel);
19159
+ scan(path20.join(d, entry.name), rel);
18733
19160
  } else {
18734
- const stat = fs14.statSync(path18.join(d, entry.name));
19161
+ const stat = fs14.statSync(path20.join(d, entry.name));
18735
19162
  files.push({ path: rel, size: stat.size, type: "file" });
18736
19163
  }
18737
19164
  }
@@ -18754,7 +19181,7 @@ var DevServer = class _DevServer {
18754
19181
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
18755
19182
  return;
18756
19183
  }
18757
- const fullPath = path18.resolve(dir, path18.normalize(filePath));
19184
+ const fullPath = path20.resolve(dir, path20.normalize(filePath));
18758
19185
  if (!fullPath.startsWith(dir)) {
18759
19186
  this.json(res, 403, { error: "Forbidden" });
18760
19187
  return;
@@ -18779,14 +19206,14 @@ var DevServer = class _DevServer {
18779
19206
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
18780
19207
  return;
18781
19208
  }
18782
- const fullPath = path18.resolve(dir, path18.normalize(filePath));
19209
+ const fullPath = path20.resolve(dir, path20.normalize(filePath));
18783
19210
  if (!fullPath.startsWith(dir)) {
18784
19211
  this.json(res, 403, { error: "Forbidden" });
18785
19212
  return;
18786
19213
  }
18787
19214
  try {
18788
19215
  if (fs14.existsSync(fullPath)) fs14.copyFileSync(fullPath, fullPath + ".bak");
18789
- fs14.mkdirSync(path18.dirname(fullPath), { recursive: true });
19216
+ fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
18790
19217
  fs14.writeFileSync(fullPath, content, "utf-8");
18791
19218
  this.log(`File saved: ${fullPath} (${content.length} chars)`);
18792
19219
  this.providerLoader.reload();
@@ -18803,7 +19230,7 @@ var DevServer = class _DevServer {
18803
19230
  return;
18804
19231
  }
18805
19232
  for (const name of ["scripts.js", "provider.json"]) {
18806
- const p = path18.join(dir, name);
19233
+ const p = path20.join(dir, name);
18807
19234
  if (fs14.existsSync(p)) {
18808
19235
  const source = fs14.readFileSync(p, "utf-8");
18809
19236
  this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
@@ -18824,8 +19251,8 @@ var DevServer = class _DevServer {
18824
19251
  this.json(res, 404, { error: `Provider not found: ${type}` });
18825
19252
  return;
18826
19253
  }
18827
- const target = fs14.existsSync(path18.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
18828
- const targetPath = path18.join(dir, target);
19254
+ const target = fs14.existsSync(path20.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
19255
+ const targetPath = path20.join(dir, target);
18829
19256
  try {
18830
19257
  if (fs14.existsSync(targetPath)) fs14.copyFileSync(targetPath, targetPath + ".bak");
18831
19258
  fs14.writeFileSync(targetPath, source, "utf-8");
@@ -18930,14 +19357,14 @@ var DevServer = class _DevServer {
18930
19357
  child.stderr?.on("data", (d) => {
18931
19358
  stderr += d.toString();
18932
19359
  });
18933
- await new Promise((resolve9) => {
19360
+ await new Promise((resolve12) => {
18934
19361
  const timer = setTimeout(() => {
18935
19362
  child.kill();
18936
- resolve9();
19363
+ resolve12();
18937
19364
  }, timeout);
18938
19365
  child.on("exit", () => {
18939
19366
  clearTimeout(timer);
18940
- resolve9();
19367
+ resolve12();
18941
19368
  });
18942
19369
  });
18943
19370
  const elapsed = Date.now() - start;
@@ -18985,7 +19412,7 @@ var DevServer = class _DevServer {
18985
19412
  }
18986
19413
  let targetDir;
18987
19414
  targetDir = this.providerLoader.getUserProviderDir(category, type);
18988
- const jsonPath = path18.join(targetDir, "provider.json");
19415
+ const jsonPath = path20.join(targetDir, "provider.json");
18989
19416
  if (fs14.existsSync(jsonPath)) {
18990
19417
  this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
18991
19418
  return;
@@ -18997,8 +19424,8 @@ var DevServer = class _DevServer {
18997
19424
  const createdFiles = ["provider.json"];
18998
19425
  if (result.files) {
18999
19426
  for (const [relPath, content] of Object.entries(result.files)) {
19000
- const fullPath = path18.join(targetDir, relPath);
19001
- fs14.mkdirSync(path18.dirname(fullPath), { recursive: true });
19427
+ const fullPath = path20.join(targetDir, relPath);
19428
+ fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
19002
19429
  fs14.writeFileSync(fullPath, content, "utf-8");
19003
19430
  createdFiles.push(relPath);
19004
19431
  }
@@ -19051,22 +19478,22 @@ var DevServer = class _DevServer {
19051
19478
  if (!fs14.existsSync(scriptsDir)) return null;
19052
19479
  const versions = fs14.readdirSync(scriptsDir).filter((d) => {
19053
19480
  try {
19054
- return fs14.statSync(path18.join(scriptsDir, d)).isDirectory();
19481
+ return fs14.statSync(path20.join(scriptsDir, d)).isDirectory();
19055
19482
  } catch {
19056
19483
  return false;
19057
19484
  }
19058
19485
  }).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
19059
19486
  if (versions.length === 0) return null;
19060
- return path18.join(scriptsDir, versions[0]);
19487
+ return path20.join(scriptsDir, versions[0]);
19061
19488
  }
19062
19489
  resolveAutoImplWritableProviderDir(category, type, requestedDir) {
19063
- const canonicalUserDir = path18.resolve(this.providerLoader.getUserProviderDir(category, type));
19064
- const desiredDir = requestedDir ? path18.resolve(requestedDir) : canonicalUserDir;
19065
- const upstreamRoot = path18.resolve(this.providerLoader.getUpstreamDir());
19066
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path18.sep}`)) {
19490
+ const canonicalUserDir = path20.resolve(this.providerLoader.getUserProviderDir(category, type));
19491
+ const desiredDir = requestedDir ? path20.resolve(requestedDir) : canonicalUserDir;
19492
+ const upstreamRoot = path20.resolve(this.providerLoader.getUpstreamDir());
19493
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path20.sep}`)) {
19067
19494
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
19068
19495
  }
19069
- if (path18.basename(desiredDir) !== type) {
19496
+ if (path20.basename(desiredDir) !== type) {
19070
19497
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
19071
19498
  }
19072
19499
  const sourceDir = this.findProviderDir(type);
@@ -19074,11 +19501,11 @@ var DevServer = class _DevServer {
19074
19501
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
19075
19502
  }
19076
19503
  if (!fs14.existsSync(desiredDir)) {
19077
- fs14.mkdirSync(path18.dirname(desiredDir), { recursive: true });
19504
+ fs14.mkdirSync(path20.dirname(desiredDir), { recursive: true });
19078
19505
  fs14.cpSync(sourceDir, desiredDir, { recursive: true });
19079
19506
  this.log(`Auto-implement writable copy created: ${desiredDir}`);
19080
19507
  }
19081
- const providerJson = path18.join(desiredDir, "provider.json");
19508
+ const providerJson = path20.join(desiredDir, "provider.json");
19082
19509
  if (!fs14.existsSync(providerJson)) {
19083
19510
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
19084
19511
  }
@@ -19126,7 +19553,7 @@ var DevServer = class _DevServer {
19126
19553
  setMode: "set_mode.js"
19127
19554
  };
19128
19555
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
19129
- const scriptsDir = path18.join(providerDir, "scripts");
19556
+ const scriptsDir = path20.join(providerDir, "scripts");
19130
19557
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
19131
19558
  if (latestScriptsDir) {
19132
19559
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -19137,7 +19564,7 @@ var DevServer = class _DevServer {
19137
19564
  for (const file of fs14.readdirSync(latestScriptsDir)) {
19138
19565
  if (file.endsWith(".js") && targetFileNames.has(file)) {
19139
19566
  try {
19140
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
19567
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19141
19568
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
19142
19569
  lines.push("```javascript");
19143
19570
  lines.push(content);
@@ -19154,7 +19581,7 @@ var DevServer = class _DevServer {
19154
19581
  lines.push("");
19155
19582
  for (const file of refFiles) {
19156
19583
  try {
19157
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
19584
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19158
19585
  lines.push(`### \`${file}\` \u{1F512}`);
19159
19586
  lines.push("```javascript");
19160
19587
  lines.push(content);
@@ -19195,10 +19622,10 @@ var DevServer = class _DevServer {
19195
19622
  lines.push("");
19196
19623
  }
19197
19624
  }
19198
- const docsDir = path18.join(providerDir, "../../docs");
19625
+ const docsDir = path20.join(providerDir, "../../docs");
19199
19626
  const loadGuide = (name) => {
19200
19627
  try {
19201
- const p = path18.join(docsDir, name);
19628
+ const p = path20.join(docsDir, name);
19202
19629
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
19203
19630
  } catch {
19204
19631
  }
@@ -19372,7 +19799,7 @@ var DevServer = class _DevServer {
19372
19799
  parseApproval: "parse_approval.js"
19373
19800
  };
19374
19801
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
19375
- const scriptsDir = path18.join(providerDir, "scripts");
19802
+ const scriptsDir = path20.join(providerDir, "scripts");
19376
19803
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
19377
19804
  if (latestScriptsDir) {
19378
19805
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -19384,7 +19811,7 @@ var DevServer = class _DevServer {
19384
19811
  if (!file.endsWith(".js")) continue;
19385
19812
  if (!targetFileNames.has(file)) continue;
19386
19813
  try {
19387
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
19814
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19388
19815
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
19389
19816
  lines.push("```javascript");
19390
19817
  lines.push(content);
@@ -19400,7 +19827,7 @@ var DevServer = class _DevServer {
19400
19827
  lines.push("");
19401
19828
  for (const file of refFiles) {
19402
19829
  try {
19403
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
19830
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19404
19831
  lines.push(`### \`${file}\` \u{1F512}`);
19405
19832
  lines.push("```javascript");
19406
19833
  lines.push(content);
@@ -19433,10 +19860,10 @@ var DevServer = class _DevServer {
19433
19860
  lines.push("");
19434
19861
  }
19435
19862
  }
19436
- const docsDir = path18.join(providerDir, "../../docs");
19863
+ const docsDir = path20.join(providerDir, "../../docs");
19437
19864
  const loadGuide = (name) => {
19438
19865
  try {
19439
- const p = path18.join(docsDir, name);
19866
+ const p = path20.join(docsDir, name);
19440
19867
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
19441
19868
  } catch {
19442
19869
  }
@@ -19612,14 +20039,14 @@ data: ${JSON.stringify(msg.data)}
19612
20039
  res.end(JSON.stringify(data, null, 2));
19613
20040
  }
19614
20041
  async readBody(req) {
19615
- return new Promise((resolve9) => {
20042
+ return new Promise((resolve12) => {
19616
20043
  let body = "";
19617
20044
  req.on("data", (chunk) => body += chunk);
19618
20045
  req.on("end", () => {
19619
20046
  try {
19620
- resolve9(JSON.parse(body));
20047
+ resolve12(JSON.parse(body));
19621
20048
  } catch {
19622
- resolve9({});
20049
+ resolve12({});
19623
20050
  }
19624
20051
  });
19625
20052
  });
@@ -19953,6 +20380,7 @@ var SessionHostRuntimeTransport = class {
19953
20380
  }
19954
20381
  }
19955
20382
  handleEvent(event) {
20383
+ if (!("sessionId" in event)) return;
19956
20384
  if (event.sessionId !== this.options.runtimeId) return;
19957
20385
  if ((event.type === "session_started" || event.type === "session_resumed") && typeof event.pid === "number") {
19958
20386
  this.currentPid = event.pid;
@@ -20028,7 +20456,10 @@ var SessionHostRuntimeTransport = class {
20028
20456
  clientId: client.clientId,
20029
20457
  type: client.type,
20030
20458
  readOnly: client.readOnly
20031
- }))
20459
+ })),
20460
+ restoredFromStorage: record.meta?.restoredFromStorage === true,
20461
+ recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
20462
+ recoveryError: typeof record.meta?.runtimeRecoveryError === "string" ? String(record.meta.runtimeRecoveryError) : null
20032
20463
  };
20033
20464
  }
20034
20465
  enqueue(action) {
@@ -20083,7 +20514,7 @@ async function waitForReady(endpoint, timeoutMs = STARTUP_TIMEOUT_MS) {
20083
20514
  const deadline = Date.now() + timeoutMs;
20084
20515
  while (Date.now() < deadline) {
20085
20516
  if (await canConnect(endpoint)) return;
20086
- await new Promise((resolve9) => setTimeout(resolve9, STARTUP_POLL_MS));
20517
+ await new Promise((resolve12) => setTimeout(resolve12, STARTUP_POLL_MS));
20087
20518
  }
20088
20519
  throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
20089
20520
  }
@@ -20239,10 +20670,10 @@ async function installExtension(ide, extension) {
20239
20670
  const buffer = Buffer.from(await res.arrayBuffer());
20240
20671
  const fs15 = await import("fs");
20241
20672
  fs15.writeFileSync(vsixPath, buffer);
20242
- return new Promise((resolve9) => {
20673
+ return new Promise((resolve12) => {
20243
20674
  const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
20244
20675
  (0, import_child_process10.exec)(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
20245
- resolve9({
20676
+ resolve12({
20246
20677
  extensionId: extension.id,
20247
20678
  marketplaceId: extension.marketplaceId,
20248
20679
  success: !error,
@@ -20255,11 +20686,11 @@ async function installExtension(ide, extension) {
20255
20686
  } catch (e) {
20256
20687
  }
20257
20688
  }
20258
- return new Promise((resolve9) => {
20689
+ return new Promise((resolve12) => {
20259
20690
  const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
20260
20691
  (0, import_child_process10.exec)(cmd, { timeout: 6e4 }, (error, stdout, stderr) => {
20261
20692
  if (error) {
20262
- resolve9({
20693
+ resolve12({
20263
20694
  extensionId: extension.id,
20264
20695
  marketplaceId: extension.marketplaceId,
20265
20696
  success: false,
@@ -20267,7 +20698,7 @@ async function installExtension(ide, extension) {
20267
20698
  error: stderr || error.message
20268
20699
  });
20269
20700
  } else {
20270
- resolve9({
20701
+ resolve12({
20271
20702
  extensionId: extension.id,
20272
20703
  marketplaceId: extension.marketplaceId,
20273
20704
  success: true,
@@ -20403,13 +20834,32 @@ async function initDaemonComponents(config) {
20403
20834
  const detectedIdesRef = { value: [] };
20404
20835
  let agentStreamManager = null;
20405
20836
  let poller = null;
20837
+ const refreshProviderAvailability = async (providerType) => {
20838
+ const targetProvider = providerType ? providerLoader.getMeta(providerLoader.resolveAlias(providerType)) : null;
20839
+ const targetCategory = targetProvider?.category;
20840
+ if (!providerType || targetCategory === "cli" || targetCategory === "acp") {
20841
+ if (providerType && targetProvider) {
20842
+ const detected = await detectCLI(targetProvider.type, providerLoader, { includeVersion: false });
20843
+ providerLoader.setProviderAvailability(targetProvider.type, {
20844
+ installed: !!detected,
20845
+ detectedPath: detected?.path || null
20846
+ });
20847
+ } else {
20848
+ providerLoader.setCliDetectionResults(await detectCLIs(providerLoader, { includeVersion: false }), true);
20849
+ }
20850
+ }
20851
+ if (!providerType || targetCategory === "ide") {
20852
+ detectedIdesRef.value = await detectIDEs(providerLoader);
20853
+ providerLoader.setIdeDetectionResults(detectedIdesRef.value, true);
20854
+ }
20855
+ };
20406
20856
  const cliManager = new DaemonCliManager({
20407
20857
  ...config.cliManagerDeps,
20408
20858
  getInstanceManager: () => instanceManager,
20409
20859
  getSessionRegistry: () => sessionRegistry
20410
20860
  }, providerLoader);
20411
20861
  LOG.info("Init", "Detecting IDEs...");
20412
- detectedIdesRef.value = await detectIDEs();
20862
+ await refreshProviderAvailability();
20413
20863
  const installed = detectedIdesRef.value.filter((i) => i.installed);
20414
20864
  LOG.info("Init", `Found ${installed.length} IDE(s): ${installed.map((i) => i.id).join(", ") || "none"}`);
20415
20865
  const cdpSetupContext = {
@@ -20449,7 +20899,11 @@ async function initDaemonComponents(config) {
20449
20899
  adapters: cliManager.adapters,
20450
20900
  providerLoader,
20451
20901
  instanceManager,
20452
- sessionRegistry
20902
+ sessionRegistry,
20903
+ onProviderSettingChanged: async (providerType) => {
20904
+ await refreshProviderAvailability(providerType);
20905
+ config.onStatusChange?.();
20906
+ }
20453
20907
  });
20454
20908
  agentStreamManager = new DaemonAgentStreamManager(
20455
20909
  LOG.forComponent("AgentStream").asLogFn(),
@@ -20472,6 +20926,7 @@ async function initDaemonComponents(config) {
20472
20926
  onIdeConnected: () => poller?.start(),
20473
20927
  onStatusChange: config.onStatusChange,
20474
20928
  onPostChatCommand: config.onPostChatCommand,
20929
+ sessionHostControl: config.sessionHostControl,
20475
20930
  getCdpLogFn: config.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
20476
20931
  });
20477
20932
  poller = new AgentStreamPoller({