@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.mjs CHANGED
@@ -233,7 +233,7 @@ var init_config = __esm({
233
233
 
234
234
  // src/logging/logger.ts
235
235
  import * as fs2 from "fs";
236
- import * as path4 from "path";
236
+ import * as path6 from "path";
237
237
  import * as os4 from "os";
238
238
  function setLogLevel(level) {
239
239
  currentLevel = level;
@@ -249,13 +249,13 @@ function getDaemonLogDir() {
249
249
  return LOG_DIR;
250
250
  }
251
251
  function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
252
- return path4.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
252
+ return path6.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
253
253
  }
254
254
  function checkDateRotation() {
255
255
  const today = getDateStr();
256
256
  if (today !== currentDate) {
257
257
  currentDate = today;
258
- currentLogFile = path4.join(LOG_DIR, `daemon-${currentDate}.log`);
258
+ currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
259
259
  cleanOldLogs();
260
260
  }
261
261
  }
@@ -269,7 +269,7 @@ function cleanOldLogs() {
269
269
  const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
270
270
  if (dateMatch && dateMatch[1] < cutoffStr) {
271
271
  try {
272
- fs2.unlinkSync(path4.join(LOG_DIR, file));
272
+ fs2.unlinkSync(path6.join(LOG_DIR, file));
273
273
  } catch {
274
274
  }
275
275
  }
@@ -393,7 +393,7 @@ var init_logger = __esm({
393
393
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
394
394
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
395
395
  currentLevel = "info";
396
- 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");
396
+ 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");
397
397
  MAX_LOG_SIZE = 5 * 1024 * 1024;
398
398
  MAX_LOG_DAYS = 7;
399
399
  try {
@@ -401,16 +401,16 @@ var init_logger = __esm({
401
401
  } catch {
402
402
  }
403
403
  currentDate = getDateStr();
404
- currentLogFile = path4.join(LOG_DIR, `daemon-${currentDate}.log`);
404
+ currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
405
405
  cleanOldLogs();
406
406
  try {
407
- const oldLog = path4.join(LOG_DIR, "daemon.log");
407
+ const oldLog = path6.join(LOG_DIR, "daemon.log");
408
408
  if (fs2.existsSync(oldLog)) {
409
409
  const stat = fs2.statSync(oldLog);
410
410
  const oldDate = stat.mtime.toISOString().slice(0, 10);
411
- fs2.renameSync(oldLog, path4.join(LOG_DIR, `daemon-${oldDate}.log`));
411
+ fs2.renameSync(oldLog, path6.join(LOG_DIR, `daemon-${oldDate}.log`));
412
412
  }
413
- const oldLogBackup = path4.join(LOG_DIR, "daemon.log.old");
413
+ const oldLogBackup = path6.join(LOG_DIR, "daemon.log.old");
414
414
  if (fs2.existsSync(oldLogBackup)) {
415
415
  fs2.unlinkSync(oldLogBackup);
416
416
  }
@@ -442,7 +442,7 @@ var init_logger = __esm({
442
442
  }
443
443
  };
444
444
  interceptorInstalled = false;
445
- LOG_PATH = path4.join(LOG_DIR, `daemon-${getDateStr()}.log`);
445
+ LOG_PATH = path6.join(LOG_DIR, `daemon-${getDateStr()}.log`);
446
446
  }
447
447
  });
448
448
 
@@ -802,7 +802,7 @@ __export(provider_cli_adapter_exports, {
802
802
  normalizeCliProviderForRuntime: () => normalizeCliProviderForRuntime
803
803
  });
804
804
  import * as os8 from "os";
805
- import * as path7 from "path";
805
+ import * as path9 from "path";
806
806
  import { execSync as execSync3 } from "child_process";
807
807
  function stripAnsi(str) {
808
808
  return str.replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][\s\S]*?\x1B\\/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B\[\d*[A-HJKSTfG]/g, " ").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/ +/g, " ");
@@ -873,16 +873,22 @@ function computeTerminalQueryTail(buffer) {
873
873
  return "";
874
874
  }
875
875
  function findBinary(name) {
876
+ const trimmed = String(name || "").trim();
877
+ if (!trimmed) return trimmed;
878
+ const expanded = trimmed.startsWith("~") ? path9.join(os8.homedir(), trimmed.slice(1)) : trimmed;
879
+ if (path9.isAbsolute(expanded) || expanded.includes("/") || expanded.includes("\\")) {
880
+ return path9.isAbsolute(expanded) ? expanded : path9.resolve(expanded);
881
+ }
876
882
  const isWin = os8.platform() === "win32";
877
883
  try {
878
- const cmd = isWin ? `where ${name}` : `which ${name}`;
884
+ const cmd = isWin ? `where ${trimmed}` : `which ${trimmed}`;
879
885
  return execSync3(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n")[0].trim();
880
886
  } catch {
881
- return isWin ? `${name}.cmd` : name;
887
+ return isWin ? `${trimmed}.cmd` : trimmed;
882
888
  }
883
889
  }
884
890
  function isScriptBinary(binaryPath) {
885
- if (!path7.isAbsolute(binaryPath)) return false;
891
+ if (!path9.isAbsolute(binaryPath)) return false;
886
892
  try {
887
893
  const fs15 = __require("fs");
888
894
  const resolved = fs15.realpathSync(binaryPath);
@@ -898,7 +904,7 @@ function isScriptBinary(binaryPath) {
898
904
  }
899
905
  }
900
906
  function looksLikeMachOOrElf(filePath) {
901
- if (!path7.isAbsolute(filePath)) return false;
907
+ if (!path9.isAbsolute(filePath)) return false;
902
908
  try {
903
909
  const fs15 = __require("fs");
904
910
  const resolved = fs15.realpathSync(filePath);
@@ -1379,16 +1385,17 @@ var init_provider_cli_adapter = __esm({
1379
1385
  async spawn() {
1380
1386
  if (this.ptyProcess) return;
1381
1387
  const { spawn: spawnConfig } = this.provider;
1382
- const binaryPath = findBinary(spawnConfig.command);
1388
+ const configuredCommand = typeof this.runtimeSettings.executablePath === "string" && this.runtimeSettings.executablePath.trim() ? this.runtimeSettings.executablePath.trim() : spawnConfig.command;
1389
+ const binaryPath = findBinary(configuredCommand);
1383
1390
  const isWin = os8.platform() === "win32";
1384
1391
  const allArgs = [...spawnConfig.args, ...this.extraArgs];
1385
1392
  LOG.info("CLI", `[${this.cliType}] Spawning in ${this.workingDir}`);
1386
1393
  this.resetTraceSession();
1387
1394
  let shellCmd;
1388
1395
  let shellArgs;
1389
- const useShellUnix = !isWin && (!!spawnConfig.shell || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
1396
+ const useShellUnix = !isWin && (!!spawnConfig.shell || !path9.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
1390
1397
  const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
1391
- const useShellWin = !!spawnConfig.shell || isCmdShim || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
1398
+ const useShellWin = !!spawnConfig.shell || isCmdShim || !path9.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
1392
1399
  const useShell = isWin ? useShellWin : useShellUnix;
1393
1400
  if (useShell) {
1394
1401
  if (!spawnConfig.shell && !isWin) {
@@ -1762,7 +1769,7 @@ var init_provider_cli_adapter = __esm({
1762
1769
  `[${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)}`
1763
1770
  );
1764
1771
  }
1765
- await new Promise((resolve9) => setTimeout(resolve9, 50));
1772
+ await new Promise((resolve12) => setTimeout(resolve12, 50));
1766
1773
  }
1767
1774
  const finalScreenText = this.terminalScreen.getText() || "";
1768
1775
  LOG.warn(
@@ -2273,7 +2280,7 @@ ${data.message || ""}`.trim();
2273
2280
  const deadline = Date.now() + 1e4;
2274
2281
  while (this.startupParseGate && Date.now() < deadline) {
2275
2282
  this.resolveStartupState("send_wait");
2276
- await new Promise((resolve9) => setTimeout(resolve9, 50));
2283
+ await new Promise((resolve12) => setTimeout(resolve12, 50));
2277
2284
  }
2278
2285
  }
2279
2286
  await this.waitForInteractivePrompt();
@@ -2498,17 +2505,17 @@ ${data.message || ""}`.trim();
2498
2505
  }
2499
2506
  }
2500
2507
  waitForStopped(timeoutMs) {
2501
- return new Promise((resolve9) => {
2508
+ return new Promise((resolve12) => {
2502
2509
  const startedAt = Date.now();
2503
2510
  const timer = setInterval(() => {
2504
2511
  if (!this.ptyProcess || this.currentStatus === "stopped") {
2505
2512
  clearInterval(timer);
2506
- resolve9(true);
2513
+ resolve12(true);
2507
2514
  return;
2508
2515
  }
2509
2516
  if (Date.now() - startedAt >= timeoutMs) {
2510
2517
  clearInterval(timer);
2511
- resolve9(false);
2518
+ resolve12(false);
2512
2519
  }
2513
2520
  }, 100);
2514
2521
  });
@@ -3124,6 +3131,7 @@ function resetState() {
3124
3131
  import { execSync } from "child_process";
3125
3132
  import { existsSync as existsSync4 } from "fs";
3126
3133
  import { platform, homedir as homedir3 } from "os";
3134
+ import * as path4 from "path";
3127
3135
  var BUILTIN_IDE_DEFINITIONS = [];
3128
3136
  var registeredIDEs = /* @__PURE__ */ new Map();
3129
3137
  function registerIDEDefinition(def) {
@@ -3140,9 +3148,16 @@ function getMergedDefinitions() {
3140
3148
  return [...merged.values()];
3141
3149
  }
3142
3150
  function findCliCommand(command) {
3151
+ const trimmed = String(command || "").trim();
3152
+ if (!trimmed) return null;
3153
+ if (path4.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
3154
+ const candidate = trimmed.startsWith("~") ? path4.join(homedir3(), trimmed.slice(1)) : trimmed;
3155
+ const resolved = path4.isAbsolute(candidate) ? candidate : path4.resolve(candidate);
3156
+ return existsSync4(resolved) ? resolved : null;
3157
+ }
3143
3158
  try {
3144
3159
  const result = execSync(
3145
- platform() === "win32" ? `where ${command}` : `which ${command}`,
3160
+ platform() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
3146
3161
  { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
3147
3162
  ).trim();
3148
3163
  return result.split("\n")[0] || null;
@@ -3165,23 +3180,23 @@ function getIdeVersion(cliCommand) {
3165
3180
  function checkPathExists(paths) {
3166
3181
  const home = homedir3();
3167
3182
  for (const p of paths) {
3168
- if (p.includes("*")) {
3183
+ const normalized = p.startsWith("~") ? path4.join(home, p.slice(1)) : p;
3184
+ if (normalized.includes("*")) {
3169
3185
  const username = home.split(/[\\/]/).pop() || "";
3170
- const resolved = p.replace("*", username);
3186
+ const resolved = normalized.replace("*", username);
3171
3187
  if (existsSync4(resolved)) return resolved;
3172
3188
  } else {
3173
- if (existsSync4(p)) return p;
3189
+ if (existsSync4(normalized)) return normalized;
3174
3190
  }
3175
3191
  }
3176
3192
  return null;
3177
3193
  }
3178
- async function detectIDEs() {
3194
+ async function detectIDEs(providerLoader) {
3179
3195
  const os18 = platform();
3180
3196
  const results = [];
3181
3197
  for (const def of getMergedDefinitions()) {
3182
- const cliPath = findCliCommand(def.cli);
3183
- const appPath = checkPathExists(def.paths[os18] || []);
3184
- const installed = !!(cliPath || appPath);
3198
+ const cliPath = findCliCommand(providerLoader?.getIdeCliCommand(def.id, def.cli) || def.cli);
3199
+ const appPath = checkPathExists(providerLoader?.getIdePathCandidates(def.id, def.paths[os18] || []) || []);
3185
3200
  let resolvedCli = cliPath;
3186
3201
  if (!resolvedCli && appPath && os18 === "darwin") {
3187
3202
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
@@ -3204,6 +3219,7 @@ async function detectIDEs() {
3204
3219
  }
3205
3220
  }
3206
3221
  }
3222
+ const installed = os18 === "darwin" ? !!(resolvedCli || appPath) : !!resolvedCli;
3207
3223
  const version = resolvedCli ? getIdeVersion(resolvedCli) : null;
3208
3224
  results.push({
3209
3225
  id: def.id,
@@ -3222,48 +3238,77 @@ async function detectIDEs() {
3222
3238
  // src/detection/cli-detector.ts
3223
3239
  import { exec } from "child_process";
3224
3240
  import * as os2 from "os";
3241
+ import * as path5 from "path";
3242
+ import { existsSync as existsSync5 } from "fs";
3225
3243
  function parseVersion(raw) {
3226
3244
  const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
3227
3245
  return match ? match[1] : raw.split("\n")[0].slice(0, 100);
3228
3246
  }
3247
+ function shellQuote(value) {
3248
+ if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
3249
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
3250
+ }
3251
+ function expandHome(value) {
3252
+ const trimmed = value.trim();
3253
+ if (!trimmed.startsWith("~")) return trimmed;
3254
+ return path5.join(os2.homedir(), trimmed.slice(1));
3255
+ }
3256
+ function isExplicitCommandPath(command) {
3257
+ const trimmed = command.trim();
3258
+ return path5.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
3259
+ }
3260
+ function resolveCommandPath(command) {
3261
+ const trimmed = command.trim();
3262
+ if (!trimmed) return null;
3263
+ if (isExplicitCommandPath(trimmed)) {
3264
+ const expanded = expandHome(trimmed);
3265
+ const candidate = path5.isAbsolute(expanded) ? expanded : path5.resolve(expanded);
3266
+ return existsSync5(candidate) ? candidate : null;
3267
+ }
3268
+ return null;
3269
+ }
3229
3270
  function execAsync(cmd, timeoutMs = 5e3) {
3230
- return new Promise((resolve9) => {
3271
+ return new Promise((resolve12) => {
3231
3272
  const child = exec(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout) => {
3232
3273
  if (err || !stdout?.trim()) {
3233
- resolve9(null);
3274
+ resolve12(null);
3234
3275
  } else {
3235
- resolve9(stdout.trim());
3276
+ resolve12(stdout.trim());
3236
3277
  }
3237
3278
  });
3238
- child.on("error", () => resolve9(null));
3279
+ child.on("error", () => resolve12(null));
3239
3280
  });
3240
3281
  }
3241
- async function detectCLIs(providerLoader) {
3282
+ async function detectCLIs(providerLoader, options) {
3242
3283
  const platform9 = os2.platform();
3243
3284
  const whichCmd = platform9 === "win32" ? "where" : "which";
3285
+ const includeVersion = options?.includeVersion !== false;
3244
3286
  const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
3245
3287
  const results = await Promise.all(
3246
3288
  cliList.map(async (cli) => {
3247
3289
  try {
3248
- const pathResult = await execAsync(`${whichCmd} ${cli.command}`);
3290
+ const explicitPath = resolveCommandPath(cli.command);
3291
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
3249
3292
  if (!pathResult) return { ...cli, installed: false };
3250
- const firstPath = pathResult.split("\n")[0];
3293
+ const firstPath = explicitPath || pathResult.split("\n")[0];
3251
3294
  let version;
3252
- try {
3295
+ if (includeVersion) {
3253
3296
  const versionCommands = [
3254
- cli.versionCommand,
3255
- `${cli.command} --version`,
3256
- `${cli.command} -V`,
3257
- `${cli.command} -v`
3297
+ `"${firstPath}" --version`,
3298
+ `"${firstPath}" -V`,
3299
+ `"${firstPath}" -v`,
3300
+ cli.versionCommand
3258
3301
  ].filter((v) => !!v);
3259
- for (const versionCommand of versionCommands) {
3260
- const versionResult = await execAsync(versionCommand, 3e3);
3261
- if (versionResult) {
3262
- version = parseVersion(versionResult);
3263
- break;
3302
+ try {
3303
+ for (const versionCommand of versionCommands) {
3304
+ const versionResult = await execAsync(versionCommand, 3e3);
3305
+ if (versionResult) {
3306
+ version = parseVersion(versionResult);
3307
+ break;
3308
+ }
3264
3309
  }
3310
+ } catch {
3265
3311
  }
3266
- } catch {
3267
3312
  }
3268
3313
  return { ...cli, installed: true, version, path: firstPath };
3269
3314
  } catch {
@@ -3273,7 +3318,7 @@ async function detectCLIs(providerLoader) {
3273
3318
  );
3274
3319
  return results;
3275
3320
  }
3276
- async function detectCLI(cliId, providerLoader) {
3321
+ async function detectCLI(cliId, providerLoader, options) {
3277
3322
  const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
3278
3323
  if (providerLoader) {
3279
3324
  const cliList = providerLoader.getCliDetectionList();
@@ -3282,25 +3327,28 @@ async function detectCLI(cliId, providerLoader) {
3282
3327
  const platform9 = os2.platform();
3283
3328
  const whichCmd = platform9 === "win32" ? "where" : "which";
3284
3329
  try {
3285
- const pathResult = await execAsync(`${whichCmd} ${target.command}`);
3330
+ const explicitPath = resolveCommandPath(target.command);
3331
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
3286
3332
  if (!pathResult) return null;
3287
- const firstPath = pathResult.split("\n")[0];
3333
+ const firstPath = explicitPath || pathResult.split("\n")[0];
3288
3334
  let version;
3289
- try {
3335
+ if (options?.includeVersion !== false) {
3290
3336
  const versionCommands = [
3291
- target.versionCommand,
3292
- `${target.command} --version`,
3293
- `${target.command} -V`,
3294
- `${target.command} -v`
3337
+ `"${firstPath}" --version`,
3338
+ `"${firstPath}" -V`,
3339
+ `"${firstPath}" -v`,
3340
+ target.versionCommand
3295
3341
  ].filter((v) => !!v);
3296
- for (const versionCommand of versionCommands) {
3297
- const versionResult = await execAsync(versionCommand, 3e3);
3298
- if (versionResult) {
3299
- version = parseVersion(versionResult);
3300
- break;
3342
+ try {
3343
+ for (const versionCommand of versionCommands) {
3344
+ const versionResult = await execAsync(versionCommand, 3e3);
3345
+ if (versionResult) {
3346
+ version = parseVersion(versionResult);
3347
+ break;
3348
+ }
3301
3349
  }
3350
+ } catch {
3302
3351
  }
3303
- } catch {
3304
3352
  }
3305
3353
  return { ...target, installed: true, version, path: firstPath };
3306
3354
  } catch {
@@ -3308,7 +3356,7 @@ async function detectCLI(cliId, providerLoader) {
3308
3356
  }
3309
3357
  }
3310
3358
  }
3311
- const all = await detectCLIs(providerLoader);
3359
+ const all = await detectCLIs(providerLoader, options);
3312
3360
  return all.find((c) => c.id === resolvedId && c.installed) || null;
3313
3361
  }
3314
3362
 
@@ -3435,7 +3483,7 @@ var DaemonCdpManager = class {
3435
3483
  * Returns multiple entries if multiple IDE windows are open on same port
3436
3484
  */
3437
3485
  static listAllTargets(port) {
3438
- return new Promise((resolve9) => {
3486
+ return new Promise((resolve12) => {
3439
3487
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
3440
3488
  let data = "";
3441
3489
  res.on("data", (chunk) => data += chunk.toString());
@@ -3451,16 +3499,16 @@ var DaemonCdpManager = class {
3451
3499
  (t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
3452
3500
  );
3453
3501
  const fallbackPages = pages.filter((t) => !isNonMain(t.title || ""));
3454
- resolve9(mainPages.length > 0 ? mainPages : fallbackPages);
3502
+ resolve12(mainPages.length > 0 ? mainPages : fallbackPages);
3455
3503
  } catch {
3456
- resolve9([]);
3504
+ resolve12([]);
3457
3505
  }
3458
3506
  });
3459
3507
  });
3460
- req.on("error", () => resolve9([]));
3508
+ req.on("error", () => resolve12([]));
3461
3509
  req.setTimeout(2e3, () => {
3462
3510
  req.destroy();
3463
- resolve9([]);
3511
+ resolve12([]);
3464
3512
  });
3465
3513
  });
3466
3514
  }
@@ -3500,7 +3548,7 @@ var DaemonCdpManager = class {
3500
3548
  }
3501
3549
  }
3502
3550
  findTargetOnPort(port) {
3503
- return new Promise((resolve9) => {
3551
+ return new Promise((resolve12) => {
3504
3552
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
3505
3553
  let data = "";
3506
3554
  res.on("data", (chunk) => data += chunk.toString());
@@ -3511,7 +3559,7 @@ var DaemonCdpManager = class {
3511
3559
  (t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
3512
3560
  );
3513
3561
  if (pages.length === 0) {
3514
- resolve9(targets.find((t) => t.webSocketDebuggerUrl) || null);
3562
+ resolve12(targets.find((t) => t.webSocketDebuggerUrl) || null);
3515
3563
  return;
3516
3564
  }
3517
3565
  const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
@@ -3521,24 +3569,24 @@ var DaemonCdpManager = class {
3521
3569
  const specific = list.find((t) => t.id === this._targetId);
3522
3570
  if (specific) {
3523
3571
  this._pageTitle = specific.title || "";
3524
- resolve9(specific);
3572
+ resolve12(specific);
3525
3573
  } else {
3526
3574
  this.log(`[CDP] Target ${this._targetId} not found in page list`);
3527
- resolve9(null);
3575
+ resolve12(null);
3528
3576
  }
3529
3577
  return;
3530
3578
  }
3531
3579
  this._pageTitle = list[0]?.title || "";
3532
- resolve9(list[0]);
3580
+ resolve12(list[0]);
3533
3581
  } catch {
3534
- resolve9(null);
3582
+ resolve12(null);
3535
3583
  }
3536
3584
  });
3537
3585
  });
3538
- req.on("error", () => resolve9(null));
3586
+ req.on("error", () => resolve12(null));
3539
3587
  req.setTimeout(2e3, () => {
3540
3588
  req.destroy();
3541
- resolve9(null);
3589
+ resolve12(null);
3542
3590
  });
3543
3591
  });
3544
3592
  }
@@ -3549,7 +3597,7 @@ var DaemonCdpManager = class {
3549
3597
  this.extensionProviders = providers;
3550
3598
  }
3551
3599
  connectToTarget(wsUrl) {
3552
- return new Promise((resolve9) => {
3600
+ return new Promise((resolve12) => {
3553
3601
  this.ws = new WebSocket(wsUrl);
3554
3602
  this.ws.on("open", async () => {
3555
3603
  this._connected = true;
@@ -3559,17 +3607,17 @@ var DaemonCdpManager = class {
3559
3607
  }
3560
3608
  this.connectBrowserWs().catch(() => {
3561
3609
  });
3562
- resolve9(true);
3610
+ resolve12(true);
3563
3611
  });
3564
3612
  this.ws.on("message", (data) => {
3565
3613
  try {
3566
3614
  const msg = JSON.parse(data.toString());
3567
3615
  if (msg.id && this.pending.has(msg.id)) {
3568
- const { resolve: resolve10, reject } = this.pending.get(msg.id);
3616
+ const { resolve: resolve13, reject } = this.pending.get(msg.id);
3569
3617
  this.pending.delete(msg.id);
3570
3618
  this.failureCount = 0;
3571
3619
  if (msg.error) reject(new Error(msg.error.message));
3572
- else resolve10(msg.result);
3620
+ else resolve13(msg.result);
3573
3621
  } else if (msg.method === "Runtime.executionContextCreated") {
3574
3622
  this.contexts.add(msg.params.context.id);
3575
3623
  } else if (msg.method === "Runtime.executionContextDestroyed") {
@@ -3592,7 +3640,7 @@ var DaemonCdpManager = class {
3592
3640
  this.ws.on("error", (err) => {
3593
3641
  this.log(`[CDP] WebSocket error: ${err.message}`);
3594
3642
  this._connected = false;
3595
- resolve9(false);
3643
+ resolve12(false);
3596
3644
  });
3597
3645
  });
3598
3646
  }
@@ -3606,7 +3654,7 @@ var DaemonCdpManager = class {
3606
3654
  return;
3607
3655
  }
3608
3656
  this.log(`[CDP] Connecting browser WS for target discovery...`);
3609
- await new Promise((resolve9, reject) => {
3657
+ await new Promise((resolve12, reject) => {
3610
3658
  this.browserWs = new WebSocket(browserWsUrl);
3611
3659
  this.browserWs.on("open", async () => {
3612
3660
  this._browserConnected = true;
@@ -3616,16 +3664,16 @@ var DaemonCdpManager = class {
3616
3664
  } catch (e) {
3617
3665
  this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
3618
3666
  }
3619
- resolve9();
3667
+ resolve12();
3620
3668
  });
3621
3669
  this.browserWs.on("message", (data) => {
3622
3670
  try {
3623
3671
  const msg = JSON.parse(data.toString());
3624
3672
  if (msg.id && this.browserPending.has(msg.id)) {
3625
- const { resolve: resolve10, reject: reject2 } = this.browserPending.get(msg.id);
3673
+ const { resolve: resolve13, reject: reject2 } = this.browserPending.get(msg.id);
3626
3674
  this.browserPending.delete(msg.id);
3627
3675
  if (msg.error) reject2(new Error(msg.error.message));
3628
- else resolve10(msg.result);
3676
+ else resolve13(msg.result);
3629
3677
  }
3630
3678
  } catch {
3631
3679
  }
@@ -3645,31 +3693,31 @@ var DaemonCdpManager = class {
3645
3693
  }
3646
3694
  }
3647
3695
  getBrowserWsUrl() {
3648
- return new Promise((resolve9) => {
3696
+ return new Promise((resolve12) => {
3649
3697
  const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
3650
3698
  let data = "";
3651
3699
  res.on("data", (chunk) => data += chunk.toString());
3652
3700
  res.on("end", () => {
3653
3701
  try {
3654
3702
  const info = JSON.parse(data);
3655
- resolve9(info.webSocketDebuggerUrl || null);
3703
+ resolve12(info.webSocketDebuggerUrl || null);
3656
3704
  } catch {
3657
- resolve9(null);
3705
+ resolve12(null);
3658
3706
  }
3659
3707
  });
3660
3708
  });
3661
- req.on("error", () => resolve9(null));
3709
+ req.on("error", () => resolve12(null));
3662
3710
  req.setTimeout(3e3, () => {
3663
3711
  req.destroy();
3664
- resolve9(null);
3712
+ resolve12(null);
3665
3713
  });
3666
3714
  });
3667
3715
  }
3668
3716
  sendBrowser(method, params = {}, timeoutMs = 15e3) {
3669
- return new Promise((resolve9, reject) => {
3717
+ return new Promise((resolve12, reject) => {
3670
3718
  if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
3671
3719
  const id = this.browserMsgId++;
3672
- this.browserPending.set(id, { resolve: resolve9, reject });
3720
+ this.browserPending.set(id, { resolve: resolve12, reject });
3673
3721
  this.browserWs.send(JSON.stringify({ id, method, params }));
3674
3722
  setTimeout(() => {
3675
3723
  if (this.browserPending.has(id)) {
@@ -3709,11 +3757,11 @@ var DaemonCdpManager = class {
3709
3757
  }
3710
3758
  // ─── CDP Protocol ────────────────────────────────────────
3711
3759
  sendInternal(method, params = {}, timeoutMs = 15e3) {
3712
- return new Promise((resolve9, reject) => {
3760
+ return new Promise((resolve12, reject) => {
3713
3761
  if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
3714
3762
  if (this.ws.readyState !== WebSocket.OPEN) return reject(new Error("WebSocket not open"));
3715
3763
  const id = this.msgId++;
3716
- this.pending.set(id, { resolve: resolve9, reject });
3764
+ this.pending.set(id, { resolve: resolve12, reject });
3717
3765
  this.ws.send(JSON.stringify({ id, method, params }));
3718
3766
  setTimeout(() => {
3719
3767
  if (this.pending.has(id)) {
@@ -3962,7 +4010,7 @@ var DaemonCdpManager = class {
3962
4010
  const browserWs = this.browserWs;
3963
4011
  let msgId = this.browserMsgId;
3964
4012
  const sendWs = (method, params = {}, sessionId) => {
3965
- return new Promise((resolve9, reject) => {
4013
+ return new Promise((resolve12, reject) => {
3966
4014
  const mid = msgId++;
3967
4015
  this.browserMsgId = msgId;
3968
4016
  const handler = (raw) => {
@@ -3971,7 +4019,7 @@ var DaemonCdpManager = class {
3971
4019
  if (msg.id === mid) {
3972
4020
  browserWs.removeListener("message", handler);
3973
4021
  if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
3974
- else resolve9(msg.result);
4022
+ else resolve12(msg.result);
3975
4023
  }
3976
4024
  } catch {
3977
4025
  }
@@ -4162,14 +4210,14 @@ var DaemonCdpManager = class {
4162
4210
  if (!ws || ws.readyState !== WebSocket.OPEN) {
4163
4211
  throw new Error("CDP not connected");
4164
4212
  }
4165
- return new Promise((resolve9, reject) => {
4213
+ return new Promise((resolve12, reject) => {
4166
4214
  const id = getNextId();
4167
4215
  pendingMap.set(id, {
4168
4216
  resolve: (result) => {
4169
4217
  if (result?.result?.subtype === "error") {
4170
4218
  reject(new Error(result.result.description));
4171
4219
  } else {
4172
- resolve9(result?.result?.value);
4220
+ resolve12(result?.result?.value);
4173
4221
  }
4174
4222
  },
4175
4223
  reject
@@ -4201,10 +4249,10 @@ var DaemonCdpManager = class {
4201
4249
  throw new Error("CDP not connected");
4202
4250
  }
4203
4251
  const sendViaSession = (method, params = {}) => {
4204
- return new Promise((resolve9, reject) => {
4252
+ return new Promise((resolve12, reject) => {
4205
4253
  const pendingMap = this._browserConnected ? this.browserPending : this.pending;
4206
4254
  const id = this._browserConnected ? this.browserMsgId++ : this.msgId++;
4207
- pendingMap.set(id, { resolve: resolve9, reject });
4255
+ pendingMap.set(id, { resolve: resolve12, reject });
4208
4256
  ws.send(JSON.stringify({ id, sessionId, method, params }));
4209
4257
  setTimeout(() => {
4210
4258
  if (pendingMap.has(id)) {
@@ -4832,9 +4880,9 @@ function normalizeControlValue(value) {
4832
4880
 
4833
4881
  // src/config/chat-history.ts
4834
4882
  import * as fs3 from "fs";
4835
- import * as path5 from "path";
4883
+ import * as path7 from "path";
4836
4884
  import * as os5 from "os";
4837
- var HISTORY_DIR = path5.join(os5.homedir(), ".adhdev", "history");
4885
+ var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
4838
4886
  var RETAIN_DAYS = 30;
4839
4887
  var ChatHistoryWriter = class {
4840
4888
  /** Last seen message count per agent (deduplication) */
@@ -4879,11 +4927,11 @@ var ChatHistoryWriter = class {
4879
4927
  });
4880
4928
  }
4881
4929
  if (newMessages.length === 0) return;
4882
- const dir = path5.join(HISTORY_DIR, this.sanitize(agentType));
4930
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
4883
4931
  fs3.mkdirSync(dir, { recursive: true });
4884
4932
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4885
4933
  const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
4886
- const filePath = path5.join(dir, `${filePrefix}${date}.jsonl`);
4934
+ const filePath = path7.join(dir, `${filePrefix}${date}.jsonl`);
4887
4935
  const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
4888
4936
  fs3.appendFileSync(filePath, lines, "utf-8");
4889
4937
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
@@ -4937,14 +4985,14 @@ var ChatHistoryWriter = class {
4937
4985
  this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
4938
4986
  this.lastSeenCounts.delete(fromDedupKey);
4939
4987
  }
4940
- const dir = path5.join(HISTORY_DIR, this.sanitize(agentType));
4988
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
4941
4989
  if (!fs3.existsSync(dir)) return;
4942
4990
  const fromPrefix = `${this.sanitize(fromId)}_`;
4943
4991
  const toPrefix = `${this.sanitize(toId)}_`;
4944
4992
  const files = fs3.readdirSync(dir).filter((file) => file.startsWith(fromPrefix) && file.endsWith(".jsonl"));
4945
4993
  for (const file of files) {
4946
- const sourcePath = path5.join(dir, file);
4947
- const targetPath = path5.join(dir, `${toPrefix}${file.slice(fromPrefix.length)}`);
4994
+ const sourcePath = path7.join(dir, file);
4995
+ const targetPath = path7.join(dir, `${toPrefix}${file.slice(fromPrefix.length)}`);
4948
4996
  const sourceLines = fs3.readFileSync(sourcePath, "utf-8").split("\n").filter(Boolean);
4949
4997
  const rewritten = sourceLines.map((line) => {
4950
4998
  try {
@@ -4985,10 +5033,10 @@ var ChatHistoryWriter = class {
4985
5033
  const cutoff = Date.now() - RETAIN_DAYS * 24 * 60 * 60 * 1e3;
4986
5034
  const agentDirs = fs3.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
4987
5035
  for (const dir of agentDirs) {
4988
- const dirPath = path5.join(HISTORY_DIR, dir.name);
5036
+ const dirPath = path7.join(HISTORY_DIR, dir.name);
4989
5037
  const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
4990
5038
  for (const file of files) {
4991
- const filePath = path5.join(dirPath, file);
5039
+ const filePath = path7.join(dirPath, file);
4992
5040
  const stat = fs3.statSync(filePath);
4993
5041
  if (stat.mtimeMs < cutoff) {
4994
5042
  fs3.unlinkSync(filePath);
@@ -5006,7 +5054,7 @@ var ChatHistoryWriter = class {
5006
5054
  function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
5007
5055
  try {
5008
5056
  const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
5009
- const dir = path5.join(HISTORY_DIR, sanitized);
5057
+ const dir = path7.join(HISTORY_DIR, sanitized);
5010
5058
  if (!fs3.existsSync(dir)) return { messages: [], hasMore: false };
5011
5059
  const sanitizedInstance = historySessionId?.replace(/[^a-zA-Z0-9_-]/g, "_");
5012
5060
  const files = fs3.readdirSync(dir).filter((f) => {
@@ -5020,7 +5068,7 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
5020
5068
  const needed = offset + limit + 1;
5021
5069
  for (const file of files) {
5022
5070
  if (allMessages.length >= needed) break;
5023
- const filePath = path5.join(dir, file);
5071
+ const filePath = path7.join(dir, file);
5024
5072
  const content = fs3.readFileSync(filePath, "utf-8");
5025
5073
  const lines = content.trim().split("\n").filter(Boolean);
5026
5074
  for (let i = lines.length - 1; i >= 0; i--) {
@@ -5042,7 +5090,7 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
5042
5090
  function listSavedHistorySessions(agentType, options = {}) {
5043
5091
  try {
5044
5092
  const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
5045
- const dir = path5.join(HISTORY_DIR, sanitized);
5093
+ const dir = path7.join(HISTORY_DIR, sanitized);
5046
5094
  if (!fs3.existsSync(dir)) return { sessions: [], hasMore: false };
5047
5095
  const groupedFiles = /* @__PURE__ */ new Map();
5048
5096
  const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
@@ -5063,7 +5111,7 @@ function listSavedHistorySessions(agentType, options = {}) {
5063
5111
  let sessionTitle = "";
5064
5112
  let preview = "";
5065
5113
  for (const file of files.sort()) {
5066
- const filePath = path5.join(dir, file);
5114
+ const filePath = path7.join(dir, file);
5067
5115
  const content = fs3.readFileSync(filePath, "utf-8");
5068
5116
  const lines = content.split("\n").filter(Boolean);
5069
5117
  for (const line of lines) {
@@ -5116,6 +5164,7 @@ var ExtensionProviderInstance = class {
5116
5164
  currentStatus = "idle";
5117
5165
  agentStreams = [];
5118
5166
  messages = [];
5167
+ prevMessageHashes = /* @__PURE__ */ new Map();
5119
5168
  activeModal = null;
5120
5169
  currentModel = "";
5121
5170
  currentMode = "";
@@ -5181,7 +5230,7 @@ var ExtensionProviderInstance = class {
5181
5230
  onEvent(event, data) {
5182
5231
  if (event === "stream_update") {
5183
5232
  if (data?.streams) this.agentStreams = data.streams;
5184
- if (data?.messages) this.messages = data.messages;
5233
+ if (data?.messages) this.messages = this.assignReceivedAt(data.messages);
5185
5234
  if (data?.activeModal !== void 0) this.activeModal = data.activeModal;
5186
5235
  if (data?.model) this.currentModel = data.model;
5187
5236
  if (data?.mode) this.currentMode = data.mode;
@@ -5207,6 +5256,7 @@ var ExtensionProviderInstance = class {
5207
5256
  dispose() {
5208
5257
  this.agentStreams = [];
5209
5258
  this.messages = [];
5259
+ this.prevMessageHashes.clear();
5210
5260
  this.monitor.reset();
5211
5261
  this.appliedEffectKeys.clear();
5212
5262
  this.runtimeMessages = [];
@@ -5362,6 +5412,23 @@ var ExtensionProviderInstance = class {
5362
5412
  this.chatId || this.instanceId
5363
5413
  );
5364
5414
  }
5415
+ /**
5416
+ * Assign stable receivedAt to extension messages.
5417
+ * Same pattern as IdeProviderInstance.readChat() prevByHash —
5418
+ * preserves first-seen timestamp across polling cycles.
5419
+ */
5420
+ assignReceivedAt(messages) {
5421
+ const now = Date.now();
5422
+ const nextHashes = /* @__PURE__ */ new Map();
5423
+ for (const msg of messages) {
5424
+ const hash = `${msg.role}:${(msg.content || "").slice(0, 100)}`;
5425
+ const prevTime = this.prevMessageHashes.get(hash);
5426
+ msg.receivedAt = prevTime || now;
5427
+ nextHashes.set(hash, msg.receivedAt);
5428
+ }
5429
+ this.prevMessageHashes = nextHashes;
5430
+ return messages;
5431
+ }
5365
5432
  mergeConversationMessages(messages) {
5366
5433
  if (this.runtimeMessages.length === 0) return messages;
5367
5434
  return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
@@ -5418,6 +5485,7 @@ ${effect.notification.body || ""}`.trim();
5418
5485
  }
5419
5486
  this.agentStreams = [];
5420
5487
  this.messages = [];
5488
+ this.prevMessageHashes.clear();
5421
5489
  this.activeModal = null;
5422
5490
  this.currentModel = "";
5423
5491
  this.currentMode = "";
@@ -6403,16 +6471,28 @@ function trimMessageForStatus(message, stringLimit) {
6403
6471
  if (!message || typeof message !== "object") return message;
6404
6472
  return trimStructuredStrings(message, stringLimit);
6405
6473
  }
6474
+ function normalizeMessageTime(message) {
6475
+ if (!message || typeof message !== "object") return message;
6476
+ const msg = message;
6477
+ if (msg.receivedAt == null) {
6478
+ const fallback = msg.timestamp ?? msg.createdAt;
6479
+ if (fallback != null) {
6480
+ const ts2 = typeof fallback === "string" ? Date.parse(fallback) : Number(fallback);
6481
+ if (Number.isFinite(ts2) && ts2 > 0) msg.receivedAt = ts2;
6482
+ }
6483
+ }
6484
+ return msg;
6485
+ }
6406
6486
  function trimMessagesForStatus(messages) {
6407
6487
  if (!Array.isArray(messages) || messages.length === 0) return [];
6408
6488
  const recent = messages.slice(-STATUS_ACTIVE_CHAT_MESSAGE_LIMIT);
6409
6489
  const kept = [];
6410
6490
  let totalBytes = 0;
6411
6491
  for (let i = recent.length - 1; i >= 0; i -= 1) {
6412
- let normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT);
6492
+ let normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_STRING_LIMIT));
6413
6493
  let size = estimateBytes(normalized);
6414
6494
  if (size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
6415
- normalized = trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT);
6495
+ normalized = normalizeMessageTime(trimMessageForStatus(recent[i], STATUS_ACTIVE_CHAT_FALLBACK_STRING_LIMIT));
6416
6496
  size = estimateBytes(normalized);
6417
6497
  }
6418
6498
  if (kept.length > 0 && totalBytes + size > STATUS_ACTIVE_CHAT_TOTAL_BYTES_LIMIT) {
@@ -7035,7 +7115,7 @@ async function handleSendChat(h, args) {
7035
7115
  if (isExtensionTransport(transport)) {
7036
7116
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
7037
7117
  try {
7038
- const evalResult = await h.evaluateProviderScript("sendMessage", { MESSAGE: text }, 3e4);
7118
+ const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
7039
7119
  if (evalResult?.result) {
7040
7120
  const parsed = parseMaybeJson(evalResult.result);
7041
7121
  if (didProviderConfirmSend(parsed)) {
@@ -7066,7 +7146,7 @@ async function handleSendChat(h, args) {
7066
7146
  return { success: false, error: `CDP for ${managerKey || "unknown"} not connected` };
7067
7147
  }
7068
7148
  _log(`Targeting IDE: ${getCurrentManagerKey(h)}`);
7069
- const sendScript = h.getProviderScript("sendMessage", { MESSAGE: text });
7149
+ const sendScript = h.getProviderScript("sendMessage", { message: text });
7070
7150
  if (sendScript) {
7071
7151
  try {
7072
7152
  const result = await targetCdp.evaluate(sendScript, 3e4);
@@ -7206,6 +7286,14 @@ async function handleListChats(h, args) {
7206
7286
  } catch {
7207
7287
  }
7208
7288
  }
7289
+ if (parsed?.sessions && Array.isArray(parsed.sessions)) {
7290
+ LOG.info("Command", `[list_chats] OK: ${parsed.sessions.length} chats`);
7291
+ return { success: true, chats: parsed.sessions };
7292
+ }
7293
+ if (parsed?.chats && Array.isArray(parsed.chats)) {
7294
+ LOG.info("Command", `[list_chats] OK: ${parsed.chats.length} chats`);
7295
+ return { success: true, chats: parsed.chats };
7296
+ }
7209
7297
  if (Array.isArray(parsed)) {
7210
7298
  LOG.info("Command", `[list_chats] OK: ${parsed.length} chats`);
7211
7299
  return { success: true, chats: parsed };
@@ -7275,7 +7363,13 @@ async function handleSwitchChat(h, args) {
7275
7363
  } catch (e) {
7276
7364
  return { success: false, error: `webviewSwitchSession failed: ${e.message}` };
7277
7365
  }
7278
- const script = h.getProviderScript("switchSession", { SESSION_ID: JSON.stringify(sessionId) }) || h.getProviderScript("switch_session", { SESSION_ID: JSON.stringify(sessionId) });
7366
+ const switchParams = {
7367
+ sessionId,
7368
+ title: sessionId,
7369
+ id: sessionId,
7370
+ SESSION_ID: JSON.stringify(sessionId)
7371
+ };
7372
+ const script = h.getProviderScript("switchSession", switchParams) || h.getProviderScript("switch_session", switchParams);
7279
7373
  if (!script) return { success: false, error: "switch_session script not available" };
7280
7374
  try {
7281
7375
  const raw = await cdp.evaluate(script, 15e3);
@@ -7354,8 +7448,8 @@ async function handleSetMode(h, args) {
7354
7448
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
7355
7449
  if (adapter) {
7356
7450
  const acpInstance = adapter._acpInstance;
7357
- if (acpInstance && typeof acpInstance.onEvent === "function") {
7358
- acpInstance.onEvent("set_mode", { mode });
7451
+ if (acpInstance && typeof acpInstance.setMode === "function") {
7452
+ await acpInstance.setMode(mode);
7359
7453
  return { success: true, mode };
7360
7454
  }
7361
7455
  }
@@ -7412,9 +7506,9 @@ async function handleChangeModel(h, args) {
7412
7506
  LOG.info("Command", `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
7413
7507
  if (adapter) {
7414
7508
  const acpInstance = adapter._acpInstance;
7415
- if (acpInstance && typeof acpInstance.onEvent === "function") {
7416
- acpInstance.onEvent("change_model", { model });
7417
- LOG.info("Command", `[change_model] Dispatched change_model event to ACP instance`);
7509
+ if (acpInstance && typeof acpInstance.setConfigOption === "function") {
7510
+ await acpInstance.setConfigOption("model", model);
7511
+ LOG.info("Command", `[change_model] Updated ACP model to ${model}`);
7418
7512
  return { success: true, model };
7419
7513
  }
7420
7514
  }
@@ -7534,6 +7628,18 @@ async function handleResolveAction(h, args) {
7534
7628
  const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
7535
7629
  return { success: ok };
7536
7630
  }
7631
+ if (transport === "acp") {
7632
+ const adapter = getTargetedCliAdapter(h, args, provider?.type);
7633
+ const acpInstance = adapter?._acpInstance;
7634
+ if (!acpInstance) return { success: false, error: "ACP instance not found" };
7635
+ try {
7636
+ await acpInstance.resolvePermission(action === "approve" || action === "accept" || action === "always");
7637
+ LOG.info("Command", `[resolveAction] ACP \u2192 ${action}`);
7638
+ return { success: true, action };
7639
+ } catch (e) {
7640
+ return { success: false, error: e?.message || "ACP resolve action failed" };
7641
+ }
7642
+ }
7537
7643
  if (provider?.scripts?.webviewResolveAction || provider?.scripts?.webview_resolve_action) {
7538
7644
  const script = h.getProviderScript("webviewResolveAction", { action, button, buttonText: button }) || h.getProviderScript("webview_resolve_action", { action, button, buttonText: button });
7539
7645
  if (script) {
@@ -7612,7 +7718,7 @@ async function handleResolveAction(h, args) {
7612
7718
 
7613
7719
  // src/commands/cdp-commands.ts
7614
7720
  import * as fs4 from "fs";
7615
- import * as path6 from "path";
7721
+ import * as path8 from "path";
7616
7722
  import * as os6 from "os";
7617
7723
  var KEY_TO_VK = {
7618
7724
  Backspace: 8,
@@ -7864,25 +7970,25 @@ function resolveSafePath(requestedPath) {
7864
7970
  const inputPath = rawPath || ".";
7865
7971
  const home = os6.homedir();
7866
7972
  if (inputPath.startsWith("~")) {
7867
- return path6.resolve(path6.join(home, inputPath.slice(1)));
7973
+ return path8.resolve(path8.join(home, inputPath.slice(1)));
7868
7974
  }
7869
7975
  if (process.platform === "win32") {
7870
7976
  const normalized = normalizeWindowsRequestedPath(inputPath);
7871
- if (path6.win32.isAbsolute(normalized)) {
7872
- return path6.win32.normalize(normalized);
7977
+ if (path8.win32.isAbsolute(normalized)) {
7978
+ return path8.win32.normalize(normalized);
7873
7979
  }
7874
- return path6.win32.resolve(normalized);
7980
+ return path8.win32.resolve(normalized);
7875
7981
  }
7876
- if (path6.isAbsolute(inputPath)) {
7877
- return path6.normalize(inputPath);
7982
+ if (path8.isAbsolute(inputPath)) {
7983
+ return path8.normalize(inputPath);
7878
7984
  }
7879
- return path6.resolve(inputPath);
7985
+ return path8.resolve(inputPath);
7880
7986
  }
7881
7987
  function listDirectoryEntriesSafe(dirPath) {
7882
7988
  const entries = fs4.readdirSync(dirPath, { withFileTypes: true });
7883
7989
  const files = [];
7884
7990
  for (const entry of entries) {
7885
- const entryPath = path6.join(dirPath, entry.name);
7991
+ const entryPath = path8.join(dirPath, entry.name);
7886
7992
  try {
7887
7993
  if (entry.isDirectory()) {
7888
7994
  files.push({ name: entry.name, type: "directory" });
@@ -7921,7 +8027,7 @@ async function handleFileRead(h, args) {
7921
8027
  async function handleFileWrite(h, args) {
7922
8028
  try {
7923
8029
  const filePath = resolveSafePath(args?.path);
7924
- fs4.mkdirSync(path6.dirname(filePath), { recursive: true });
8030
+ fs4.mkdirSync(path8.dirname(filePath), { recursive: true });
7925
8031
  fs4.writeFileSync(filePath, args?.content || "", "utf-8");
7926
8032
  return { success: true, path: filePath };
7927
8033
  } catch (e) {
@@ -8009,7 +8115,7 @@ function handleGetProviderSettings(h, args) {
8009
8115
  }
8010
8116
  return { success: true, settings: allSettings, values: allValues };
8011
8117
  }
8012
- function handleSetProviderSetting(h, args) {
8118
+ async function handleSetProviderSetting(h, args) {
8013
8119
  const loader = h.ctx.providerLoader;
8014
8120
  const { providerType, key, value } = args || {};
8015
8121
  if (!providerType || !key || value === void 0) {
@@ -8022,6 +8128,7 @@ function handleSetProviderSetting(h, args) {
8022
8128
  const updated = h.ctx.instanceManager.updateInstanceSettings(providerType, allSettings);
8023
8129
  LOG.info("Command", `[set_provider_setting] ${providerType}.${key}=${JSON.stringify(value)} \u2192 ${updated} instance(s) updated`);
8024
8130
  }
8131
+ await h.ctx.onProviderSettingChanged?.(providerType, key, value);
8025
8132
  return { success: true, providerType, key, value };
8026
8133
  }
8027
8134
  return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
@@ -8059,10 +8166,10 @@ function getCliScriptCommand(payload) {
8059
8166
  }
8060
8167
  const command = payload.command;
8061
8168
  if (!command || typeof command !== "object") return null;
8062
- if (command.type !== "send_message") return null;
8169
+ if (command.type !== "send_message" && command.type !== "pty_write") return null;
8063
8170
  const text = typeof command.text === "string" ? command.text.trim() : typeof command.message === "string" ? command.message.trim() : "";
8064
8171
  if (!text) return null;
8065
- return { type: "send_message", text };
8172
+ return { type: command.type, text };
8066
8173
  }
8067
8174
  function applyProviderPatch(h, args, payload) {
8068
8175
  if (!payload || typeof payload !== "object") return;
@@ -8103,6 +8210,8 @@ async function executeProviderScript(h, args, scriptName) {
8103
8210
  const cliCommand = getCliScriptCommand(parsed.payload);
8104
8211
  if (cliCommand?.type === "send_message" && cliCommand.text) {
8105
8212
  await adapter.sendMessage(cliCommand.text);
8213
+ } else if (cliCommand?.type === "pty_write" && cliCommand.text && adapter.writeRaw) {
8214
+ adapter.writeRaw(cliCommand.text + "\r");
8106
8215
  }
8107
8216
  applyProviderPatch(h, args, parsed.payload);
8108
8217
  return { success: true, ...parsed.payload && typeof parsed.payload === "object" ? parsed.payload : { result: parsed.payload } };
@@ -8468,9 +8577,26 @@ var DaemonCommandHandler = class {
8468
8577
  if (provider?.scripts) {
8469
8578
  const fn = provider.scripts[scriptName];
8470
8579
  if (typeof fn === "function") {
8471
- const firstVal = params ? Object.values(params)[0] : void 0;
8472
- const script = firstVal ? fn(firstVal) : fn();
8473
- if (script) return script;
8580
+ if (params && Object.keys(params).length > 0) {
8581
+ const firstVal = Object.values(params)[0];
8582
+ if (scriptName === "sendMessage" && typeof firstVal === "string") {
8583
+ const legacyScript = fn(firstVal);
8584
+ if (legacyScript) return legacyScript;
8585
+ }
8586
+ const script = fn(params);
8587
+ if (script) {
8588
+ const likelyLegacyObjectLeak = typeof script === "string" && script.includes("[object Object]") && typeof firstVal === "string";
8589
+ if (!likelyLegacyObjectLeak) return script;
8590
+ }
8591
+ if (firstVal !== void 0) {
8592
+ const legacyScript = fn(firstVal);
8593
+ if (legacyScript) return legacyScript;
8594
+ }
8595
+ if (script) return script;
8596
+ } else {
8597
+ const script = fn();
8598
+ if (script) return script;
8599
+ }
8474
8600
  }
8475
8601
  }
8476
8602
  return null;
@@ -8764,7 +8890,7 @@ var DaemonCommandHandler = class {
8764
8890
  try {
8765
8891
  const http3 = await import("http");
8766
8892
  const postData = JSON.stringify(body);
8767
- const result = await new Promise((resolve9, reject) => {
8893
+ const result = await new Promise((resolve12, reject) => {
8768
8894
  const req = http3.request({
8769
8895
  hostname: "127.0.0.1",
8770
8896
  port: 19280,
@@ -8776,9 +8902,9 @@ var DaemonCommandHandler = class {
8776
8902
  res.on("data", (chunk) => data += chunk);
8777
8903
  res.on("end", () => {
8778
8904
  try {
8779
- resolve9(JSON.parse(data));
8905
+ resolve12(JSON.parse(data));
8780
8906
  } catch {
8781
- resolve9({ raw: data });
8907
+ resolve12({ raw: data });
8782
8908
  }
8783
8909
  });
8784
8910
  });
@@ -8796,15 +8922,15 @@ var DaemonCommandHandler = class {
8796
8922
  if (!providerType) return { success: false, error: "providerType required" };
8797
8923
  try {
8798
8924
  const http3 = await import("http");
8799
- const result = await new Promise((resolve9, reject) => {
8925
+ const result = await new Promise((resolve12, reject) => {
8800
8926
  http3.get(`http://127.0.0.1:19280/api/providers/${providerType}/${endpoint}`, (res) => {
8801
8927
  let data = "";
8802
8928
  res.on("data", (chunk) => data += chunk);
8803
8929
  res.on("end", () => {
8804
8930
  try {
8805
- resolve9(JSON.parse(data));
8931
+ resolve12(JSON.parse(data));
8806
8932
  } catch {
8807
- resolve9({ raw: data });
8933
+ resolve12({ raw: data });
8808
8934
  }
8809
8935
  });
8810
8936
  }).on("error", reject);
@@ -8818,7 +8944,7 @@ var DaemonCommandHandler = class {
8818
8944
  try {
8819
8945
  const http3 = await import("http");
8820
8946
  const postData = JSON.stringify(args || {});
8821
- const result = await new Promise((resolve9, reject) => {
8947
+ const result = await new Promise((resolve12, reject) => {
8822
8948
  const req = http3.request({
8823
8949
  hostname: "127.0.0.1",
8824
8950
  port: 19280,
@@ -8830,9 +8956,9 @@ var DaemonCommandHandler = class {
8830
8956
  res.on("data", (chunk) => data += chunk);
8831
8957
  res.on("end", () => {
8832
8958
  try {
8833
- resolve9(JSON.parse(data));
8959
+ resolve12(JSON.parse(data));
8834
8960
  } catch {
8835
- resolve9({ raw: data });
8961
+ resolve12({ raw: data });
8836
8962
  }
8837
8963
  });
8838
8964
  });
@@ -8850,7 +8976,7 @@ var DaemonCommandHandler = class {
8850
8976
  // src/commands/cli-manager.ts
8851
8977
  init_provider_cli_adapter();
8852
8978
  import * as os10 from "os";
8853
- import * as path9 from "path";
8979
+ import * as path11 from "path";
8854
8980
  import * as crypto4 from "crypto";
8855
8981
  import chalk from "chalk";
8856
8982
  init_config();
@@ -8858,7 +8984,7 @@ init_config();
8858
8984
  // src/providers/cli-provider-instance.ts
8859
8985
  init_provider_cli_adapter();
8860
8986
  import * as os9 from "os";
8861
- import * as path8 from "path";
8987
+ import * as path10 from "path";
8862
8988
  import * as crypto3 from "crypto";
8863
8989
  import * as fs5 from "fs";
8864
8990
  import { createRequire } from "module";
@@ -8866,7 +8992,7 @@ init_logger();
8866
8992
  var CachedDatabaseSync = null;
8867
8993
  function getDatabaseSync() {
8868
8994
  if (CachedDatabaseSync) return CachedDatabaseSync;
8869
- const requireFn = typeof __require === "function" ? __require : createRequire(path8.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
8995
+ const requireFn = typeof __require === "function" ? __require : createRequire(path10.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
8870
8996
  const sqliteModule = requireFn(`node:${"sqlite"}`);
8871
8997
  CachedDatabaseSync = sqliteModule.DatabaseSync;
8872
8998
  if (!CachedDatabaseSync) {
@@ -8931,6 +9057,7 @@ var CliProviderInstance = class {
8931
9057
  this.detectStatusTransition();
8932
9058
  });
8933
9059
  await this.adapter.spawn();
9060
+ this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
8934
9061
  if (this.providerSessionId) {
8935
9062
  const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
8936
9063
  if (restoredHistory.messages.length > 0) {
@@ -9025,6 +9152,7 @@ var CliProviderInstance = class {
9025
9152
  this.promoteProviderSessionId(parsedProviderSessionId);
9026
9153
  }
9027
9154
  const runtime = this.adapter.getRuntimeMetadata();
9155
+ this.maybeAppendRuntimeRecoveryMessage(runtime);
9028
9156
  const parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
9029
9157
  const controlValues = extractProviderControlValues(this.provider.controls, parsedStatus);
9030
9158
  if (controlValues) {
@@ -9351,6 +9479,28 @@ ${effect.notification.body || ""}`.trim();
9351
9479
  const pad = (value) => String(value).padStart(2, "0");
9352
9480
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
9353
9481
  }
9482
+ maybeAppendRuntimeRecoveryMessage(runtime) {
9483
+ if (!runtime?.restoredFromStorage || !runtime.runtimeId) return;
9484
+ const recoveryState = String(runtime.recoveryState || "").trim();
9485
+ if (!recoveryState) return;
9486
+ let content = "";
9487
+ if (recoveryState === "auto_resumed") {
9488
+ content = "Session host restored this CLI after restart and reattached it from a saved snapshot.";
9489
+ } else if (recoveryState === "resume_failed") {
9490
+ const errorSuffix = runtime.recoveryError ? ` Resume failed: ${runtime.recoveryError}` : "";
9491
+ content = `Session host found this CLI after restart, but automatic resume failed.${errorSuffix}`;
9492
+ } else if (recoveryState === "host_restart_interrupted") {
9493
+ content = "Session host found this CLI in interrupted state after restart and is attempting to resume it.";
9494
+ } else if (recoveryState === "orphan_snapshot") {
9495
+ content = "Session host restored the last snapshot for this CLI, but the original runtime was not resumed automatically.";
9496
+ } else {
9497
+ content = `Session host restored this CLI after restart (${recoveryState}).`;
9498
+ }
9499
+ this.appendRuntimeSystemMessage(
9500
+ content,
9501
+ `runtime_recovery:${runtime.runtimeId}:${recoveryState}`
9502
+ );
9503
+ }
9354
9504
  appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
9355
9505
  const normalizedContent = String(content || "").trim();
9356
9506
  if (!normalizedContent) return;
@@ -9676,8 +9826,9 @@ var AcpProviderInstance = class {
9676
9826
  async setConfigOption(category, value) {
9677
9827
  const opt = this.configOptions.find((c) => c.category === category);
9678
9828
  if (!opt) {
9679
- this.log.warn(`[${this.type}] No config option for category: ${category}`);
9680
- return;
9829
+ const message = `[${this.type}] No config option for category: ${category}`;
9830
+ this.log.warn(message);
9831
+ throw new Error(message);
9681
9832
  }
9682
9833
  if (this.useStaticConfig) {
9683
9834
  opt.currentValue = value;
@@ -9689,8 +9840,9 @@ var AcpProviderInstance = class {
9689
9840
  return;
9690
9841
  }
9691
9842
  if (!this.connection || !this.sessionId) {
9692
- this.log.warn(`[${this.type}] Cannot set config: no active connection/session`);
9693
- return;
9843
+ const message = `[${this.type}] Cannot set config: no active connection/session`;
9844
+ this.log.warn(message);
9845
+ throw new Error(message);
9694
9846
  }
9695
9847
  try {
9696
9848
  this.log.info(`[${this.type}] Sending session/set_config_option: configId=${opt.configId} value=${value} sessionId=${this.sessionId}`);
@@ -9704,7 +9856,9 @@ var AcpProviderInstance = class {
9704
9856
  if (result?.configOptions) this.parseConfigOptions(result.configOptions);
9705
9857
  this.log.info(`[${this.type}] Config ${category} set to: ${value} | response: ${JSON.stringify(result)?.slice(0, 300)}`);
9706
9858
  } catch (e) {
9707
- this.log.warn(`[${this.type}] set_config_option failed: ${e?.message}`);
9859
+ const message = e?.message || "Unknown ACP config error";
9860
+ this.log.warn(`[${this.type}] set_config_option failed: ${message}`);
9861
+ throw new Error(message);
9708
9862
  }
9709
9863
  }
9710
9864
  async setMode(modeId) {
@@ -9720,8 +9874,9 @@ var AcpProviderInstance = class {
9720
9874
  return;
9721
9875
  }
9722
9876
  if (!this.connection || !this.sessionId) {
9723
- this.log.warn(`[${this.type}] Cannot set mode: no active connection/session`);
9724
- return;
9877
+ const message = `[${this.type}] Cannot set mode: no active connection/session`;
9878
+ this.log.warn(message);
9879
+ throw new Error(message);
9725
9880
  }
9726
9881
  try {
9727
9882
  await this.connection.setSessionMode({
@@ -9731,7 +9886,9 @@ var AcpProviderInstance = class {
9731
9886
  this.currentMode = modeId;
9732
9887
  this.log.info(`[${this.type}] Mode set to: ${modeId}`);
9733
9888
  } catch (e) {
9734
- this.log.warn(`[${this.type}] set_mode failed: ${e?.message}`);
9889
+ const message = e?.message || "Unknown ACP mode error";
9890
+ this.log.warn(`[${this.type}] set_mode failed: ${message}`);
9891
+ throw new Error(message);
9735
9892
  }
9736
9893
  }
9737
9894
  /** Static config: kill process and restart with new args */
@@ -9779,7 +9936,7 @@ var AcpProviderInstance = class {
9779
9936
  if (!spawnConfig) {
9780
9937
  throw new Error(`[ACP:${this.type}] No spawn config defined`);
9781
9938
  }
9782
- const command = spawnConfig.command;
9939
+ const command = typeof this.settings.executablePath === "string" && this.settings.executablePath.trim() ? this.settings.executablePath.trim() : spawnConfig.command;
9783
9940
  let baseArgs = spawnConfig.args || [];
9784
9941
  if (this.provider.spawnArgBuilder && Object.keys(this.selectedConfig).length > 0) {
9785
9942
  baseArgs = this.provider.spawnArgBuilder(this.selectedConfig);
@@ -9895,13 +10052,13 @@ var AcpProviderInstance = class {
9895
10052
  }
9896
10053
  this.currentStatus = "waiting_approval";
9897
10054
  this.detectStatusTransition();
9898
- const approved = await new Promise((resolve9) => {
9899
- this.permissionResolvers.push(resolve9);
10055
+ const approved = await new Promise((resolve12) => {
10056
+ this.permissionResolvers.push(resolve12);
9900
10057
  setTimeout(() => {
9901
- const idx = this.permissionResolvers.indexOf(resolve9);
10058
+ const idx = this.permissionResolvers.indexOf(resolve12);
9902
10059
  if (idx >= 0) {
9903
10060
  this.permissionResolvers.splice(idx, 1);
9904
- resolve9(false);
10061
+ resolve12(false);
9905
10062
  }
9906
10063
  }, 3e5);
9907
10064
  });
@@ -10608,7 +10765,7 @@ var DaemonCliManager = class {
10608
10765
  async startSession(cliType, workingDir, cliArgs, initialModel, options) {
10609
10766
  const trimmed = (workingDir || "").trim();
10610
10767
  if (!trimmed) throw new Error("working directory required");
10611
- const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path9.resolve(trimmed);
10768
+ const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path11.resolve(trimmed);
10612
10769
  const normalizedType = this.providerLoader.resolveAlias(cliType);
10613
10770
  const provider = this.providerLoader.getByAlias(cliType);
10614
10771
  const key = crypto4.randomUUID();
@@ -10695,10 +10852,10 @@ ${installInfo}`
10695
10852
  if (!cliInfo) {
10696
10853
  const installHint = provider?.install || "";
10697
10854
  const displayName = provider?.displayName || provider?.name || cliType;
10698
- const spawnCmd = provider?.spawn?.command || cliType;
10855
+ const spawnCmd = this.providerLoader.getSpawnCommand(normalizedType, provider?.spawn?.command || cliType);
10699
10856
  throw new Error(
10700
10857
  `${displayName} is not installed.
10701
- Command '${spawnCmd}' not found on PATH.
10858
+ Command '${spawnCmd}' is not available.
10702
10859
  ` + (installHint ? `
10703
10860
  ${installHint}
10704
10861
  ` : "") + `
@@ -11058,16 +11215,17 @@ Run 'adhdev doctor' for detailed diagnostics.`
11058
11215
  import { execSync as execSync4, spawn as spawn2 } from "child_process";
11059
11216
  import * as net from "net";
11060
11217
  import * as os12 from "os";
11061
- import * as path11 from "path";
11218
+ import * as path13 from "path";
11062
11219
 
11063
11220
  // src/providers/provider-loader.ts
11064
11221
  import * as fs6 from "fs";
11065
- import * as path10 from "path";
11222
+ import * as path12 from "path";
11066
11223
  import * as os11 from "os";
11067
11224
  import * as chokidar from "chokidar";
11068
11225
  init_logger();
11069
11226
  var ProviderLoader = class _ProviderLoader {
11070
11227
  providers = /* @__PURE__ */ new Map();
11228
+ providerAvailability = /* @__PURE__ */ new Map();
11071
11229
  userDir;
11072
11230
  upstreamDir;
11073
11231
  disableUpstream;
@@ -11083,12 +11241,12 @@ var ProviderLoader = class _ProviderLoader {
11083
11241
  static META_FILE = ".meta.json";
11084
11242
  constructor(options) {
11085
11243
  this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
11086
- const defaultProvidersDir = path10.join(os11.homedir(), ".adhdev", "providers");
11244
+ const defaultProvidersDir = path12.join(os11.homedir(), ".adhdev", "providers");
11087
11245
  if (options?.userDir) {
11088
11246
  this.userDir = options.userDir;
11089
11247
  this.log(`Config 'providerDir' applied: ${this.userDir}`);
11090
11248
  } else {
11091
- const localRepoPath = path10.resolve(__dirname, "../../../../../adhdev-providers");
11249
+ const localRepoPath = path12.resolve(__dirname, "../../../../../adhdev-providers");
11092
11250
  if (fs6.existsSync(localRepoPath)) {
11093
11251
  this.userDir = localRepoPath;
11094
11252
  this.log(`Auto-detected local public repository: ${this.userDir} (Dev workspace speedup)`);
@@ -11097,7 +11255,7 @@ var ProviderLoader = class _ProviderLoader {
11097
11255
  this.log(`Using default user providers directory: ${this.userDir}`);
11098
11256
  }
11099
11257
  }
11100
- this.upstreamDir = path10.join(defaultProvidersDir, ".upstream");
11258
+ this.upstreamDir = path12.join(defaultProvidersDir, ".upstream");
11101
11259
  this.disableUpstream = options?.disableUpstream ?? false;
11102
11260
  }
11103
11261
  log(msg) {
@@ -11127,7 +11285,7 @@ var ProviderLoader = class _ProviderLoader {
11127
11285
  * Canonical provider directory shape for a given root.
11128
11286
  */
11129
11287
  getProviderDir(root, category, type) {
11130
- return path10.join(root, category, type);
11288
+ return path12.join(root, category, type);
11131
11289
  }
11132
11290
  /**
11133
11291
  * Canonical user override directory for a provider.
@@ -11154,7 +11312,7 @@ var ProviderLoader = class _ProviderLoader {
11154
11312
  resolveProviderFile(type, ...segments) {
11155
11313
  const dir = this.findProviderDirInternal(type);
11156
11314
  if (!dir) return null;
11157
- return path10.join(dir, ...segments);
11315
+ return path12.join(dir, ...segments);
11158
11316
  }
11159
11317
  /**
11160
11318
  * Load all providers (3-tier priority)
@@ -11165,6 +11323,7 @@ var ProviderLoader = class _ProviderLoader {
11165
11323
  */
11166
11324
  loadAll() {
11167
11325
  this.providers.clear();
11326
+ this.providerAvailability.clear();
11168
11327
  let upstreamCount = 0;
11169
11328
  if (!this.disableUpstream && fs6.existsSync(this.upstreamDir)) {
11170
11329
  upstreamCount = this.loadDir(this.upstreamDir);
@@ -11192,7 +11351,7 @@ var ProviderLoader = class _ProviderLoader {
11192
11351
  if (!fs6.existsSync(this.upstreamDir)) return false;
11193
11352
  try {
11194
11353
  return fs6.readdirSync(this.upstreamDir).some(
11195
- (d) => fs6.statSync(path10.join(this.upstreamDir, d)).isDirectory()
11354
+ (d) => fs6.statSync(path12.join(this.upstreamDir, d)).isDirectory()
11196
11355
  );
11197
11356
  } catch {
11198
11357
  return false;
@@ -11235,11 +11394,12 @@ var ProviderLoader = class _ProviderLoader {
11235
11394
  if ((p.category === "cli" || p.category === "acp") && p.spawn?.command) {
11236
11395
  const verCmdConfig = p.versionCommand;
11237
11396
  const versionCommand = typeof verCmdConfig === "object" && verCmdConfig !== null ? verCmdConfig[process.platform] : verCmdConfig;
11397
+ const command = this.getSpawnCommand(p.type, p.spawn.command);
11238
11398
  result.push({
11239
11399
  id: p.type,
11240
11400
  displayName: p.displayName || p.name,
11241
11401
  icon: p.icon || "\u{1F527}",
11242
- command: p.spawn.command,
11402
+ command,
11243
11403
  category: p.category,
11244
11404
  ...typeof versionCommand === "string" && versionCommand.trim() ? { versionCommand: versionCommand.trim() } : {}
11245
11405
  });
@@ -11368,6 +11528,71 @@ var ProviderLoader = class _ProviderLoader {
11368
11528
  getAvailableIdeTypes() {
11369
11529
  return [...this.providers.values()].filter((p) => p.category === "ide" && p.cdpPorts).map((p) => p.type);
11370
11530
  }
11531
+ getSpawnCommand(type, fallback) {
11532
+ const override = this.getOptionalStringSetting(type, "executablePath");
11533
+ if (override) return override;
11534
+ return fallback || this.providers.get(type)?.spawn?.command || type;
11535
+ }
11536
+ getIdeCliCommand(type, fallback) {
11537
+ const override = this.getOptionalStringSetting(type, "cliPathOverride");
11538
+ if (override) return override;
11539
+ return fallback || this.providers.get(type)?.cli || null;
11540
+ }
11541
+ getIdePathCandidates(type, fallback) {
11542
+ const override = this.getOptionalStringSetting(type, "appPathOverride");
11543
+ if (override) return [override];
11544
+ if (fallback && fallback.length > 0) return fallback;
11545
+ const osPaths = this.providers.get(type)?.paths?.[process.platform];
11546
+ return Array.isArray(osPaths) ? [...osPaths] : [];
11547
+ }
11548
+ setProviderAvailability(type, state) {
11549
+ this.providerAvailability.set(type, {
11550
+ installed: !!state.installed,
11551
+ detectedPath: state.detectedPath ?? null
11552
+ });
11553
+ }
11554
+ setCliDetectionResults(results, replace = true) {
11555
+ if (replace) {
11556
+ for (const provider of this.providers.values()) {
11557
+ if (provider.category === "cli" || provider.category === "acp") {
11558
+ this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
11559
+ }
11560
+ }
11561
+ }
11562
+ for (const result of results) {
11563
+ this.setProviderAvailability(result.id, {
11564
+ installed: !!result.installed,
11565
+ detectedPath: result.path || null
11566
+ });
11567
+ }
11568
+ }
11569
+ setIdeDetectionResults(results, replace = true) {
11570
+ if (replace) {
11571
+ for (const provider of this.providers.values()) {
11572
+ if (provider.category === "ide") {
11573
+ this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
11574
+ }
11575
+ }
11576
+ }
11577
+ for (const result of results) {
11578
+ this.setProviderAvailability(result.id, {
11579
+ installed: !!result.installed,
11580
+ detectedPath: result.cliCommand || result.path || null
11581
+ });
11582
+ }
11583
+ }
11584
+ getAvailableProviderInfos() {
11585
+ return this.getAll().map((provider) => {
11586
+ const availability = this.providerAvailability.get(provider.type);
11587
+ return {
11588
+ ...provider,
11589
+ ...availability ? {
11590
+ installed: availability.installed,
11591
+ detectedPath: availability.detectedPath
11592
+ } : {}
11593
+ };
11594
+ });
11595
+ }
11371
11596
  /**
11372
11597
  * Register IDE providers to core/detector registry
11373
11598
  * → Enables detectIDEs() to detect provider.js-based IDEs
@@ -11442,8 +11667,8 @@ var ProviderLoader = class _ProviderLoader {
11442
11667
  resolved._resolvedScriptDir = entry.scriptDir;
11443
11668
  resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
11444
11669
  if (providerDir) {
11445
- const fullDir = path10.join(providerDir, entry.scriptDir);
11446
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
11670
+ const fullDir = path12.join(providerDir, entry.scriptDir);
11671
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11447
11672
  }
11448
11673
  matched = true;
11449
11674
  }
@@ -11458,8 +11683,8 @@ var ProviderLoader = class _ProviderLoader {
11458
11683
  resolved._resolvedScriptDir = base.defaultScriptDir;
11459
11684
  resolved._resolvedScriptsSource = "defaultScriptDir:version_miss";
11460
11685
  if (providerDir) {
11461
- const fullDir = path10.join(providerDir, base.defaultScriptDir);
11462
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
11686
+ const fullDir = path12.join(providerDir, base.defaultScriptDir);
11687
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11463
11688
  }
11464
11689
  }
11465
11690
  resolved._versionWarning = `Version ${currentVersion} not in compatibility matrix. Using default scripts.`;
@@ -11476,8 +11701,8 @@ var ProviderLoader = class _ProviderLoader {
11476
11701
  resolved._resolvedScriptDir = dirOverride;
11477
11702
  resolved._resolvedScriptsSource = `versions:${range}`;
11478
11703
  if (providerDir) {
11479
- const fullDir = path10.join(providerDir, dirOverride);
11480
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
11704
+ const fullDir = path12.join(providerDir, dirOverride);
11705
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11481
11706
  }
11482
11707
  }
11483
11708
  } else if (override.scripts) {
@@ -11493,8 +11718,8 @@ var ProviderLoader = class _ProviderLoader {
11493
11718
  resolved._resolvedScriptDir = base.defaultScriptDir;
11494
11719
  resolved._resolvedScriptsSource = "defaultScriptDir:no_version";
11495
11720
  if (providerDir) {
11496
- const fullDir = path10.join(providerDir, base.defaultScriptDir);
11497
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
11721
+ const fullDir = path12.join(providerDir, base.defaultScriptDir);
11722
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11498
11723
  }
11499
11724
  }
11500
11725
  }
@@ -11519,14 +11744,14 @@ var ProviderLoader = class _ProviderLoader {
11519
11744
  this.log(` [loadScriptsFromDir] ${type}: providerDir not found`);
11520
11745
  return null;
11521
11746
  }
11522
- const dir = path10.join(providerDir, scriptDir);
11747
+ const dir = path12.join(providerDir, scriptDir);
11523
11748
  if (!fs6.existsSync(dir)) {
11524
11749
  this.log(` [loadScriptsFromDir] ${type}: dir not found: ${dir}`);
11525
11750
  return null;
11526
11751
  }
11527
11752
  const cached = this.scriptsCache.get(dir);
11528
11753
  if (cached) return cached;
11529
- const scriptsJs = path10.join(dir, "scripts.js");
11754
+ const scriptsJs = path12.join(dir, "scripts.js");
11530
11755
  if (fs6.existsSync(scriptsJs)) {
11531
11756
  try {
11532
11757
  delete __require.cache[__require.resolve(scriptsJs)];
@@ -11568,7 +11793,7 @@ var ProviderLoader = class _ProviderLoader {
11568
11793
  return;
11569
11794
  }
11570
11795
  if (filePath.endsWith(".js") || filePath.endsWith(".json")) {
11571
- this.log(`File changed: ${path10.basename(filePath)}, reloading...`);
11796
+ this.log(`File changed: ${path12.basename(filePath)}, reloading...`);
11572
11797
  this.reload();
11573
11798
  }
11574
11799
  };
@@ -11623,7 +11848,7 @@ var ProviderLoader = class _ProviderLoader {
11623
11848
  }
11624
11849
  const https = __require("https");
11625
11850
  const { execSync: execSync7 } = __require("child_process");
11626
- const metaPath = path10.join(this.upstreamDir, _ProviderLoader.META_FILE);
11851
+ const metaPath = path12.join(this.upstreamDir, _ProviderLoader.META_FILE);
11627
11852
  let prevEtag = "";
11628
11853
  let prevTimestamp = 0;
11629
11854
  try {
@@ -11640,7 +11865,7 @@ var ProviderLoader = class _ProviderLoader {
11640
11865
  return { updated: false };
11641
11866
  }
11642
11867
  try {
11643
- const etag = await new Promise((resolve9, reject) => {
11868
+ const etag = await new Promise((resolve12, reject) => {
11644
11869
  const options = {
11645
11870
  method: "HEAD",
11646
11871
  hostname: "github.com",
@@ -11658,7 +11883,7 @@ var ProviderLoader = class _ProviderLoader {
11658
11883
  headers: { "User-Agent": "adhdev-launcher" },
11659
11884
  timeout: 1e4
11660
11885
  }, (res2) => {
11661
- resolve9(res2.headers.etag || res2.headers["last-modified"] || "");
11886
+ resolve12(res2.headers.etag || res2.headers["last-modified"] || "");
11662
11887
  });
11663
11888
  req2.on("error", reject);
11664
11889
  req2.on("timeout", () => {
@@ -11667,7 +11892,7 @@ var ProviderLoader = class _ProviderLoader {
11667
11892
  });
11668
11893
  req2.end();
11669
11894
  } else {
11670
- resolve9(res.headers.etag || res.headers["last-modified"] || "");
11895
+ resolve12(res.headers.etag || res.headers["last-modified"] || "");
11671
11896
  }
11672
11897
  });
11673
11898
  req.on("error", reject);
@@ -11683,17 +11908,17 @@ var ProviderLoader = class _ProviderLoader {
11683
11908
  return { updated: false };
11684
11909
  }
11685
11910
  this.log("Downloading latest providers from GitHub...");
11686
- const tmpTar = path10.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
11687
- const tmpExtract = path10.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
11911
+ const tmpTar = path12.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
11912
+ const tmpExtract = path12.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
11688
11913
  await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
11689
11914
  fs6.mkdirSync(tmpExtract, { recursive: true });
11690
11915
  execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
11691
11916
  const extracted = fs6.readdirSync(tmpExtract);
11692
11917
  const rootDir = extracted.find(
11693
- (d) => fs6.statSync(path10.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
11918
+ (d) => fs6.statSync(path12.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
11694
11919
  );
11695
11920
  if (!rootDir) throw new Error("Unexpected tarball structure");
11696
- const sourceDir = path10.join(tmpExtract, rootDir);
11921
+ const sourceDir = path12.join(tmpExtract, rootDir);
11697
11922
  const backupDir = this.upstreamDir + ".bak";
11698
11923
  if (fs6.existsSync(this.upstreamDir)) {
11699
11924
  if (fs6.existsSync(backupDir)) fs6.rmSync(backupDir, { recursive: true, force: true });
@@ -11731,7 +11956,7 @@ var ProviderLoader = class _ProviderLoader {
11731
11956
  downloadFile(url, destPath) {
11732
11957
  const https = __require("https");
11733
11958
  const http3 = __require("http");
11734
- return new Promise((resolve9, reject) => {
11959
+ return new Promise((resolve12, reject) => {
11735
11960
  const doRequest = (reqUrl, redirectCount = 0) => {
11736
11961
  if (redirectCount > 5) {
11737
11962
  reject(new Error("Too many redirects"));
@@ -11751,7 +11976,7 @@ var ProviderLoader = class _ProviderLoader {
11751
11976
  res.pipe(ws);
11752
11977
  ws.on("finish", () => {
11753
11978
  ws.close();
11754
- resolve9();
11979
+ resolve12();
11755
11980
  });
11756
11981
  ws.on("error", reject);
11757
11982
  });
@@ -11768,8 +11993,8 @@ var ProviderLoader = class _ProviderLoader {
11768
11993
  copyDirRecursive(src, dest) {
11769
11994
  fs6.mkdirSync(dest, { recursive: true });
11770
11995
  for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
11771
- const srcPath = path10.join(src, entry.name);
11772
- const destPath = path10.join(dest, entry.name);
11996
+ const srcPath = path12.join(src, entry.name);
11997
+ const destPath = path12.join(dest, entry.name);
11773
11998
  if (entry.isDirectory()) {
11774
11999
  this.copyDirRecursive(srcPath, destPath);
11775
12000
  } else {
@@ -11780,7 +12005,7 @@ var ProviderLoader = class _ProviderLoader {
11780
12005
  /** .meta.json save */
11781
12006
  writeMeta(metaPath, etag, timestamp) {
11782
12007
  try {
11783
- fs6.mkdirSync(path10.dirname(metaPath), { recursive: true });
12008
+ fs6.mkdirSync(path12.dirname(metaPath), { recursive: true });
11784
12009
  fs6.writeFileSync(metaPath, JSON.stringify({
11785
12010
  etag,
11786
12011
  timestamp,
@@ -11797,7 +12022,7 @@ var ProviderLoader = class _ProviderLoader {
11797
12022
  const scan = (d) => {
11798
12023
  try {
11799
12024
  for (const entry of fs6.readdirSync(d, { withFileTypes: true })) {
11800
- if (entry.isDirectory()) scan(path10.join(d, entry.name));
12025
+ if (entry.isDirectory()) scan(path12.join(d, entry.name));
11801
12026
  else if (entry.name === "provider.json") count++;
11802
12027
  }
11803
12028
  } catch {
@@ -11811,9 +12036,8 @@ var ProviderLoader = class _ProviderLoader {
11811
12036
  * Get public settings schema for a provider (for dashboard UI rendering)
11812
12037
  */
11813
12038
  getPublicSettings(type) {
11814
- const provider = this.providers.get(type);
11815
- if (!provider?.settings) return [];
11816
- return Object.entries(provider.settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
12039
+ const settings = this.getSettingsSchema(type);
12040
+ return Object.entries(settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
11817
12041
  }
11818
12042
  /**
11819
12043
  * Get public settings schema for all providers
@@ -11830,8 +12054,7 @@ var ProviderLoader = class _ProviderLoader {
11830
12054
  * Resolved setting value for a provider (default + user override)
11831
12055
  */
11832
12056
  getSettingValue(type, key) {
11833
- const provider = this.providers.get(type);
11834
- const schemaDef = provider?.settings?.[key];
12057
+ const schemaDef = this.getSettingsSchema(type)[key];
11835
12058
  const defaultVal = schemaDef ? schemaDef.default : void 0;
11836
12059
  try {
11837
12060
  const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
@@ -11846,10 +12069,9 @@ var ProviderLoader = class _ProviderLoader {
11846
12069
  * All resolved settings for a provider (default + user override)
11847
12070
  */
11848
12071
  getSettings(type) {
11849
- const provider = this.providers.get(type);
11850
- if (!provider?.settings) return {};
12072
+ const settings = this.getSettingsSchema(type);
11851
12073
  const result = {};
11852
- for (const [key, def] of Object.entries(provider.settings)) {
12074
+ for (const [key] of Object.entries(settings)) {
11853
12075
  result[key] = this.getSettingValue(type, key);
11854
12076
  }
11855
12077
  return result;
@@ -11858,11 +12080,11 @@ var ProviderLoader = class _ProviderLoader {
11858
12080
  * Save provider setting value (writes to config.json)
11859
12081
  */
11860
12082
  setSetting(type, key, value) {
11861
- const provider = this.providers.get(type);
11862
- const schemaDef = provider?.settings?.[key];
12083
+ const schemaDef = this.getSettingsSchema(type)[key];
11863
12084
  if (!schemaDef) return false;
11864
12085
  if (!schemaDef.public) return false;
11865
12086
  if (schemaDef.type === "boolean" && typeof value !== "boolean") return false;
12087
+ if (schemaDef.type === "string" && typeof value !== "string") return false;
11866
12088
  if (schemaDef.type === "number") {
11867
12089
  if (typeof value !== "number") return false;
11868
12090
  if (schemaDef.min !== void 0 && value < schemaDef.min) return false;
@@ -11883,6 +12105,53 @@ var ProviderLoader = class _ProviderLoader {
11883
12105
  return false;
11884
12106
  }
11885
12107
  }
12108
+ getOptionalStringSetting(type, key) {
12109
+ const value = this.getSettingValue(type, key);
12110
+ if (typeof value !== "string") return null;
12111
+ const trimmed = value.trim();
12112
+ return trimmed ? trimmed : null;
12113
+ }
12114
+ getSettingsSchema(type) {
12115
+ const provider = this.providers.get(type);
12116
+ if (!provider) return {};
12117
+ return {
12118
+ ...this.getSyntheticSettings(type, provider),
12119
+ ...provider.settings || {}
12120
+ };
12121
+ }
12122
+ getSyntheticSettings(type, provider) {
12123
+ const result = {};
12124
+ if ((provider.category === "cli" || provider.category === "acp") && provider.spawn?.command && !provider.settings?.executablePath) {
12125
+ result.executablePath = {
12126
+ type: "string",
12127
+ default: "",
12128
+ public: true,
12129
+ label: "Executable path",
12130
+ description: "Optional absolute path for this provider binary. Leave blank to use the default PATH lookup."
12131
+ };
12132
+ }
12133
+ if (provider.category === "ide") {
12134
+ if (provider.cli && !provider.settings?.cliPathOverride) {
12135
+ result.cliPathOverride = {
12136
+ type: "string",
12137
+ default: "",
12138
+ public: true,
12139
+ label: "CLI path override",
12140
+ description: "Optional absolute path for the IDE CLI launcher. Leave blank to use the detected default."
12141
+ };
12142
+ }
12143
+ if (provider.paths && !provider.settings?.appPathOverride) {
12144
+ result.appPathOverride = {
12145
+ type: "string",
12146
+ default: "",
12147
+ public: true,
12148
+ label: "App path override",
12149
+ description: "Optional absolute path for the IDE app bundle or executable. Leave blank to use the default install locations."
12150
+ };
12151
+ }
12152
+ }
12153
+ return result;
12154
+ }
11886
12155
  // ─── Private ───────────────────────────────────
11887
12156
  /**
11888
12157
  * Find the on-disk directory for a provider by type.
@@ -11896,17 +12165,17 @@ var ProviderLoader = class _ProviderLoader {
11896
12165
  for (const root of searchRoots) {
11897
12166
  if (!fs6.existsSync(root)) continue;
11898
12167
  const candidate = this.getProviderDir(root, cat, type);
11899
- if (fs6.existsSync(path10.join(candidate, "provider.json"))) return candidate;
11900
- const catDir = path10.join(root, cat);
12168
+ if (fs6.existsSync(path12.join(candidate, "provider.json"))) return candidate;
12169
+ const catDir = path12.join(root, cat);
11901
12170
  if (fs6.existsSync(catDir)) {
11902
12171
  try {
11903
12172
  for (const entry of fs6.readdirSync(catDir, { withFileTypes: true })) {
11904
12173
  if (!entry.isDirectory()) continue;
11905
- const jsonPath = path10.join(catDir, entry.name, "provider.json");
12174
+ const jsonPath = path12.join(catDir, entry.name, "provider.json");
11906
12175
  if (fs6.existsSync(jsonPath)) {
11907
12176
  try {
11908
12177
  const data = JSON.parse(fs6.readFileSync(jsonPath, "utf-8"));
11909
- if (data.type === type) return path10.join(catDir, entry.name);
12178
+ if (data.type === type) return path12.join(catDir, entry.name);
11910
12179
  } catch {
11911
12180
  }
11912
12181
  }
@@ -11923,7 +12192,7 @@ var ProviderLoader = class _ProviderLoader {
11923
12192
  * (template substitution is NOT applied here — scripts.js handles that)
11924
12193
  */
11925
12194
  buildScriptWrappersFromDir(dir) {
11926
- const scriptsJs = path10.join(dir, "scripts.js");
12195
+ const scriptsJs = path12.join(dir, "scripts.js");
11927
12196
  if (fs6.existsSync(scriptsJs)) {
11928
12197
  try {
11929
12198
  delete __require.cache[__require.resolve(scriptsJs)];
@@ -11937,7 +12206,7 @@ var ProviderLoader = class _ProviderLoader {
11937
12206
  for (const file of fs6.readdirSync(dir)) {
11938
12207
  if (!file.endsWith(".js")) continue;
11939
12208
  const scriptName = toCamel(file.replace(".js", ""));
11940
- const filePath = path10.join(dir, file);
12209
+ const filePath = path12.join(dir, file);
11941
12210
  result[scriptName] = (...args) => {
11942
12211
  try {
11943
12212
  let content = fs6.readFileSync(filePath, "utf-8");
@@ -11997,7 +12266,7 @@ var ProviderLoader = class _ProviderLoader {
11997
12266
  }
11998
12267
  const hasJson = entries.some((e) => e.name === "provider.json");
11999
12268
  if (hasJson) {
12000
- const jsonPath = path10.join(d, "provider.json");
12269
+ const jsonPath = path12.join(d, "provider.json");
12001
12270
  try {
12002
12271
  const raw = fs6.readFileSync(jsonPath, "utf-8");
12003
12272
  const mod = JSON.parse(raw);
@@ -12010,7 +12279,7 @@ var ProviderLoader = class _ProviderLoader {
12010
12279
  delete mod.extensionIdPattern_flags;
12011
12280
  }
12012
12281
  const hasCompatibility = Array.isArray(mod.compatibility);
12013
- const scriptsPath = path10.join(d, "scripts.js");
12282
+ const scriptsPath = path12.join(d, "scripts.js");
12014
12283
  if (!hasCompatibility && fs6.existsSync(scriptsPath)) {
12015
12284
  try {
12016
12285
  delete __require.cache[__require.resolve(scriptsPath)];
@@ -12036,7 +12305,7 @@ var ProviderLoader = class _ProviderLoader {
12036
12305
  if (!entry.isDirectory()) continue;
12037
12306
  if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
12038
12307
  if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
12039
- scan(path10.join(d, entry.name));
12308
+ scan(path12.join(d, entry.name));
12040
12309
  }
12041
12310
  }
12042
12311
  };
@@ -12132,17 +12401,17 @@ async function findFreePort(ports) {
12132
12401
  throw new Error("No free port found");
12133
12402
  }
12134
12403
  function checkPortFree(port) {
12135
- return new Promise((resolve9) => {
12404
+ return new Promise((resolve12) => {
12136
12405
  const server = net.createServer();
12137
12406
  server.unref();
12138
- server.on("error", () => resolve9(false));
12407
+ server.on("error", () => resolve12(false));
12139
12408
  server.listen(port, "127.0.0.1", () => {
12140
- server.close(() => resolve9(true));
12409
+ server.close(() => resolve12(true));
12141
12410
  });
12142
12411
  });
12143
12412
  }
12144
12413
  async function isCdpActive(port) {
12145
- return new Promise((resolve9) => {
12414
+ return new Promise((resolve12) => {
12146
12415
  const req = __require("http").get(`http://127.0.0.1:${port}/json/version`, {
12147
12416
  timeout: 2e3
12148
12417
  }, (res) => {
@@ -12151,16 +12420,16 @@ async function isCdpActive(port) {
12151
12420
  res.on("end", () => {
12152
12421
  try {
12153
12422
  const info = JSON.parse(data);
12154
- resolve9(!!info["WebKit-Version"] || !!info["Browser"]);
12423
+ resolve12(!!info["WebKit-Version"] || !!info["Browser"]);
12155
12424
  } catch {
12156
- resolve9(false);
12425
+ resolve12(false);
12157
12426
  }
12158
12427
  });
12159
12428
  });
12160
- req.on("error", () => resolve9(false));
12429
+ req.on("error", () => resolve12(false));
12161
12430
  req.on("timeout", () => {
12162
12431
  req.destroy();
12163
- resolve9(false);
12432
+ resolve12(false);
12164
12433
  });
12165
12434
  });
12166
12435
  }
@@ -12294,8 +12563,8 @@ function detectCurrentWorkspace(ideId) {
12294
12563
  const appNameMap = getMacAppIdentifiers();
12295
12564
  const appName = appNameMap[ideId];
12296
12565
  if (appName) {
12297
- const storagePath = path11.join(
12298
- process.env.APPDATA || path11.join(os12.homedir(), "AppData", "Roaming"),
12566
+ const storagePath = path13.join(
12567
+ process.env.APPDATA || path13.join(os12.homedir(), "AppData", "Roaming"),
12299
12568
  appName,
12300
12569
  "storage.json"
12301
12570
  );
@@ -12319,7 +12588,7 @@ function detectCurrentWorkspace(ideId) {
12319
12588
  async function launchWithCdp(options = {}) {
12320
12589
  const platform9 = os12.platform();
12321
12590
  let targetIde;
12322
- const ides = await detectIDEs();
12591
+ const ides = await detectIDEs(getProviderLoader());
12323
12592
  if (options.ideId) {
12324
12593
  targetIde = ides.find((i) => i.id === options.ideId && i.installed);
12325
12594
  if (!targetIde) {
@@ -12473,9 +12742,9 @@ init_logger();
12473
12742
 
12474
12743
  // src/logging/command-log.ts
12475
12744
  import * as fs7 from "fs";
12476
- import * as path12 from "path";
12745
+ import * as path14 from "path";
12477
12746
  import * as os13 from "os";
12478
- 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");
12747
+ 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");
12479
12748
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
12480
12749
  var MAX_DAYS = 7;
12481
12750
  try {
@@ -12513,13 +12782,13 @@ function getDateStr2() {
12513
12782
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
12514
12783
  }
12515
12784
  var currentDate2 = getDateStr2();
12516
- var currentFile = path12.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
12785
+ var currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
12517
12786
  var writeCount2 = 0;
12518
12787
  function checkRotation() {
12519
12788
  const today = getDateStr2();
12520
12789
  if (today !== currentDate2) {
12521
12790
  currentDate2 = today;
12522
- currentFile = path12.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
12791
+ currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
12523
12792
  cleanOldFiles();
12524
12793
  }
12525
12794
  }
@@ -12533,7 +12802,7 @@ function cleanOldFiles() {
12533
12802
  const dateMatch = file.match(/commands-(\d{4}-\d{2}-\d{2})/);
12534
12803
  if (dateMatch && dateMatch[1] < cutoffStr) {
12535
12804
  try {
12536
- fs7.unlinkSync(path12.join(LOG_DIR2, file));
12805
+ fs7.unlinkSync(path14.join(LOG_DIR2, file));
12537
12806
  } catch {
12538
12807
  }
12539
12808
  }
@@ -12625,12 +12894,15 @@ function buildDetectedIdeInfos(detectedIdes, cdpManagers) {
12625
12894
  }));
12626
12895
  }
12627
12896
  function buildAvailableProviders(providerLoader) {
12628
- return providerLoader.getAll().map((provider) => ({
12897
+ const providers = providerLoader.getAvailableProviderInfos?.() || providerLoader.getAll();
12898
+ return providers.map((provider) => ({
12629
12899
  type: provider.type,
12630
12900
  name: provider.displayName || provider.type,
12631
12901
  displayName: provider.displayName || provider.type,
12632
12902
  icon: provider.icon || "\u{1F4BB}",
12633
- category: provider.category
12903
+ category: provider.category,
12904
+ ...provider.installed !== void 0 ? { installed: provider.installed } : {},
12905
+ ...provider.detectedPath !== void 0 ? { detectedPath: provider.detectedPath } : {}
12634
12906
  }));
12635
12907
  }
12636
12908
  function parseMessageTime(value) {
@@ -12644,17 +12916,17 @@ function parseMessageTime(value) {
12644
12916
  function getSessionMessageUpdatedAt(session) {
12645
12917
  const lastMessage = session.activeChat?.messages?.at?.(-1);
12646
12918
  if (!lastMessage) return 0;
12647
- return parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt) || 0;
12919
+ return parseMessageTime(lastMessage.receivedAt) || 0;
12648
12920
  }
12649
12921
  function getSessionCompletionMarker(session) {
12650
12922
  const lastMessage = session.activeChat?.messages?.at?.(-1);
12651
12923
  if (!lastMessage) return "";
12652
12924
  const role = typeof lastMessage.role === "string" ? lastMessage.role : "";
12653
- if (role === "user" || role === "human") return "";
12925
+ if (role === "user" || role === "human" || role === "system") return "";
12654
12926
  if (typeof lastMessage._turnKey === "string" && lastMessage._turnKey) return `turn:${lastMessage._turnKey}`;
12655
12927
  if (typeof lastMessage.id === "string" && lastMessage.id) return `id:${lastMessage.id}`;
12656
12928
  if (typeof lastMessage.index === "number" && Number.isFinite(lastMessage.index)) return `idx:${lastMessage.index}`;
12657
- const timestamp = parseMessageTime(lastMessage.timestamp) || parseMessageTime(lastMessage.receivedAt) || parseMessageTime(lastMessage.createdAt);
12929
+ const timestamp = parseMessageTime(lastMessage.receivedAt);
12658
12930
  return timestamp > 0 ? `ts:${timestamp}` : "";
12659
12931
  }
12660
12932
  function getSessionLastUsedAt(session) {
@@ -12671,7 +12943,7 @@ function getUnreadState(hasContentChange, status, lastUsedAt, lastSeenAt, lastRo
12671
12943
  if (status === "generating" || status === "starting") {
12672
12944
  return { unread: false, inboxBucket: "working" };
12673
12945
  }
12674
- const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human";
12946
+ const unread = completionMarker ? completionMarker !== seenCompletionMarker : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== "user" && lastRole !== "human" && lastRole !== "system";
12675
12947
  return { unread, inboxBucket: unread ? "task_complete" : "idle" };
12676
12948
  }
12677
12949
  function buildRecentLaunches(recentActivity) {
@@ -12758,13 +13030,13 @@ import { execFileSync } from "child_process";
12758
13030
  import { spawn as spawn3 } from "child_process";
12759
13031
  import * as fs8 from "fs";
12760
13032
  import * as os15 from "os";
12761
- import * as path13 from "path";
13033
+ import * as path15 from "path";
12762
13034
  var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
12763
13035
  function getUpgradeLogPath() {
12764
13036
  const home = os15.homedir();
12765
- const dir = path13.join(home, ".adhdev");
13037
+ const dir = path15.join(home, ".adhdev");
12766
13038
  fs8.mkdirSync(dir, { recursive: true });
12767
- return path13.join(dir, "daemon-upgrade.log");
13039
+ return path15.join(dir, "daemon-upgrade.log");
12768
13040
  }
12769
13041
  function appendUpgradeLog(message) {
12770
13042
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
@@ -12797,14 +13069,14 @@ async function waitForPidExit(pid, timeoutMs) {
12797
13069
  while (Date.now() - start < timeoutMs) {
12798
13070
  try {
12799
13071
  process.kill(pid, 0);
12800
- await new Promise((resolve9) => setTimeout(resolve9, 250));
13072
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
12801
13073
  } catch {
12802
13074
  return;
12803
13075
  }
12804
13076
  }
12805
13077
  }
12806
13078
  function stopSessionHostProcesses(appName) {
12807
- const pidFile = path13.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
13079
+ const pidFile = path15.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
12808
13080
  try {
12809
13081
  if (fs8.existsSync(pidFile)) {
12810
13082
  const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
@@ -12833,7 +13105,7 @@ function stopSessionHostProcesses(appName) {
12833
13105
  }
12834
13106
  }
12835
13107
  function removeDaemonPidFile() {
12836
- const pidFile = path13.join(os15.homedir(), ".adhdev", "daemon.pid");
13108
+ const pidFile = path15.join(os15.homedir(), ".adhdev", "daemon.pid");
12837
13109
  try {
12838
13110
  fs8.unlinkSync(pidFile);
12839
13111
  } catch {
@@ -12844,7 +13116,7 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
12844
13116
  const npmRoot = execFileSync(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
12845
13117
  if (!npmRoot) return;
12846
13118
  const npmPrefix = execFileSync(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
12847
- const binDir = process.platform === "win32" ? npmPrefix : path13.join(npmPrefix, "bin");
13119
+ const binDir = process.platform === "win32" ? npmPrefix : path15.join(npmPrefix, "bin");
12848
13120
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
12849
13121
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
12850
13122
  if (pkgName === "@adhdev/daemon-standalone") {
@@ -12852,25 +13124,25 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
12852
13124
  }
12853
13125
  if (pkgName.startsWith("@")) {
12854
13126
  const [scope, name] = pkgName.split("/");
12855
- const scopeDir = path13.join(npmRoot, scope);
13127
+ const scopeDir = path15.join(npmRoot, scope);
12856
13128
  if (!fs8.existsSync(scopeDir)) return;
12857
13129
  for (const entry of fs8.readdirSync(scopeDir)) {
12858
13130
  if (!entry.startsWith(`.${name}-`)) continue;
12859
- fs8.rmSync(path13.join(scopeDir, entry), { recursive: true, force: true });
12860
- appendUpgradeLog(`Removed stale scoped staging dir: ${path13.join(scopeDir, entry)}`);
13131
+ fs8.rmSync(path15.join(scopeDir, entry), { recursive: true, force: true });
13132
+ appendUpgradeLog(`Removed stale scoped staging dir: ${path15.join(scopeDir, entry)}`);
12861
13133
  }
12862
13134
  } else {
12863
13135
  for (const entry of fs8.readdirSync(npmRoot)) {
12864
13136
  if (!entry.startsWith(`.${pkgName}-`)) continue;
12865
- fs8.rmSync(path13.join(npmRoot, entry), { recursive: true, force: true });
12866
- appendUpgradeLog(`Removed stale staging dir: ${path13.join(npmRoot, entry)}`);
13137
+ fs8.rmSync(path15.join(npmRoot, entry), { recursive: true, force: true });
13138
+ appendUpgradeLog(`Removed stale staging dir: ${path15.join(npmRoot, entry)}`);
12867
13139
  }
12868
13140
  }
12869
13141
  if (fs8.existsSync(binDir)) {
12870
13142
  for (const entry of fs8.readdirSync(binDir)) {
12871
13143
  if (![...binNames].some((name) => entry.startsWith(`.${name}-`))) continue;
12872
- fs8.rmSync(path13.join(binDir, entry), { recursive: true, force: true });
12873
- appendUpgradeLog(`Removed stale bin staging entry: ${path13.join(binDir, entry)}`);
13144
+ fs8.rmSync(path15.join(binDir, entry), { recursive: true, force: true });
13145
+ appendUpgradeLog(`Removed stale bin staging entry: ${path15.join(binDir, entry)}`);
12874
13146
  }
12875
13147
  }
12876
13148
  }
@@ -12912,7 +13184,7 @@ async function runDaemonUpgradeHelper(payload) {
12912
13184
  appendUpgradeLog(installOutput.trim());
12913
13185
  }
12914
13186
  if (process.platform === "win32") {
12915
- await new Promise((resolve9) => setTimeout(resolve9, 500));
13187
+ await new Promise((resolve12) => setTimeout(resolve12, 500));
12916
13188
  cleanupStaleGlobalInstallDirs(payload.packageName);
12917
13189
  appendUpgradeLog("Post-install staging cleanup complete");
12918
13190
  }
@@ -12956,6 +13228,25 @@ var CHAT_COMMANDS = [
12956
13228
  "change_model"
12957
13229
  ];
12958
13230
  var READ_DEBUG_ENABLED2 = process.argv.includes("--dev") || process.env.ADHDEV_READ_DEBUG === "1";
13231
+ function toHostedCliRuntimeDescriptor(record) {
13232
+ if (!record || typeof record !== "object") return null;
13233
+ const runtimeId = typeof record.sessionId === "string" ? record.sessionId : "";
13234
+ const cliType = typeof record.providerType === "string" ? record.providerType : "";
13235
+ const workspace = typeof record.workspace === "string" ? record.workspace : "";
13236
+ if (!runtimeId || !cliType || !workspace) return null;
13237
+ return {
13238
+ runtimeId,
13239
+ runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
13240
+ displayName: typeof record.displayName === "string" ? record.displayName : void 0,
13241
+ workspaceLabel: typeof record.workspaceLabel === "string" ? record.workspaceLabel : void 0,
13242
+ lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
13243
+ recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
13244
+ cliType,
13245
+ workspace,
13246
+ cliArgs: Array.isArray(record.meta?.cliArgs) ? record.meta.cliArgs : [],
13247
+ providerSessionId: typeof record.meta?.providerSessionId === "string" ? String(record.meta.providerSessionId) : void 0
13248
+ };
13249
+ }
12959
13250
  var DaemonCommandRouter = class {
12960
13251
  deps;
12961
13252
  constructor(deps) {
@@ -13029,6 +13320,90 @@ var DaemonCommandRouter = class {
13029
13320
  return { success: false, error: e.message };
13030
13321
  }
13031
13322
  }
13323
+ case "session_host_get_diagnostics": {
13324
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13325
+ const diagnostics = await this.deps.sessionHostControl.getDiagnostics({
13326
+ includeSessions: args?.includeSessions !== false,
13327
+ limit: Number(args?.limit) || void 0
13328
+ });
13329
+ return { success: true, diagnostics };
13330
+ }
13331
+ case "session_host_list_sessions": {
13332
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13333
+ const sessions = await this.deps.sessionHostControl.listSessions();
13334
+ return { success: true, sessions };
13335
+ }
13336
+ case "session_host_stop_session": {
13337
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13338
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13339
+ if (!sessionId) return { success: false, error: "sessionId required" };
13340
+ const record = await this.deps.sessionHostControl.stopSession(sessionId);
13341
+ return { success: true, record };
13342
+ }
13343
+ case "session_host_resume_session": {
13344
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13345
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13346
+ if (!sessionId) return { success: false, error: "sessionId required" };
13347
+ const record = await this.deps.sessionHostControl.resumeSession(sessionId);
13348
+ const hosted = toHostedCliRuntimeDescriptor(record);
13349
+ if (hosted) {
13350
+ await this.deps.cliManager.restoreHostedSessions([hosted]);
13351
+ }
13352
+ return { success: true, record };
13353
+ }
13354
+ case "session_host_restart_session": {
13355
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13356
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13357
+ if (!sessionId) return { success: false, error: "sessionId required" };
13358
+ const record = await this.deps.sessionHostControl.restartSession(sessionId);
13359
+ const hosted = toHostedCliRuntimeDescriptor(record);
13360
+ if (hosted) {
13361
+ await this.deps.cliManager.restoreHostedSessions([hosted]);
13362
+ }
13363
+ return { success: true, record };
13364
+ }
13365
+ case "session_host_send_signal": {
13366
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13367
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13368
+ const signal = typeof args?.signal === "string" ? args.signal : "";
13369
+ if (!sessionId) return { success: false, error: "sessionId required" };
13370
+ if (!signal) return { success: false, error: "signal required" };
13371
+ const record = await this.deps.sessionHostControl.sendSignal(sessionId, signal);
13372
+ return { success: true, record };
13373
+ }
13374
+ case "session_host_force_detach_client": {
13375
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13376
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13377
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
13378
+ if (!sessionId) return { success: false, error: "sessionId required" };
13379
+ if (!clientId) return { success: false, error: "clientId required" };
13380
+ const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
13381
+ return { success: true, record };
13382
+ }
13383
+ case "session_host_acquire_write": {
13384
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13385
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13386
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
13387
+ const ownerType = args?.ownerType === "agent" ? "agent" : "user";
13388
+ if (!sessionId) return { success: false, error: "sessionId required" };
13389
+ if (!clientId) return { success: false, error: "clientId required" };
13390
+ const record = await this.deps.sessionHostControl.acquireWrite({
13391
+ sessionId,
13392
+ clientId,
13393
+ ownerType,
13394
+ force: args?.force !== false
13395
+ });
13396
+ return { success: true, record };
13397
+ }
13398
+ case "session_host_release_write": {
13399
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13400
+ const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
13401
+ const clientId = typeof args?.clientId === "string" ? args.clientId : "";
13402
+ if (!sessionId) return { success: false, error: "sessionId required" };
13403
+ if (!clientId) return { success: false, error: "clientId required" };
13404
+ const record = await this.deps.sessionHostControl.releaseWrite({ sessionId, clientId });
13405
+ return { success: true, record };
13406
+ }
13032
13407
  case "list_saved_sessions": {
13033
13408
  const providerType = typeof args?.providerType === "string" ? args.providerType.trim() : typeof args?.agentType === "string" ? args.agentType.trim() : "";
13034
13409
  const kind = args?.kind === "acp" ? "acp" : "cli";
@@ -13087,6 +13462,12 @@ var DaemonCommandRouter = class {
13087
13462
  if (!ideType) throw new Error("ideType required");
13088
13463
  const killProcess = args?.killProcess !== false;
13089
13464
  await this.stopIde(ideType, killProcess);
13465
+ try {
13466
+ const results = await detectIDEs(this.deps.providerLoader);
13467
+ this.deps.detectedIdes.value = results;
13468
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
13469
+ } catch {
13470
+ }
13090
13471
  return { success: true, ideType, stopped: true, processKilled: killProcess };
13091
13472
  }
13092
13473
  // ─── IDE restart ───
@@ -13129,6 +13510,12 @@ var DaemonCommandRouter = class {
13129
13510
  }
13130
13511
  }
13131
13512
  this.deps.onIdeConnected?.();
13513
+ try {
13514
+ const results = await detectIDEs(this.deps.providerLoader);
13515
+ this.deps.detectedIdes.value = results;
13516
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
13517
+ } catch {
13518
+ }
13132
13519
  if (result.success && resolvedWorkspace) {
13133
13520
  try {
13134
13521
  const next = appendRecentActivity(loadState(), {
@@ -13156,8 +13543,9 @@ var DaemonCommandRouter = class {
13156
13543
  }
13157
13544
  // ─── Detect IDEs ───
13158
13545
  case "detect_ides": {
13159
- const results = await detectIDEs();
13546
+ const results = await detectIDEs(this.deps.providerLoader);
13160
13547
  this.deps.detectedIdes.value = results;
13548
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
13161
13549
  return { success: true, detectedInfo: results };
13162
13550
  }
13163
13551
  // ─── Set User Name ───
@@ -13557,6 +13945,14 @@ var ProviderStreamAdapter = class {
13557
13945
  hasScript(name) {
13558
13946
  return typeof this.provider.scripts?.[name] === "function";
13559
13947
  }
13948
+ parseMaybeJson(raw) {
13949
+ if (typeof raw !== "string") return raw;
13950
+ try {
13951
+ return JSON.parse(raw);
13952
+ } catch {
13953
+ return raw;
13954
+ }
13955
+ }
13560
13956
  summarizeRaw(raw) {
13561
13957
  try {
13562
13958
  if (typeof raw === "string") return raw.replace(/\s+/g, " ").trim().slice(0, 240);
@@ -13617,12 +14013,30 @@ var ProviderStreamAdapter = class {
13617
14013
  }
13618
14014
  }
13619
14015
  async sendMessage(evaluate, text) {
13620
- const script = this.callScript("sendMessage", text);
14016
+ const params = { message: text };
14017
+ const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
13621
14018
  if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
13622
14019
  const result = await evaluate(script);
13623
14020
  if (result && typeof result === "string" && result.startsWith("error:")) {
13624
14021
  throw new Error(`[${this.agentName}] sendMessage failed: ${result}`);
13625
14022
  }
14023
+ const parsed = this.parseMaybeJson(result);
14024
+ if (parsed === true) return;
14025
+ if (typeof parsed === "string") {
14026
+ const normalized = parsed.trim().toLowerCase();
14027
+ if (normalized === "ok" || normalized === "sent" || normalized === "success" || normalized === "true") {
14028
+ return;
14029
+ }
14030
+ }
14031
+ if (parsed && typeof parsed === "object") {
14032
+ if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
14033
+ return;
14034
+ }
14035
+ if (typeof parsed.error === "string" && parsed.error.trim()) {
14036
+ throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
14037
+ }
14038
+ }
14039
+ throw new Error(`[${this.agentName}] sendMessage was not confirmed`);
13626
14040
  }
13627
14041
  async resolveAction(evaluate, action, button) {
13628
14042
  const script = this.callScript("resolveAction", { action, button });
@@ -13645,7 +14059,10 @@ var ProviderStreamAdapter = class {
13645
14059
  const raw = await evaluate(script, 1e4);
13646
14060
  const data = typeof raw === "string" ? JSON.parse(raw) : raw;
13647
14061
  if (data?.error) return [];
13648
- return Array.isArray(data) ? data : [];
14062
+ if (Array.isArray(data)) return data;
14063
+ if (Array.isArray(data?.sessions)) return data.sessions;
14064
+ if (Array.isArray(data?.chats)) return data.chats;
14065
+ return [];
13649
14066
  } catch {
13650
14067
  return [];
13651
14068
  }
@@ -13653,7 +14070,17 @@ var ProviderStreamAdapter = class {
13653
14070
  async switchSession(evaluate, sessionId) {
13654
14071
  const script = this.callScript("switchSession", sessionId);
13655
14072
  if (!script) return false;
13656
- return await evaluate(script, 1e4) === true;
14073
+ const raw = await evaluate(script, 1e4);
14074
+ const data = this.parseMaybeJson(raw);
14075
+ if (data === true) return true;
14076
+ if (typeof data === "string") {
14077
+ const normalized = data.trim().toLowerCase();
14078
+ return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
14079
+ }
14080
+ if (data && typeof data === "object") {
14081
+ return data.switched === true || data.success === true || data.ok === true;
14082
+ }
14083
+ return false;
13657
14084
  }
13658
14085
  async focusEditor(evaluate) {
13659
14086
  const script = this.callScript("focusEditor");
@@ -14343,11 +14770,11 @@ var ProviderInstanceManager = class {
14343
14770
 
14344
14771
  // src/providers/version-archive.ts
14345
14772
  import * as fs10 from "fs";
14346
- import * as path14 from "path";
14773
+ import * as path16 from "path";
14347
14774
  import * as os16 from "os";
14348
14775
  import { execSync as execSync5 } from "child_process";
14349
14776
  import { platform as platform7 } from "os";
14350
- var ARCHIVE_PATH = path14.join(os16.homedir(), ".adhdev", "version-history.json");
14777
+ var ARCHIVE_PATH = path16.join(os16.homedir(), ".adhdev", "version-history.json");
14351
14778
  var MAX_ENTRIES_PER_PROVIDER = 20;
14352
14779
  var VersionArchive = class {
14353
14780
  history = {};
@@ -14394,7 +14821,7 @@ var VersionArchive = class {
14394
14821
  }
14395
14822
  save() {
14396
14823
  try {
14397
- fs10.mkdirSync(path14.dirname(ARCHIVE_PATH), { recursive: true });
14824
+ fs10.mkdirSync(path16.dirname(ARCHIVE_PATH), { recursive: true });
14398
14825
  fs10.writeFileSync(ARCHIVE_PATH, JSON.stringify(this.history, null, 2));
14399
14826
  } catch {
14400
14827
  }
@@ -14435,7 +14862,7 @@ function checkPathExists2(paths) {
14435
14862
  for (const p of paths) {
14436
14863
  if (p.includes("*")) {
14437
14864
  const home = os16.homedir();
14438
- const resolved = p.replace(/\*/g, home.split(path14.sep).pop() || "");
14865
+ const resolved = p.replace(/\*/g, home.split(path16.sep).pop() || "");
14439
14866
  if (fs10.existsSync(resolved)) return resolved;
14440
14867
  } else {
14441
14868
  if (fs10.existsSync(p)) return p;
@@ -14445,7 +14872,7 @@ function checkPathExists2(paths) {
14445
14872
  }
14446
14873
  function getMacAppVersion(appPath) {
14447
14874
  if (platform7() !== "darwin" || !appPath.endsWith(".app")) return null;
14448
- const plistPath = path14.join(appPath, "Contents", "Info.plist");
14875
+ const plistPath = path16.join(appPath, "Contents", "Info.plist");
14449
14876
  if (!fs10.existsSync(plistPath)) return null;
14450
14877
  const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
14451
14878
  return raw || null;
@@ -14472,7 +14899,7 @@ async function detectAllVersions(loader, archive) {
14472
14899
  const cliBin = provider.cli ? findBinary2(provider.cli) : null;
14473
14900
  let resolvedBin = cliBin;
14474
14901
  if (!resolvedBin && appPath && currentOs === "darwin") {
14475
- const bundled = path14.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
14902
+ const bundled = path16.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
14476
14903
  if (provider.cli && fs10.existsSync(bundled)) resolvedBin = bundled;
14477
14904
  }
14478
14905
  info.installed = !!(appPath || resolvedBin);
@@ -14513,7 +14940,7 @@ async function detectAllVersions(loader, archive) {
14513
14940
  // src/daemon/dev-server.ts
14514
14941
  import * as http2 from "http";
14515
14942
  import * as fs14 from "fs";
14516
- import * as path18 from "path";
14943
+ import * as path20 from "path";
14517
14944
 
14518
14945
  // src/daemon/scaffold-template.ts
14519
14946
  function generateFiles(type, name, category, opts = {}) {
@@ -14850,7 +15277,7 @@ init_logger();
14850
15277
  // src/daemon/dev-cdp-handlers.ts
14851
15278
  init_logger();
14852
15279
  import * as fs11 from "fs";
14853
- import * as path15 from "path";
15280
+ import * as path17 from "path";
14854
15281
  async function handleCdpEvaluate(ctx, req, res) {
14855
15282
  const body = await ctx.readBody(req);
14856
15283
  const { expression, timeout, ideType } = body;
@@ -15028,17 +15455,17 @@ async function handleScriptHints(ctx, type, _req, res) {
15028
15455
  return;
15029
15456
  }
15030
15457
  let scriptsPath = "";
15031
- const directScripts = path15.join(dir, "scripts.js");
15458
+ const directScripts = path17.join(dir, "scripts.js");
15032
15459
  if (fs11.existsSync(directScripts)) {
15033
15460
  scriptsPath = directScripts;
15034
15461
  } else {
15035
- const scriptsDir = path15.join(dir, "scripts");
15462
+ const scriptsDir = path17.join(dir, "scripts");
15036
15463
  if (fs11.existsSync(scriptsDir)) {
15037
15464
  const versions = fs11.readdirSync(scriptsDir).filter((d) => {
15038
- return fs11.statSync(path15.join(scriptsDir, d)).isDirectory();
15465
+ return fs11.statSync(path17.join(scriptsDir, d)).isDirectory();
15039
15466
  }).sort().reverse();
15040
15467
  for (const ver of versions) {
15041
- const p = path15.join(scriptsDir, ver, "scripts.js");
15468
+ const p = path17.join(scriptsDir, ver, "scripts.js");
15042
15469
  if (fs11.existsSync(p)) {
15043
15470
  scriptsPath = p;
15044
15471
  break;
@@ -15857,7 +16284,7 @@ async function handleDomContext(ctx, type, req, res) {
15857
16284
 
15858
16285
  // src/daemon/dev-cli-debug.ts
15859
16286
  import * as fs12 from "fs";
15860
- import * as path16 from "path";
16287
+ import * as path18 from "path";
15861
16288
  function slugifyFixtureName(value) {
15862
16289
  const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
15863
16290
  return normalized || `fixture-${Date.now()}`;
@@ -15867,11 +16294,11 @@ function getCliFixtureDir(ctx, type) {
15867
16294
  if (!providerDir) {
15868
16295
  throw new Error(`Provider directory not found for '${type}'`);
15869
16296
  }
15870
- return path16.join(providerDir, "fixtures");
16297
+ return path18.join(providerDir, "fixtures");
15871
16298
  }
15872
16299
  function readCliFixture(ctx, type, name) {
15873
16300
  const fixtureDir = getCliFixtureDir(ctx, type);
15874
- const filePath = path16.join(fixtureDir, `${name}.json`);
16301
+ const filePath = path18.join(fixtureDir, `${name}.json`);
15875
16302
  if (!fs12.existsSync(filePath)) {
15876
16303
  throw new Error(`Fixture not found: ${filePath}`);
15877
16304
  }
@@ -16031,7 +16458,7 @@ function getCliTargetBundle(ctx, type, instanceId) {
16031
16458
  return { target, instance, adapter };
16032
16459
  }
16033
16460
  function sleep(ms) {
16034
- return new Promise((resolve9) => setTimeout(resolve9, ms));
16461
+ return new Promise((resolve12) => setTimeout(resolve12, ms));
16035
16462
  }
16036
16463
  async function waitForCliReady(ctx, type, instanceId, timeoutMs) {
16037
16464
  const startedAt = Date.now();
@@ -16630,7 +17057,7 @@ async function handleCliFixtureCapture(ctx, req, res) {
16630
17057
  },
16631
17058
  notes: typeof body?.notes === "string" ? body.notes : void 0
16632
17059
  };
16633
- const filePath = path16.join(fixtureDir, `${name}.json`);
17060
+ const filePath = path18.join(fixtureDir, `${name}.json`);
16634
17061
  fs12.writeFileSync(filePath, JSON.stringify(fixture, null, 2));
16635
17062
  ctx.json(res, 200, {
16636
17063
  saved: true,
@@ -16654,7 +17081,7 @@ async function handleCliFixtureList(ctx, type, _req, res) {
16654
17081
  return;
16655
17082
  }
16656
17083
  const fixtures = fs12.readdirSync(fixtureDir).filter((file) => file.endsWith(".json")).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" })).map((file) => {
16657
- const fullPath = path16.join(fixtureDir, file);
17084
+ const fullPath = path18.join(fixtureDir, file);
16658
17085
  try {
16659
17086
  const raw = JSON.parse(fs12.readFileSync(fullPath, "utf-8"));
16660
17087
  return {
@@ -16790,7 +17217,7 @@ async function handleCliRaw(ctx, req, res) {
16790
17217
 
16791
17218
  // src/daemon/dev-auto-implement.ts
16792
17219
  import * as fs13 from "fs";
16793
- import * as path17 from "path";
17220
+ import * as path19 from "path";
16794
17221
  import * as os17 from "os";
16795
17222
  function getAutoImplPid(ctx) {
16796
17223
  const proc = ctx.autoImplProcess;
@@ -16830,22 +17257,22 @@ function getLatestScriptVersionDir(scriptsDir) {
16830
17257
  if (!fs13.existsSync(scriptsDir)) return null;
16831
17258
  const versions = fs13.readdirSync(scriptsDir).filter((d) => {
16832
17259
  try {
16833
- return fs13.statSync(path17.join(scriptsDir, d)).isDirectory();
17260
+ return fs13.statSync(path19.join(scriptsDir, d)).isDirectory();
16834
17261
  } catch {
16835
17262
  return false;
16836
17263
  }
16837
17264
  }).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
16838
17265
  if (versions.length === 0) return null;
16839
- return path17.join(scriptsDir, versions[0]);
17266
+ return path19.join(scriptsDir, versions[0]);
16840
17267
  }
16841
17268
  function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
16842
- const canonicalUserDir = path17.resolve(ctx.providerLoader.getUserProviderDir(category, type));
16843
- const desiredDir = requestedDir ? path17.resolve(requestedDir) : canonicalUserDir;
16844
- const upstreamRoot = path17.resolve(ctx.providerLoader.getUpstreamDir());
16845
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path17.sep}`)) {
17269
+ const canonicalUserDir = path19.resolve(ctx.providerLoader.getUserProviderDir(category, type));
17270
+ const desiredDir = requestedDir ? path19.resolve(requestedDir) : canonicalUserDir;
17271
+ const upstreamRoot = path19.resolve(ctx.providerLoader.getUpstreamDir());
17272
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path19.sep}`)) {
16846
17273
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
16847
17274
  }
16848
- if (path17.basename(desiredDir) !== type) {
17275
+ if (path19.basename(desiredDir) !== type) {
16849
17276
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
16850
17277
  }
16851
17278
  const sourceDir = ctx.findProviderDir(type);
@@ -16853,11 +17280,11 @@ function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
16853
17280
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
16854
17281
  }
16855
17282
  if (!fs13.existsSync(desiredDir)) {
16856
- fs13.mkdirSync(path17.dirname(desiredDir), { recursive: true });
17283
+ fs13.mkdirSync(path19.dirname(desiredDir), { recursive: true });
16857
17284
  fs13.cpSync(sourceDir, desiredDir, { recursive: true });
16858
17285
  ctx.log(`Auto-implement writable copy created: ${desiredDir}`);
16859
17286
  }
16860
- const providerJson = path17.join(desiredDir, "provider.json");
17287
+ const providerJson = path19.join(desiredDir, "provider.json");
16861
17288
  if (!fs13.existsSync(providerJson)) {
16862
17289
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
16863
17290
  }
@@ -16880,13 +17307,13 @@ function loadAutoImplReferenceScripts(ctx, referenceType) {
16880
17307
  const refDir = ctx.findProviderDir(referenceType);
16881
17308
  if (!refDir || !fs13.existsSync(refDir)) return {};
16882
17309
  const referenceScripts = {};
16883
- const scriptsDir = path17.join(refDir, "scripts");
17310
+ const scriptsDir = path19.join(refDir, "scripts");
16884
17311
  const latestDir = getLatestScriptVersionDir(scriptsDir);
16885
17312
  if (!latestDir) return referenceScripts;
16886
17313
  for (const file of fs13.readdirSync(latestDir)) {
16887
17314
  if (!file.endsWith(".js")) continue;
16888
17315
  try {
16889
- referenceScripts[file] = fs13.readFileSync(path17.join(latestDir, file), "utf-8");
17316
+ referenceScripts[file] = fs13.readFileSync(path19.join(latestDir, file), "utf-8");
16890
17317
  } catch {
16891
17318
  }
16892
17319
  }
@@ -16994,9 +17421,9 @@ async function handleAutoImplement(ctx, type, req, res) {
16994
17421
  });
16995
17422
  const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
16996
17423
  const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
16997
- const tmpDir = path17.join(os17.tmpdir(), "adhdev-autoimpl");
17424
+ const tmpDir = path19.join(os17.tmpdir(), "adhdev-autoimpl");
16998
17425
  if (!fs13.existsSync(tmpDir)) fs13.mkdirSync(tmpDir, { recursive: true });
16999
- const promptFile = path17.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
17426
+ const promptFile = path19.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
17000
17427
  fs13.writeFileSync(promptFile, prompt, "utf-8");
17001
17428
  ctx.log(`Auto-implement prompt written to ${promptFile} (${prompt.length} chars)`);
17002
17429
  const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
@@ -17423,7 +17850,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17423
17850
  setMode: "set_mode.js"
17424
17851
  };
17425
17852
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
17426
- const scriptsDir = path17.join(providerDir, "scripts");
17853
+ const scriptsDir = path19.join(providerDir, "scripts");
17427
17854
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
17428
17855
  if (latestScriptsDir) {
17429
17856
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -17434,7 +17861,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17434
17861
  for (const file of fs13.readdirSync(latestScriptsDir)) {
17435
17862
  if (file.endsWith(".js") && targetFileNames.has(file)) {
17436
17863
  try {
17437
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
17864
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17438
17865
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
17439
17866
  lines.push("```javascript");
17440
17867
  lines.push(content);
@@ -17451,7 +17878,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17451
17878
  lines.push("");
17452
17879
  for (const file of refFiles) {
17453
17880
  try {
17454
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
17881
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17455
17882
  lines.push(`### \`${file}\` \u{1F512}`);
17456
17883
  lines.push("```javascript");
17457
17884
  lines.push(content);
@@ -17492,10 +17919,10 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17492
17919
  lines.push("");
17493
17920
  }
17494
17921
  }
17495
- const docsDir = path17.join(providerDir, "../../docs");
17922
+ const docsDir = path19.join(providerDir, "../../docs");
17496
17923
  const loadGuide = (name) => {
17497
17924
  try {
17498
- const p = path17.join(docsDir, name);
17925
+ const p = path19.join(docsDir, name);
17499
17926
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
17500
17927
  } catch {
17501
17928
  }
@@ -17730,7 +18157,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17730
18157
  parseApproval: "parse_approval.js"
17731
18158
  };
17732
18159
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
17733
- const scriptsDir = path17.join(providerDir, "scripts");
18160
+ const scriptsDir = path19.join(providerDir, "scripts");
17734
18161
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
17735
18162
  if (latestScriptsDir) {
17736
18163
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -17742,7 +18169,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17742
18169
  if (!file.endsWith(".js")) continue;
17743
18170
  if (!targetFileNames.has(file)) continue;
17744
18171
  try {
17745
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
18172
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17746
18173
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
17747
18174
  lines.push("```javascript");
17748
18175
  lines.push(content);
@@ -17758,7 +18185,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17758
18185
  lines.push("");
17759
18186
  for (const file of refFiles) {
17760
18187
  try {
17761
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
18188
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17762
18189
  lines.push(`### \`${file}\` \u{1F512}`);
17763
18190
  lines.push("```javascript");
17764
18191
  lines.push(content);
@@ -17791,10 +18218,10 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17791
18218
  lines.push("");
17792
18219
  }
17793
18220
  }
17794
- const docsDir = path17.join(providerDir, "../../docs");
18221
+ const docsDir = path19.join(providerDir, "../../docs");
17795
18222
  const loadGuide = (name) => {
17796
18223
  try {
17797
- const p = path17.join(docsDir, name);
18224
+ const p = path19.join(docsDir, name);
17798
18225
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
17799
18226
  } catch {
17800
18227
  }
@@ -18205,8 +18632,8 @@ var DevServer = class _DevServer {
18205
18632
  }
18206
18633
  getEndpointList() {
18207
18634
  return this.routes.map((r) => {
18208
- const path19 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
18209
- return `${r.method.padEnd(5)} ${path19}`;
18635
+ const path21 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
18636
+ return `${r.method.padEnd(5)} ${path21}`;
18210
18637
  });
18211
18638
  }
18212
18639
  async start(port = DEV_SERVER_PORT) {
@@ -18237,15 +18664,15 @@ var DevServer = class _DevServer {
18237
18664
  this.json(res, 500, { error: e.message });
18238
18665
  }
18239
18666
  });
18240
- return new Promise((resolve9, reject) => {
18667
+ return new Promise((resolve12, reject) => {
18241
18668
  this.server.listen(port, "127.0.0.1", () => {
18242
18669
  this.log(`Dev server listening on http://127.0.0.1:${port}`);
18243
- resolve9();
18670
+ resolve12();
18244
18671
  });
18245
18672
  this.server.on("error", (e) => {
18246
18673
  if (e.code === "EADDRINUSE") {
18247
18674
  this.log(`Port ${port} in use, skipping dev server`);
18248
- resolve9();
18675
+ resolve12();
18249
18676
  } else {
18250
18677
  reject(e);
18251
18678
  }
@@ -18328,20 +18755,20 @@ var DevServer = class _DevServer {
18328
18755
  child.stderr?.on("data", (d) => {
18329
18756
  stderr += d.toString().slice(0, 2e3);
18330
18757
  });
18331
- await new Promise((resolve9) => {
18758
+ await new Promise((resolve12) => {
18332
18759
  const timer = setTimeout(() => {
18333
18760
  child.kill();
18334
- resolve9();
18761
+ resolve12();
18335
18762
  }, 3e3);
18336
18763
  child.on("exit", () => {
18337
18764
  clearTimeout(timer);
18338
- resolve9();
18765
+ resolve12();
18339
18766
  });
18340
18767
  child.stdout?.once("data", () => {
18341
18768
  setTimeout(() => {
18342
18769
  child.kill();
18343
18770
  clearTimeout(timer);
18344
- resolve9();
18771
+ resolve12();
18345
18772
  }, 500);
18346
18773
  });
18347
18774
  });
@@ -18488,12 +18915,12 @@ var DevServer = class _DevServer {
18488
18915
  // ─── DevConsole SPA ───
18489
18916
  getConsoleDistDir() {
18490
18917
  const candidates = [
18491
- path18.resolve(__dirname, "../../web-devconsole/dist"),
18492
- path18.resolve(__dirname, "../../../web-devconsole/dist"),
18493
- path18.join(process.cwd(), "packages/web-devconsole/dist")
18918
+ path20.resolve(__dirname, "../../web-devconsole/dist"),
18919
+ path20.resolve(__dirname, "../../../web-devconsole/dist"),
18920
+ path20.join(process.cwd(), "packages/web-devconsole/dist")
18494
18921
  ];
18495
18922
  for (const dir of candidates) {
18496
- if (fs14.existsSync(path18.join(dir, "index.html"))) return dir;
18923
+ if (fs14.existsSync(path20.join(dir, "index.html"))) return dir;
18497
18924
  }
18498
18925
  return null;
18499
18926
  }
@@ -18503,7 +18930,7 @@ var DevServer = class _DevServer {
18503
18930
  this.json(res, 500, { error: "DevConsole not found. Run: npm run build -w packages/web-devconsole" });
18504
18931
  return;
18505
18932
  }
18506
- const htmlPath = path18.join(distDir, "index.html");
18933
+ const htmlPath = path20.join(distDir, "index.html");
18507
18934
  try {
18508
18935
  const html = fs14.readFileSync(htmlPath, "utf-8");
18509
18936
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
@@ -18528,15 +18955,15 @@ var DevServer = class _DevServer {
18528
18955
  this.json(res, 404, { error: "Not found" });
18529
18956
  return;
18530
18957
  }
18531
- const safePath = path18.normalize(pathname).replace(/^\.\.\//, "");
18532
- const filePath = path18.join(distDir, safePath);
18958
+ const safePath = path20.normalize(pathname).replace(/^\.\.\//, "");
18959
+ const filePath = path20.join(distDir, safePath);
18533
18960
  if (!filePath.startsWith(distDir)) {
18534
18961
  this.json(res, 403, { error: "Forbidden" });
18535
18962
  return;
18536
18963
  }
18537
18964
  try {
18538
18965
  const content = fs14.readFileSync(filePath);
18539
- const ext = path18.extname(filePath);
18966
+ const ext = path20.extname(filePath);
18540
18967
  const contentType = _DevServer.MIME_MAP[ext] || "application/octet-stream";
18541
18968
  res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=31536000, immutable" });
18542
18969
  res.end(content);
@@ -18649,9 +19076,9 @@ var DevServer = class _DevServer {
18649
19076
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
18650
19077
  if (entry.isDirectory()) {
18651
19078
  files.push({ path: rel, size: 0, type: "dir" });
18652
- scan(path18.join(d, entry.name), rel);
19079
+ scan(path20.join(d, entry.name), rel);
18653
19080
  } else {
18654
- const stat = fs14.statSync(path18.join(d, entry.name));
19081
+ const stat = fs14.statSync(path20.join(d, entry.name));
18655
19082
  files.push({ path: rel, size: stat.size, type: "file" });
18656
19083
  }
18657
19084
  }
@@ -18674,7 +19101,7 @@ var DevServer = class _DevServer {
18674
19101
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
18675
19102
  return;
18676
19103
  }
18677
- const fullPath = path18.resolve(dir, path18.normalize(filePath));
19104
+ const fullPath = path20.resolve(dir, path20.normalize(filePath));
18678
19105
  if (!fullPath.startsWith(dir)) {
18679
19106
  this.json(res, 403, { error: "Forbidden" });
18680
19107
  return;
@@ -18699,14 +19126,14 @@ var DevServer = class _DevServer {
18699
19126
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
18700
19127
  return;
18701
19128
  }
18702
- const fullPath = path18.resolve(dir, path18.normalize(filePath));
19129
+ const fullPath = path20.resolve(dir, path20.normalize(filePath));
18703
19130
  if (!fullPath.startsWith(dir)) {
18704
19131
  this.json(res, 403, { error: "Forbidden" });
18705
19132
  return;
18706
19133
  }
18707
19134
  try {
18708
19135
  if (fs14.existsSync(fullPath)) fs14.copyFileSync(fullPath, fullPath + ".bak");
18709
- fs14.mkdirSync(path18.dirname(fullPath), { recursive: true });
19136
+ fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
18710
19137
  fs14.writeFileSync(fullPath, content, "utf-8");
18711
19138
  this.log(`File saved: ${fullPath} (${content.length} chars)`);
18712
19139
  this.providerLoader.reload();
@@ -18723,7 +19150,7 @@ var DevServer = class _DevServer {
18723
19150
  return;
18724
19151
  }
18725
19152
  for (const name of ["scripts.js", "provider.json"]) {
18726
- const p = path18.join(dir, name);
19153
+ const p = path20.join(dir, name);
18727
19154
  if (fs14.existsSync(p)) {
18728
19155
  const source = fs14.readFileSync(p, "utf-8");
18729
19156
  this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
@@ -18744,8 +19171,8 @@ var DevServer = class _DevServer {
18744
19171
  this.json(res, 404, { error: `Provider not found: ${type}` });
18745
19172
  return;
18746
19173
  }
18747
- const target = fs14.existsSync(path18.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
18748
- const targetPath = path18.join(dir, target);
19174
+ const target = fs14.existsSync(path20.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
19175
+ const targetPath = path20.join(dir, target);
18749
19176
  try {
18750
19177
  if (fs14.existsSync(targetPath)) fs14.copyFileSync(targetPath, targetPath + ".bak");
18751
19178
  fs14.writeFileSync(targetPath, source, "utf-8");
@@ -18850,14 +19277,14 @@ var DevServer = class _DevServer {
18850
19277
  child.stderr?.on("data", (d) => {
18851
19278
  stderr += d.toString();
18852
19279
  });
18853
- await new Promise((resolve9) => {
19280
+ await new Promise((resolve12) => {
18854
19281
  const timer = setTimeout(() => {
18855
19282
  child.kill();
18856
- resolve9();
19283
+ resolve12();
18857
19284
  }, timeout);
18858
19285
  child.on("exit", () => {
18859
19286
  clearTimeout(timer);
18860
- resolve9();
19287
+ resolve12();
18861
19288
  });
18862
19289
  });
18863
19290
  const elapsed = Date.now() - start;
@@ -18905,7 +19332,7 @@ var DevServer = class _DevServer {
18905
19332
  }
18906
19333
  let targetDir;
18907
19334
  targetDir = this.providerLoader.getUserProviderDir(category, type);
18908
- const jsonPath = path18.join(targetDir, "provider.json");
19335
+ const jsonPath = path20.join(targetDir, "provider.json");
18909
19336
  if (fs14.existsSync(jsonPath)) {
18910
19337
  this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
18911
19338
  return;
@@ -18917,8 +19344,8 @@ var DevServer = class _DevServer {
18917
19344
  const createdFiles = ["provider.json"];
18918
19345
  if (result.files) {
18919
19346
  for (const [relPath, content] of Object.entries(result.files)) {
18920
- const fullPath = path18.join(targetDir, relPath);
18921
- fs14.mkdirSync(path18.dirname(fullPath), { recursive: true });
19347
+ const fullPath = path20.join(targetDir, relPath);
19348
+ fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
18922
19349
  fs14.writeFileSync(fullPath, content, "utf-8");
18923
19350
  createdFiles.push(relPath);
18924
19351
  }
@@ -18971,22 +19398,22 @@ var DevServer = class _DevServer {
18971
19398
  if (!fs14.existsSync(scriptsDir)) return null;
18972
19399
  const versions = fs14.readdirSync(scriptsDir).filter((d) => {
18973
19400
  try {
18974
- return fs14.statSync(path18.join(scriptsDir, d)).isDirectory();
19401
+ return fs14.statSync(path20.join(scriptsDir, d)).isDirectory();
18975
19402
  } catch {
18976
19403
  return false;
18977
19404
  }
18978
19405
  }).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
18979
19406
  if (versions.length === 0) return null;
18980
- return path18.join(scriptsDir, versions[0]);
19407
+ return path20.join(scriptsDir, versions[0]);
18981
19408
  }
18982
19409
  resolveAutoImplWritableProviderDir(category, type, requestedDir) {
18983
- const canonicalUserDir = path18.resolve(this.providerLoader.getUserProviderDir(category, type));
18984
- const desiredDir = requestedDir ? path18.resolve(requestedDir) : canonicalUserDir;
18985
- const upstreamRoot = path18.resolve(this.providerLoader.getUpstreamDir());
18986
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path18.sep}`)) {
19410
+ const canonicalUserDir = path20.resolve(this.providerLoader.getUserProviderDir(category, type));
19411
+ const desiredDir = requestedDir ? path20.resolve(requestedDir) : canonicalUserDir;
19412
+ const upstreamRoot = path20.resolve(this.providerLoader.getUpstreamDir());
19413
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path20.sep}`)) {
18987
19414
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
18988
19415
  }
18989
- if (path18.basename(desiredDir) !== type) {
19416
+ if (path20.basename(desiredDir) !== type) {
18990
19417
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
18991
19418
  }
18992
19419
  const sourceDir = this.findProviderDir(type);
@@ -18994,11 +19421,11 @@ var DevServer = class _DevServer {
18994
19421
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
18995
19422
  }
18996
19423
  if (!fs14.existsSync(desiredDir)) {
18997
- fs14.mkdirSync(path18.dirname(desiredDir), { recursive: true });
19424
+ fs14.mkdirSync(path20.dirname(desiredDir), { recursive: true });
18998
19425
  fs14.cpSync(sourceDir, desiredDir, { recursive: true });
18999
19426
  this.log(`Auto-implement writable copy created: ${desiredDir}`);
19000
19427
  }
19001
- const providerJson = path18.join(desiredDir, "provider.json");
19428
+ const providerJson = path20.join(desiredDir, "provider.json");
19002
19429
  if (!fs14.existsSync(providerJson)) {
19003
19430
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
19004
19431
  }
@@ -19046,7 +19473,7 @@ var DevServer = class _DevServer {
19046
19473
  setMode: "set_mode.js"
19047
19474
  };
19048
19475
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
19049
- const scriptsDir = path18.join(providerDir, "scripts");
19476
+ const scriptsDir = path20.join(providerDir, "scripts");
19050
19477
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
19051
19478
  if (latestScriptsDir) {
19052
19479
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -19057,7 +19484,7 @@ var DevServer = class _DevServer {
19057
19484
  for (const file of fs14.readdirSync(latestScriptsDir)) {
19058
19485
  if (file.endsWith(".js") && targetFileNames.has(file)) {
19059
19486
  try {
19060
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
19487
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19061
19488
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
19062
19489
  lines.push("```javascript");
19063
19490
  lines.push(content);
@@ -19074,7 +19501,7 @@ var DevServer = class _DevServer {
19074
19501
  lines.push("");
19075
19502
  for (const file of refFiles) {
19076
19503
  try {
19077
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
19504
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19078
19505
  lines.push(`### \`${file}\` \u{1F512}`);
19079
19506
  lines.push("```javascript");
19080
19507
  lines.push(content);
@@ -19115,10 +19542,10 @@ var DevServer = class _DevServer {
19115
19542
  lines.push("");
19116
19543
  }
19117
19544
  }
19118
- const docsDir = path18.join(providerDir, "../../docs");
19545
+ const docsDir = path20.join(providerDir, "../../docs");
19119
19546
  const loadGuide = (name) => {
19120
19547
  try {
19121
- const p = path18.join(docsDir, name);
19548
+ const p = path20.join(docsDir, name);
19122
19549
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
19123
19550
  } catch {
19124
19551
  }
@@ -19292,7 +19719,7 @@ var DevServer = class _DevServer {
19292
19719
  parseApproval: "parse_approval.js"
19293
19720
  };
19294
19721
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
19295
- const scriptsDir = path18.join(providerDir, "scripts");
19722
+ const scriptsDir = path20.join(providerDir, "scripts");
19296
19723
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
19297
19724
  if (latestScriptsDir) {
19298
19725
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -19304,7 +19731,7 @@ var DevServer = class _DevServer {
19304
19731
  if (!file.endsWith(".js")) continue;
19305
19732
  if (!targetFileNames.has(file)) continue;
19306
19733
  try {
19307
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
19734
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19308
19735
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
19309
19736
  lines.push("```javascript");
19310
19737
  lines.push(content);
@@ -19320,7 +19747,7 @@ var DevServer = class _DevServer {
19320
19747
  lines.push("");
19321
19748
  for (const file of refFiles) {
19322
19749
  try {
19323
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
19750
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19324
19751
  lines.push(`### \`${file}\` \u{1F512}`);
19325
19752
  lines.push("```javascript");
19326
19753
  lines.push(content);
@@ -19353,10 +19780,10 @@ var DevServer = class _DevServer {
19353
19780
  lines.push("");
19354
19781
  }
19355
19782
  }
19356
- const docsDir = path18.join(providerDir, "../../docs");
19783
+ const docsDir = path20.join(providerDir, "../../docs");
19357
19784
  const loadGuide = (name) => {
19358
19785
  try {
19359
- const p = path18.join(docsDir, name);
19786
+ const p = path20.join(docsDir, name);
19360
19787
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
19361
19788
  } catch {
19362
19789
  }
@@ -19532,14 +19959,14 @@ data: ${JSON.stringify(msg.data)}
19532
19959
  res.end(JSON.stringify(data, null, 2));
19533
19960
  }
19534
19961
  async readBody(req) {
19535
- return new Promise((resolve9) => {
19962
+ return new Promise((resolve12) => {
19536
19963
  let body = "";
19537
19964
  req.on("data", (chunk) => body += chunk);
19538
19965
  req.on("end", () => {
19539
19966
  try {
19540
- resolve9(JSON.parse(body));
19967
+ resolve12(JSON.parse(body));
19541
19968
  } catch {
19542
- resolve9({});
19969
+ resolve12({});
19543
19970
  }
19544
19971
  });
19545
19972
  });
@@ -19875,6 +20302,7 @@ var SessionHostRuntimeTransport = class {
19875
20302
  }
19876
20303
  }
19877
20304
  handleEvent(event) {
20305
+ if (!("sessionId" in event)) return;
19878
20306
  if (event.sessionId !== this.options.runtimeId) return;
19879
20307
  if ((event.type === "session_started" || event.type === "session_resumed") && typeof event.pid === "number") {
19880
20308
  this.currentPid = event.pid;
@@ -19950,7 +20378,10 @@ var SessionHostRuntimeTransport = class {
19950
20378
  clientId: client.clientId,
19951
20379
  type: client.type,
19952
20380
  readOnly: client.readOnly
19953
- }))
20381
+ })),
20382
+ restoredFromStorage: record.meta?.restoredFromStorage === true,
20383
+ recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
20384
+ recoveryError: typeof record.meta?.runtimeRecoveryError === "string" ? String(record.meta.runtimeRecoveryError) : null
19954
20385
  };
19955
20386
  }
19956
20387
  enqueue(action) {
@@ -20008,7 +20439,7 @@ async function waitForReady(endpoint, timeoutMs = STARTUP_TIMEOUT_MS) {
20008
20439
  const deadline = Date.now() + timeoutMs;
20009
20440
  while (Date.now() < deadline) {
20010
20441
  if (await canConnect(endpoint)) return;
20011
- await new Promise((resolve9) => setTimeout(resolve9, STARTUP_POLL_MS));
20442
+ await new Promise((resolve12) => setTimeout(resolve12, STARTUP_POLL_MS));
20012
20443
  }
20013
20444
  throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
20014
20445
  }
@@ -20164,10 +20595,10 @@ async function installExtension(ide, extension) {
20164
20595
  const buffer = Buffer.from(await res.arrayBuffer());
20165
20596
  const fs15 = await import("fs");
20166
20597
  fs15.writeFileSync(vsixPath, buffer);
20167
- return new Promise((resolve9) => {
20598
+ return new Promise((resolve12) => {
20168
20599
  const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
20169
20600
  exec2(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
20170
- resolve9({
20601
+ resolve12({
20171
20602
  extensionId: extension.id,
20172
20603
  marketplaceId: extension.marketplaceId,
20173
20604
  success: !error,
@@ -20180,11 +20611,11 @@ async function installExtension(ide, extension) {
20180
20611
  } catch (e) {
20181
20612
  }
20182
20613
  }
20183
- return new Promise((resolve9) => {
20614
+ return new Promise((resolve12) => {
20184
20615
  const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
20185
20616
  exec2(cmd, { timeout: 6e4 }, (error, stdout, stderr) => {
20186
20617
  if (error) {
20187
- resolve9({
20618
+ resolve12({
20188
20619
  extensionId: extension.id,
20189
20620
  marketplaceId: extension.marketplaceId,
20190
20621
  success: false,
@@ -20192,7 +20623,7 @@ async function installExtension(ide, extension) {
20192
20623
  error: stderr || error.message
20193
20624
  });
20194
20625
  } else {
20195
- resolve9({
20626
+ resolve12({
20196
20627
  extensionId: extension.id,
20197
20628
  marketplaceId: extension.marketplaceId,
20198
20629
  success: true,
@@ -20328,13 +20759,32 @@ async function initDaemonComponents(config) {
20328
20759
  const detectedIdesRef = { value: [] };
20329
20760
  let agentStreamManager = null;
20330
20761
  let poller = null;
20762
+ const refreshProviderAvailability = async (providerType) => {
20763
+ const targetProvider = providerType ? providerLoader.getMeta(providerLoader.resolveAlias(providerType)) : null;
20764
+ const targetCategory = targetProvider?.category;
20765
+ if (!providerType || targetCategory === "cli" || targetCategory === "acp") {
20766
+ if (providerType && targetProvider) {
20767
+ const detected = await detectCLI(targetProvider.type, providerLoader, { includeVersion: false });
20768
+ providerLoader.setProviderAvailability(targetProvider.type, {
20769
+ installed: !!detected,
20770
+ detectedPath: detected?.path || null
20771
+ });
20772
+ } else {
20773
+ providerLoader.setCliDetectionResults(await detectCLIs(providerLoader, { includeVersion: false }), true);
20774
+ }
20775
+ }
20776
+ if (!providerType || targetCategory === "ide") {
20777
+ detectedIdesRef.value = await detectIDEs(providerLoader);
20778
+ providerLoader.setIdeDetectionResults(detectedIdesRef.value, true);
20779
+ }
20780
+ };
20331
20781
  const cliManager = new DaemonCliManager({
20332
20782
  ...config.cliManagerDeps,
20333
20783
  getInstanceManager: () => instanceManager,
20334
20784
  getSessionRegistry: () => sessionRegistry
20335
20785
  }, providerLoader);
20336
20786
  LOG.info("Init", "Detecting IDEs...");
20337
- detectedIdesRef.value = await detectIDEs();
20787
+ await refreshProviderAvailability();
20338
20788
  const installed = detectedIdesRef.value.filter((i) => i.installed);
20339
20789
  LOG.info("Init", `Found ${installed.length} IDE(s): ${installed.map((i) => i.id).join(", ") || "none"}`);
20340
20790
  const cdpSetupContext = {
@@ -20374,7 +20824,11 @@ async function initDaemonComponents(config) {
20374
20824
  adapters: cliManager.adapters,
20375
20825
  providerLoader,
20376
20826
  instanceManager,
20377
- sessionRegistry
20827
+ sessionRegistry,
20828
+ onProviderSettingChanged: async (providerType) => {
20829
+ await refreshProviderAvailability(providerType);
20830
+ config.onStatusChange?.();
20831
+ }
20378
20832
  });
20379
20833
  agentStreamManager = new DaemonAgentStreamManager(
20380
20834
  LOG.forComponent("AgentStream").asLogFn(),
@@ -20397,6 +20851,7 @@ async function initDaemonComponents(config) {
20397
20851
  onIdeConnected: () => poller?.start(),
20398
20852
  onStatusChange: config.onStatusChange,
20399
20853
  onPostChatCommand: config.onPostChatCommand,
20854
+ sessionHostControl: config.sessionHostControl,
20400
20855
  getCdpLogFn: config.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
20401
20856
  });
20402
20857
  poller = new AgentStreamPoller({