@adhdev/daemon-core 0.8.27 → 0.8.29

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 (43) hide show
  1. package/dist/agent-stream/manager.d.ts +1 -1
  2. package/dist/agent-stream/provider-adapter.d.ts +5 -0
  3. package/dist/commands/handler.d.ts +1 -0
  4. package/dist/commands/router.d.ts +5 -0
  5. package/dist/commands/stream-commands.d.ts +1 -1
  6. package/dist/config/chat-history.d.ts +12 -0
  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 +987 -377
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +985 -375
  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/provider-loader.d.ts +26 -0
  16. package/dist/shared-types.d.ts +2 -0
  17. package/dist/status/snapshot.d.ts +8 -0
  18. package/node_modules/@adhdev/session-host-core/dist/index.d.mts +24 -1
  19. package/node_modules/@adhdev/session-host-core/dist/index.d.ts +24 -1
  20. package/node_modules/@adhdev/session-host-core/dist/index.js +6 -1
  21. package/node_modules/@adhdev/session-host-core/dist/index.js.map +1 -1
  22. package/node_modules/@adhdev/session-host-core/dist/index.mjs +6 -1
  23. package/node_modules/@adhdev/session-host-core/dist/index.mjs.map +1 -1
  24. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  25. package/package.json +1 -1
  26. package/src/agent-stream/manager.ts +2 -2
  27. package/src/agent-stream/provider-adapter.ts +111 -4
  28. package/src/boot/daemon-lifecycle.ts +28 -1
  29. package/src/cli-adapters/provider-cli-adapter.ts +17 -3
  30. package/src/commands/chat-commands.ts +89 -10
  31. package/src/commands/cli-manager.ts +16 -2
  32. package/src/commands/handler.ts +1 -0
  33. package/src/commands/router.ts +23 -1
  34. package/src/commands/stream-commands.ts +6 -3
  35. package/src/config/chat-history.ts +269 -18
  36. package/src/detection/cli-detector.ts +72 -29
  37. package/src/detection/ide-detector.ts +24 -8
  38. package/src/launch.ts +1 -1
  39. package/src/providers/acp-provider-instance.ts +19 -10
  40. package/src/providers/cli-provider-instance.ts +17 -2
  41. package/src/providers/provider-loader.ts +144 -11
  42. package/src/shared-types.ts +2 -0
  43. package/src/status/snapshot.ts +19 -1
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) {
@@ -1633,6 +1640,9 @@ var init_provider_cli_adapter = __esm({
1633
1640
  looksLikeVisibleIdlePrompt(screenText) {
1634
1641
  const text = String(screenText || "");
1635
1642
  if (!text.trim()) return false;
1643
+ if (this.cliType === "codex-cli" && /(^|\n)\s*[❯›>]\s+(?:Find and fix a bug in @filename|Improve documentation in @filename|Use \/skills|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Run \/review on my current changes)(?:\n|$)/im.test(text)) {
1644
+ return true;
1645
+ }
1636
1646
  return /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(text) || /⏎\s+send/i.test(text) || /\?\s*for\s*shortcuts/i.test(text) || /Type your message(?:\s+or\s+@path\/to\/file)?/i.test(text) || /workspace\s*\(\/directory\)/i.test(text) || /for\s*shortcuts/i.test(text);
1637
1647
  }
1638
1648
  findLastMatchingLineIndex(lines, predicate) {
@@ -1762,7 +1772,7 @@ var init_provider_cli_adapter = __esm({
1762
1772
  `[${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
1773
  );
1764
1774
  }
1765
- await new Promise((resolve9) => setTimeout(resolve9, 50));
1775
+ await new Promise((resolve12) => setTimeout(resolve12, 50));
1766
1776
  }
1767
1777
  const finalScreenText = this.terminalScreen.getText() || "";
1768
1778
  LOG.warn(
@@ -2273,7 +2283,7 @@ ${data.message || ""}`.trim();
2273
2283
  const deadline = Date.now() + 1e4;
2274
2284
  while (this.startupParseGate && Date.now() < deadline) {
2275
2285
  this.resolveStartupState("send_wait");
2276
- await new Promise((resolve9) => setTimeout(resolve9, 50));
2286
+ await new Promise((resolve12) => setTimeout(resolve12, 50));
2277
2287
  }
2278
2288
  }
2279
2289
  await this.waitForInteractivePrompt();
@@ -2498,17 +2508,17 @@ ${data.message || ""}`.trim();
2498
2508
  }
2499
2509
  }
2500
2510
  waitForStopped(timeoutMs) {
2501
- return new Promise((resolve9) => {
2511
+ return new Promise((resolve12) => {
2502
2512
  const startedAt = Date.now();
2503
2513
  const timer = setInterval(() => {
2504
2514
  if (!this.ptyProcess || this.currentStatus === "stopped") {
2505
2515
  clearInterval(timer);
2506
- resolve9(true);
2516
+ resolve12(true);
2507
2517
  return;
2508
2518
  }
2509
2519
  if (Date.now() - startedAt >= timeoutMs) {
2510
2520
  clearInterval(timer);
2511
- resolve9(false);
2521
+ resolve12(false);
2512
2522
  }
2513
2523
  }, 100);
2514
2524
  });
@@ -3124,6 +3134,7 @@ function resetState() {
3124
3134
  import { execSync } from "child_process";
3125
3135
  import { existsSync as existsSync4 } from "fs";
3126
3136
  import { platform, homedir as homedir3 } from "os";
3137
+ import * as path4 from "path";
3127
3138
  var BUILTIN_IDE_DEFINITIONS = [];
3128
3139
  var registeredIDEs = /* @__PURE__ */ new Map();
3129
3140
  function registerIDEDefinition(def) {
@@ -3140,9 +3151,16 @@ function getMergedDefinitions() {
3140
3151
  return [...merged.values()];
3141
3152
  }
3142
3153
  function findCliCommand(command) {
3154
+ const trimmed = String(command || "").trim();
3155
+ if (!trimmed) return null;
3156
+ if (path4.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
3157
+ const candidate = trimmed.startsWith("~") ? path4.join(homedir3(), trimmed.slice(1)) : trimmed;
3158
+ const resolved = path4.isAbsolute(candidate) ? candidate : path4.resolve(candidate);
3159
+ return existsSync4(resolved) ? resolved : null;
3160
+ }
3143
3161
  try {
3144
3162
  const result = execSync(
3145
- platform() === "win32" ? `where ${command}` : `which ${command}`,
3163
+ platform() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
3146
3164
  { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
3147
3165
  ).trim();
3148
3166
  return result.split("\n")[0] || null;
@@ -3165,23 +3183,23 @@ function getIdeVersion(cliCommand) {
3165
3183
  function checkPathExists(paths) {
3166
3184
  const home = homedir3();
3167
3185
  for (const p of paths) {
3168
- if (p.includes("*")) {
3186
+ const normalized = p.startsWith("~") ? path4.join(home, p.slice(1)) : p;
3187
+ if (normalized.includes("*")) {
3169
3188
  const username = home.split(/[\\/]/).pop() || "";
3170
- const resolved = p.replace("*", username);
3189
+ const resolved = normalized.replace("*", username);
3171
3190
  if (existsSync4(resolved)) return resolved;
3172
3191
  } else {
3173
- if (existsSync4(p)) return p;
3192
+ if (existsSync4(normalized)) return normalized;
3174
3193
  }
3175
3194
  }
3176
3195
  return null;
3177
3196
  }
3178
- async function detectIDEs() {
3197
+ async function detectIDEs(providerLoader) {
3179
3198
  const os18 = platform();
3180
3199
  const results = [];
3181
3200
  for (const def of getMergedDefinitions()) {
3182
- const cliPath = findCliCommand(def.cli);
3183
- const appPath = checkPathExists(def.paths[os18] || []);
3184
- const installed = !!(cliPath || appPath);
3201
+ const cliPath = findCliCommand(providerLoader?.getIdeCliCommand(def.id, def.cli) || def.cli);
3202
+ const appPath = checkPathExists(providerLoader?.getIdePathCandidates(def.id, def.paths[os18] || []) || []);
3185
3203
  let resolvedCli = cliPath;
3186
3204
  if (!resolvedCli && appPath && os18 === "darwin") {
3187
3205
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
@@ -3204,6 +3222,7 @@ async function detectIDEs() {
3204
3222
  }
3205
3223
  }
3206
3224
  }
3225
+ const installed = os18 === "darwin" ? !!(resolvedCli || appPath) : !!resolvedCli;
3207
3226
  const version = resolvedCli ? getIdeVersion(resolvedCli) : null;
3208
3227
  results.push({
3209
3228
  id: def.id,
@@ -3222,48 +3241,77 @@ async function detectIDEs() {
3222
3241
  // src/detection/cli-detector.ts
3223
3242
  import { exec } from "child_process";
3224
3243
  import * as os2 from "os";
3244
+ import * as path5 from "path";
3245
+ import { existsSync as existsSync5 } from "fs";
3225
3246
  function parseVersion(raw) {
3226
3247
  const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
3227
3248
  return match ? match[1] : raw.split("\n")[0].slice(0, 100);
3228
3249
  }
3250
+ function shellQuote(value) {
3251
+ if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
3252
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
3253
+ }
3254
+ function expandHome(value) {
3255
+ const trimmed = value.trim();
3256
+ if (!trimmed.startsWith("~")) return trimmed;
3257
+ return path5.join(os2.homedir(), trimmed.slice(1));
3258
+ }
3259
+ function isExplicitCommandPath(command) {
3260
+ const trimmed = command.trim();
3261
+ return path5.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
3262
+ }
3263
+ function resolveCommandPath(command) {
3264
+ const trimmed = command.trim();
3265
+ if (!trimmed) return null;
3266
+ if (isExplicitCommandPath(trimmed)) {
3267
+ const expanded = expandHome(trimmed);
3268
+ const candidate = path5.isAbsolute(expanded) ? expanded : path5.resolve(expanded);
3269
+ return existsSync5(candidate) ? candidate : null;
3270
+ }
3271
+ return null;
3272
+ }
3229
3273
  function execAsync(cmd, timeoutMs = 5e3) {
3230
- return new Promise((resolve9) => {
3274
+ return new Promise((resolve12) => {
3231
3275
  const child = exec(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout) => {
3232
3276
  if (err || !stdout?.trim()) {
3233
- resolve9(null);
3277
+ resolve12(null);
3234
3278
  } else {
3235
- resolve9(stdout.trim());
3279
+ resolve12(stdout.trim());
3236
3280
  }
3237
3281
  });
3238
- child.on("error", () => resolve9(null));
3282
+ child.on("error", () => resolve12(null));
3239
3283
  });
3240
3284
  }
3241
- async function detectCLIs(providerLoader) {
3285
+ async function detectCLIs(providerLoader, options) {
3242
3286
  const platform9 = os2.platform();
3243
3287
  const whichCmd = platform9 === "win32" ? "where" : "which";
3288
+ const includeVersion = options?.includeVersion !== false;
3244
3289
  const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
3245
3290
  const results = await Promise.all(
3246
3291
  cliList.map(async (cli) => {
3247
3292
  try {
3248
- const pathResult = await execAsync(`${whichCmd} ${cli.command}`);
3293
+ const explicitPath = resolveCommandPath(cli.command);
3294
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
3249
3295
  if (!pathResult) return { ...cli, installed: false };
3250
- const firstPath = pathResult.split("\n")[0];
3296
+ const firstPath = explicitPath || pathResult.split("\n")[0];
3251
3297
  let version;
3252
- try {
3298
+ if (includeVersion) {
3253
3299
  const versionCommands = [
3254
- cli.versionCommand,
3255
- `${cli.command} --version`,
3256
- `${cli.command} -V`,
3257
- `${cli.command} -v`
3300
+ `"${firstPath}" --version`,
3301
+ `"${firstPath}" -V`,
3302
+ `"${firstPath}" -v`,
3303
+ cli.versionCommand
3258
3304
  ].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;
3305
+ try {
3306
+ for (const versionCommand of versionCommands) {
3307
+ const versionResult = await execAsync(versionCommand, 3e3);
3308
+ if (versionResult) {
3309
+ version = parseVersion(versionResult);
3310
+ break;
3311
+ }
3264
3312
  }
3313
+ } catch {
3265
3314
  }
3266
- } catch {
3267
3315
  }
3268
3316
  return { ...cli, installed: true, version, path: firstPath };
3269
3317
  } catch {
@@ -3273,7 +3321,7 @@ async function detectCLIs(providerLoader) {
3273
3321
  );
3274
3322
  return results;
3275
3323
  }
3276
- async function detectCLI(cliId, providerLoader) {
3324
+ async function detectCLI(cliId, providerLoader, options) {
3277
3325
  const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
3278
3326
  if (providerLoader) {
3279
3327
  const cliList = providerLoader.getCliDetectionList();
@@ -3282,25 +3330,28 @@ async function detectCLI(cliId, providerLoader) {
3282
3330
  const platform9 = os2.platform();
3283
3331
  const whichCmd = platform9 === "win32" ? "where" : "which";
3284
3332
  try {
3285
- const pathResult = await execAsync(`${whichCmd} ${target.command}`);
3333
+ const explicitPath = resolveCommandPath(target.command);
3334
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
3286
3335
  if (!pathResult) return null;
3287
- const firstPath = pathResult.split("\n")[0];
3336
+ const firstPath = explicitPath || pathResult.split("\n")[0];
3288
3337
  let version;
3289
- try {
3338
+ if (options?.includeVersion !== false) {
3290
3339
  const versionCommands = [
3291
- target.versionCommand,
3292
- `${target.command} --version`,
3293
- `${target.command} -V`,
3294
- `${target.command} -v`
3340
+ `"${firstPath}" --version`,
3341
+ `"${firstPath}" -V`,
3342
+ `"${firstPath}" -v`,
3343
+ target.versionCommand
3295
3344
  ].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;
3345
+ try {
3346
+ for (const versionCommand of versionCommands) {
3347
+ const versionResult = await execAsync(versionCommand, 3e3);
3348
+ if (versionResult) {
3349
+ version = parseVersion(versionResult);
3350
+ break;
3351
+ }
3301
3352
  }
3353
+ } catch {
3302
3354
  }
3303
- } catch {
3304
3355
  }
3305
3356
  return { ...target, installed: true, version, path: firstPath };
3306
3357
  } catch {
@@ -3308,7 +3359,7 @@ async function detectCLI(cliId, providerLoader) {
3308
3359
  }
3309
3360
  }
3310
3361
  }
3311
- const all = await detectCLIs(providerLoader);
3362
+ const all = await detectCLIs(providerLoader, options);
3312
3363
  return all.find((c) => c.id === resolvedId && c.installed) || null;
3313
3364
  }
3314
3365
 
@@ -3435,7 +3486,7 @@ var DaemonCdpManager = class {
3435
3486
  * Returns multiple entries if multiple IDE windows are open on same port
3436
3487
  */
3437
3488
  static listAllTargets(port) {
3438
- return new Promise((resolve9) => {
3489
+ return new Promise((resolve12) => {
3439
3490
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
3440
3491
  let data = "";
3441
3492
  res.on("data", (chunk) => data += chunk.toString());
@@ -3451,16 +3502,16 @@ var DaemonCdpManager = class {
3451
3502
  (t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
3452
3503
  );
3453
3504
  const fallbackPages = pages.filter((t) => !isNonMain(t.title || ""));
3454
- resolve9(mainPages.length > 0 ? mainPages : fallbackPages);
3505
+ resolve12(mainPages.length > 0 ? mainPages : fallbackPages);
3455
3506
  } catch {
3456
- resolve9([]);
3507
+ resolve12([]);
3457
3508
  }
3458
3509
  });
3459
3510
  });
3460
- req.on("error", () => resolve9([]));
3511
+ req.on("error", () => resolve12([]));
3461
3512
  req.setTimeout(2e3, () => {
3462
3513
  req.destroy();
3463
- resolve9([]);
3514
+ resolve12([]);
3464
3515
  });
3465
3516
  });
3466
3517
  }
@@ -3500,7 +3551,7 @@ var DaemonCdpManager = class {
3500
3551
  }
3501
3552
  }
3502
3553
  findTargetOnPort(port) {
3503
- return new Promise((resolve9) => {
3554
+ return new Promise((resolve12) => {
3504
3555
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
3505
3556
  let data = "";
3506
3557
  res.on("data", (chunk) => data += chunk.toString());
@@ -3511,7 +3562,7 @@ var DaemonCdpManager = class {
3511
3562
  (t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
3512
3563
  );
3513
3564
  if (pages.length === 0) {
3514
- resolve9(targets.find((t) => t.webSocketDebuggerUrl) || null);
3565
+ resolve12(targets.find((t) => t.webSocketDebuggerUrl) || null);
3515
3566
  return;
3516
3567
  }
3517
3568
  const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
@@ -3521,24 +3572,24 @@ var DaemonCdpManager = class {
3521
3572
  const specific = list.find((t) => t.id === this._targetId);
3522
3573
  if (specific) {
3523
3574
  this._pageTitle = specific.title || "";
3524
- resolve9(specific);
3575
+ resolve12(specific);
3525
3576
  } else {
3526
3577
  this.log(`[CDP] Target ${this._targetId} not found in page list`);
3527
- resolve9(null);
3578
+ resolve12(null);
3528
3579
  }
3529
3580
  return;
3530
3581
  }
3531
3582
  this._pageTitle = list[0]?.title || "";
3532
- resolve9(list[0]);
3583
+ resolve12(list[0]);
3533
3584
  } catch {
3534
- resolve9(null);
3585
+ resolve12(null);
3535
3586
  }
3536
3587
  });
3537
3588
  });
3538
- req.on("error", () => resolve9(null));
3589
+ req.on("error", () => resolve12(null));
3539
3590
  req.setTimeout(2e3, () => {
3540
3591
  req.destroy();
3541
- resolve9(null);
3592
+ resolve12(null);
3542
3593
  });
3543
3594
  });
3544
3595
  }
@@ -3549,7 +3600,7 @@ var DaemonCdpManager = class {
3549
3600
  this.extensionProviders = providers;
3550
3601
  }
3551
3602
  connectToTarget(wsUrl) {
3552
- return new Promise((resolve9) => {
3603
+ return new Promise((resolve12) => {
3553
3604
  this.ws = new WebSocket(wsUrl);
3554
3605
  this.ws.on("open", async () => {
3555
3606
  this._connected = true;
@@ -3559,17 +3610,17 @@ var DaemonCdpManager = class {
3559
3610
  }
3560
3611
  this.connectBrowserWs().catch(() => {
3561
3612
  });
3562
- resolve9(true);
3613
+ resolve12(true);
3563
3614
  });
3564
3615
  this.ws.on("message", (data) => {
3565
3616
  try {
3566
3617
  const msg = JSON.parse(data.toString());
3567
3618
  if (msg.id && this.pending.has(msg.id)) {
3568
- const { resolve: resolve10, reject } = this.pending.get(msg.id);
3619
+ const { resolve: resolve13, reject } = this.pending.get(msg.id);
3569
3620
  this.pending.delete(msg.id);
3570
3621
  this.failureCount = 0;
3571
3622
  if (msg.error) reject(new Error(msg.error.message));
3572
- else resolve10(msg.result);
3623
+ else resolve13(msg.result);
3573
3624
  } else if (msg.method === "Runtime.executionContextCreated") {
3574
3625
  this.contexts.add(msg.params.context.id);
3575
3626
  } else if (msg.method === "Runtime.executionContextDestroyed") {
@@ -3592,7 +3643,7 @@ var DaemonCdpManager = class {
3592
3643
  this.ws.on("error", (err) => {
3593
3644
  this.log(`[CDP] WebSocket error: ${err.message}`);
3594
3645
  this._connected = false;
3595
- resolve9(false);
3646
+ resolve12(false);
3596
3647
  });
3597
3648
  });
3598
3649
  }
@@ -3606,7 +3657,7 @@ var DaemonCdpManager = class {
3606
3657
  return;
3607
3658
  }
3608
3659
  this.log(`[CDP] Connecting browser WS for target discovery...`);
3609
- await new Promise((resolve9, reject) => {
3660
+ await new Promise((resolve12, reject) => {
3610
3661
  this.browserWs = new WebSocket(browserWsUrl);
3611
3662
  this.browserWs.on("open", async () => {
3612
3663
  this._browserConnected = true;
@@ -3616,16 +3667,16 @@ var DaemonCdpManager = class {
3616
3667
  } catch (e) {
3617
3668
  this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
3618
3669
  }
3619
- resolve9();
3670
+ resolve12();
3620
3671
  });
3621
3672
  this.browserWs.on("message", (data) => {
3622
3673
  try {
3623
3674
  const msg = JSON.parse(data.toString());
3624
3675
  if (msg.id && this.browserPending.has(msg.id)) {
3625
- const { resolve: resolve10, reject: reject2 } = this.browserPending.get(msg.id);
3676
+ const { resolve: resolve13, reject: reject2 } = this.browserPending.get(msg.id);
3626
3677
  this.browserPending.delete(msg.id);
3627
3678
  if (msg.error) reject2(new Error(msg.error.message));
3628
- else resolve10(msg.result);
3679
+ else resolve13(msg.result);
3629
3680
  }
3630
3681
  } catch {
3631
3682
  }
@@ -3645,31 +3696,31 @@ var DaemonCdpManager = class {
3645
3696
  }
3646
3697
  }
3647
3698
  getBrowserWsUrl() {
3648
- return new Promise((resolve9) => {
3699
+ return new Promise((resolve12) => {
3649
3700
  const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
3650
3701
  let data = "";
3651
3702
  res.on("data", (chunk) => data += chunk.toString());
3652
3703
  res.on("end", () => {
3653
3704
  try {
3654
3705
  const info = JSON.parse(data);
3655
- resolve9(info.webSocketDebuggerUrl || null);
3706
+ resolve12(info.webSocketDebuggerUrl || null);
3656
3707
  } catch {
3657
- resolve9(null);
3708
+ resolve12(null);
3658
3709
  }
3659
3710
  });
3660
3711
  });
3661
- req.on("error", () => resolve9(null));
3712
+ req.on("error", () => resolve12(null));
3662
3713
  req.setTimeout(3e3, () => {
3663
3714
  req.destroy();
3664
- resolve9(null);
3715
+ resolve12(null);
3665
3716
  });
3666
3717
  });
3667
3718
  }
3668
3719
  sendBrowser(method, params = {}, timeoutMs = 15e3) {
3669
- return new Promise((resolve9, reject) => {
3720
+ return new Promise((resolve12, reject) => {
3670
3721
  if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
3671
3722
  const id = this.browserMsgId++;
3672
- this.browserPending.set(id, { resolve: resolve9, reject });
3723
+ this.browserPending.set(id, { resolve: resolve12, reject });
3673
3724
  this.browserWs.send(JSON.stringify({ id, method, params }));
3674
3725
  setTimeout(() => {
3675
3726
  if (this.browserPending.has(id)) {
@@ -3709,11 +3760,11 @@ var DaemonCdpManager = class {
3709
3760
  }
3710
3761
  // ─── CDP Protocol ────────────────────────────────────────
3711
3762
  sendInternal(method, params = {}, timeoutMs = 15e3) {
3712
- return new Promise((resolve9, reject) => {
3763
+ return new Promise((resolve12, reject) => {
3713
3764
  if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
3714
3765
  if (this.ws.readyState !== WebSocket.OPEN) return reject(new Error("WebSocket not open"));
3715
3766
  const id = this.msgId++;
3716
- this.pending.set(id, { resolve: resolve9, reject });
3767
+ this.pending.set(id, { resolve: resolve12, reject });
3717
3768
  this.ws.send(JSON.stringify({ id, method, params }));
3718
3769
  setTimeout(() => {
3719
3770
  if (this.pending.has(id)) {
@@ -3962,7 +4013,7 @@ var DaemonCdpManager = class {
3962
4013
  const browserWs = this.browserWs;
3963
4014
  let msgId = this.browserMsgId;
3964
4015
  const sendWs = (method, params = {}, sessionId) => {
3965
- return new Promise((resolve9, reject) => {
4016
+ return new Promise((resolve12, reject) => {
3966
4017
  const mid = msgId++;
3967
4018
  this.browserMsgId = msgId;
3968
4019
  const handler = (raw) => {
@@ -3971,7 +4022,7 @@ var DaemonCdpManager = class {
3971
4022
  if (msg.id === mid) {
3972
4023
  browserWs.removeListener("message", handler);
3973
4024
  if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
3974
- else resolve9(msg.result);
4025
+ else resolve12(msg.result);
3975
4026
  }
3976
4027
  } catch {
3977
4028
  }
@@ -4162,14 +4213,14 @@ var DaemonCdpManager = class {
4162
4213
  if (!ws || ws.readyState !== WebSocket.OPEN) {
4163
4214
  throw new Error("CDP not connected");
4164
4215
  }
4165
- return new Promise((resolve9, reject) => {
4216
+ return new Promise((resolve12, reject) => {
4166
4217
  const id = getNextId();
4167
4218
  pendingMap.set(id, {
4168
4219
  resolve: (result) => {
4169
4220
  if (result?.result?.subtype === "error") {
4170
4221
  reject(new Error(result.result.description));
4171
4222
  } else {
4172
- resolve9(result?.result?.value);
4223
+ resolve12(result?.result?.value);
4173
4224
  }
4174
4225
  },
4175
4226
  reject
@@ -4201,10 +4252,10 @@ var DaemonCdpManager = class {
4201
4252
  throw new Error("CDP not connected");
4202
4253
  }
4203
4254
  const sendViaSession = (method, params = {}) => {
4204
- return new Promise((resolve9, reject) => {
4255
+ return new Promise((resolve12, reject) => {
4205
4256
  const pendingMap = this._browserConnected ? this.browserPending : this.pending;
4206
4257
  const id = this._browserConnected ? this.browserMsgId++ : this.msgId++;
4207
- pendingMap.set(id, { resolve: resolve9, reject });
4258
+ pendingMap.set(id, { resolve: resolve12, reject });
4208
4259
  ws.send(JSON.stringify({ id, sessionId, method, params }));
4209
4260
  setTimeout(() => {
4210
4261
  if (pendingMap.has(id)) {
@@ -4832,15 +4883,76 @@ function normalizeControlValue(value) {
4832
4883
 
4833
4884
  // src/config/chat-history.ts
4834
4885
  import * as fs3 from "fs";
4835
- import * as path5 from "path";
4886
+ import * as path7 from "path";
4836
4887
  import * as os5 from "os";
4837
- var HISTORY_DIR = path5.join(os5.homedir(), ".adhdev", "history");
4888
+ var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
4838
4889
  var RETAIN_DAYS = 30;
4890
+ var CODEX_STARTER_PROMPT_RE = /^(?:[›❯]\s*)?(?:Find and fix a bug in @filename|Improve documentation in @filename|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Use \/skills(?: to list available skills)?|Run \/review on my current changes)$/i;
4891
+ function normalizeHistoryComparable(text) {
4892
+ return String(text || "").replace(/\s+/g, " ").trim();
4893
+ }
4894
+ function cleanupHistoryContent(agentType, role, content) {
4895
+ let value = String(content || "").replace(/\r\n/g, "\n").trim();
4896
+ if (!value) return "";
4897
+ if (agentType === "codex-cli" && role === "assistant") {
4898
+ const filtered = value.split("\n").filter((line) => !CODEX_STARTER_PROMPT_RE.test(line.trim())).join("\n").replace(/\n{3,}/g, "\n\n").trim();
4899
+ value = filtered;
4900
+ }
4901
+ return value;
4902
+ }
4903
+ function buildHistoryMessageHash(agentType, message) {
4904
+ if (message.historyDedupKey) return message.historyDedupKey;
4905
+ const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
4906
+ return `${message.kind || "standard"}:${message.role}:${message.receivedAt || 0}:${normalizeHistoryComparable(cleaned)}`;
4907
+ }
4908
+ function buildHistoryMessageSignature(agentType, message) {
4909
+ const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
4910
+ return `${message.kind || "standard"}:${message.role}:${normalizeHistoryComparable(cleaned)}`;
4911
+ }
4912
+ function isAdjacentHistoryDuplicate(agentType, previous, next) {
4913
+ if (!previous || !next) return false;
4914
+ return buildHistoryMessageSignature(agentType, previous) === buildHistoryMessageSignature(agentType, next);
4915
+ }
4916
+ function collapseReplayAssistantTurns(agentType, messages) {
4917
+ if (agentType !== "codex-cli") return messages;
4918
+ const collapsed = [];
4919
+ let sawAssistantSinceLastUser = false;
4920
+ for (const message of messages) {
4921
+ if (message.role === "user") {
4922
+ sawAssistantSinceLastUser = false;
4923
+ collapsed.push(message);
4924
+ continue;
4925
+ }
4926
+ if (message.role === "assistant") {
4927
+ if (sawAssistantSinceLastUser) continue;
4928
+ sawAssistantSinceLastUser = true;
4929
+ collapsed.push(message);
4930
+ continue;
4931
+ }
4932
+ collapsed.push(message);
4933
+ }
4934
+ return collapsed;
4935
+ }
4936
+ function sanitizeHistoryMessage(agentType, message) {
4937
+ if (!message || message.role !== "user" && message.role !== "assistant" && message.role !== "system") {
4938
+ return null;
4939
+ }
4940
+ const content = cleanupHistoryContent(agentType, message.role, message.content);
4941
+ if (!content) return null;
4942
+ return {
4943
+ ...message,
4944
+ content
4945
+ };
4946
+ }
4839
4947
  var ChatHistoryWriter = class {
4840
4948
  /** Last seen message count per agent (deduplication) */
4841
4949
  lastSeenCounts = /* @__PURE__ */ new Map();
4842
4950
  /** Last seen message hash per agent (deduplication) */
4843
4951
  lastSeenHashes = /* @__PURE__ */ new Map();
4952
+ /** Last appended normalized message signature per agent/session */
4953
+ lastSeenSignatures = /* @__PURE__ */ new Map();
4954
+ /** Last appended normalized non-system turn signature per agent/session */
4955
+ lastSeenTurnSignatures = /* @__PURE__ */ new Map();
4844
4956
  rotated = false;
4845
4957
  /**
4846
4958
  * Append new messages to history
@@ -4862,14 +4974,36 @@ var ChatHistoryWriter = class {
4862
4974
  }
4863
4975
  const newMessages = [];
4864
4976
  for (const msg of messages) {
4865
- const hash = msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`;
4977
+ const role = msg.role;
4978
+ if (role !== "user" && role !== "assistant" && role !== "system") continue;
4979
+ const content = cleanupHistoryContent(agentType, role, msg.content || "");
4980
+ if (!content) continue;
4981
+ const receivedAt = msg.receivedAt || Date.now();
4982
+ const hash = buildHistoryMessageHash(agentType, {
4983
+ role,
4984
+ content,
4985
+ receivedAt,
4986
+ kind: typeof msg.kind === "string" ? msg.kind : void 0,
4987
+ historyDedupKey: msg.historyDedupKey
4988
+ });
4989
+ const signature = buildHistoryMessageSignature(agentType, {
4990
+ role,
4991
+ content,
4992
+ kind: typeof msg.kind === "string" ? msg.kind : void 0
4993
+ });
4866
4994
  if (seenHashes.has(hash)) continue;
4995
+ if (this.lastSeenSignatures.get(dedupKey) === signature) continue;
4996
+ if (role !== "system" && this.lastSeenTurnSignatures.get(dedupKey) === signature) continue;
4867
4997
  seenHashes.add(hash);
4998
+ this.lastSeenSignatures.set(dedupKey, signature);
4999
+ if (role !== "system") {
5000
+ this.lastSeenTurnSignatures.set(dedupKey, signature);
5001
+ }
4868
5002
  newMessages.push({
4869
- ts: new Date(msg.receivedAt || Date.now()).toISOString(),
4870
- receivedAt: msg.receivedAt || Date.now(),
4871
- role: msg.role,
4872
- content: msg.content || "",
5003
+ ts: new Date(receivedAt).toISOString(),
5004
+ receivedAt,
5005
+ role,
5006
+ content,
4873
5007
  kind: typeof msg.kind === "string" ? msg.kind : void 0,
4874
5008
  senderName: typeof msg.senderName === "string" ? msg.senderName : void 0,
4875
5009
  agent: agentType,
@@ -4879,16 +5013,18 @@ var ChatHistoryWriter = class {
4879
5013
  });
4880
5014
  }
4881
5015
  if (newMessages.length === 0) return;
4882
- const dir = path5.join(HISTORY_DIR, this.sanitize(agentType));
5016
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
4883
5017
  fs3.mkdirSync(dir, { recursive: true });
4884
5018
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4885
5019
  const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
4886
- const filePath = path5.join(dir, `${filePrefix}${date}.jsonl`);
5020
+ const filePath = path7.join(dir, `${filePrefix}${date}.jsonl`);
4887
5021
  const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
4888
5022
  fs3.appendFileSync(filePath, lines, "utf-8");
4889
5023
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
4890
5024
  if (messages.length < prevCount * 0.5 && prevCount > 3) {
4891
5025
  seenHashes.clear();
5026
+ this.lastSeenSignatures.delete(dedupKey);
5027
+ this.lastSeenTurnSignatures.delete(dedupKey);
4892
5028
  for (const msg of messages) {
4893
5029
  seenHashes.add(msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`);
4894
5030
  }
@@ -4902,6 +5038,54 @@ var ChatHistoryWriter = class {
4902
5038
  } catch {
4903
5039
  }
4904
5040
  }
5041
+ seedSessionHistory(agentType, messages = [], historySessionId, instanceId) {
5042
+ const effectiveHistoryKey = historySessionId || instanceId;
5043
+ const dedupKey = effectiveHistoryKey ? `${agentType}:${effectiveHistoryKey}` : agentType;
5044
+ const seenHashes = /* @__PURE__ */ new Set();
5045
+ for (const raw of messages) {
5046
+ const role = raw?.role;
5047
+ if (role !== "user" && role !== "assistant" && role !== "system") continue;
5048
+ const content = cleanupHistoryContent(agentType, role, raw?.content || "");
5049
+ if (!content) continue;
5050
+ seenHashes.add(buildHistoryMessageHash(agentType, {
5051
+ role,
5052
+ content,
5053
+ receivedAt: raw?.receivedAt || 0,
5054
+ kind: typeof raw?.kind === "string" ? raw.kind : void 0,
5055
+ historyDedupKey: raw?.historyDedupKey
5056
+ }));
5057
+ }
5058
+ this.lastSeenHashes.set(dedupKey, seenHashes);
5059
+ this.lastSeenCounts.set(dedupKey, messages.length);
5060
+ const lastMessage = [...messages].reverse().find((raw) => {
5061
+ const role = raw?.role;
5062
+ if (role !== "user" && role !== "assistant" && role !== "system") return false;
5063
+ return !!cleanupHistoryContent(agentType, role, raw?.content || "");
5064
+ });
5065
+ const lastTurnMessage = [...messages].reverse().find((raw) => {
5066
+ const role = raw?.role;
5067
+ if (role !== "user" && role !== "assistant") return false;
5068
+ return !!cleanupHistoryContent(agentType, role, raw?.content || "");
5069
+ });
5070
+ if (lastMessage) {
5071
+ this.lastSeenSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
5072
+ role: lastMessage.role,
5073
+ content: lastMessage.content,
5074
+ kind: typeof lastMessage.kind === "string" ? lastMessage.kind : void 0
5075
+ }));
5076
+ } else {
5077
+ this.lastSeenSignatures.delete(dedupKey);
5078
+ }
5079
+ if (lastTurnMessage) {
5080
+ this.lastSeenTurnSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
5081
+ role: lastTurnMessage.role,
5082
+ content: lastTurnMessage.content,
5083
+ kind: typeof lastTurnMessage.kind === "string" ? lastTurnMessage.kind : void 0
5084
+ }));
5085
+ } else {
5086
+ this.lastSeenTurnSignatures.delete(dedupKey);
5087
+ }
5088
+ }
4905
5089
  appendSystemMarker(agentType, content, options = {}) {
4906
5090
  this.appendNewMessages(
4907
5091
  agentType,
@@ -4932,19 +5116,29 @@ var ChatHistoryWriter = class {
4932
5116
  this.lastSeenHashes.set(toDedupKey, nextHashes);
4933
5117
  this.lastSeenHashes.delete(fromDedupKey);
4934
5118
  }
5119
+ const fromSignature = this.lastSeenSignatures.get(fromDedupKey);
5120
+ if (fromSignature) {
5121
+ this.lastSeenSignatures.set(toDedupKey, fromSignature);
5122
+ this.lastSeenSignatures.delete(fromDedupKey);
5123
+ }
5124
+ const fromTurnSignature = this.lastSeenTurnSignatures.get(fromDedupKey);
5125
+ if (fromTurnSignature) {
5126
+ this.lastSeenTurnSignatures.set(toDedupKey, fromTurnSignature);
5127
+ this.lastSeenTurnSignatures.delete(fromDedupKey);
5128
+ }
4935
5129
  const fromCount = this.lastSeenCounts.get(fromDedupKey);
4936
5130
  if (typeof fromCount === "number") {
4937
5131
  this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
4938
5132
  this.lastSeenCounts.delete(fromDedupKey);
4939
5133
  }
4940
- const dir = path5.join(HISTORY_DIR, this.sanitize(agentType));
5134
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
4941
5135
  if (!fs3.existsSync(dir)) return;
4942
5136
  const fromPrefix = `${this.sanitize(fromId)}_`;
4943
5137
  const toPrefix = `${this.sanitize(toId)}_`;
4944
5138
  const files = fs3.readdirSync(dir).filter((file) => file.startsWith(fromPrefix) && file.endsWith(".jsonl"));
4945
5139
  for (const file of files) {
4946
- const sourcePath = path5.join(dir, file);
4947
- const targetPath = path5.join(dir, `${toPrefix}${file.slice(fromPrefix.length)}`);
5140
+ const sourcePath = path7.join(dir, file);
5141
+ const targetPath = path7.join(dir, `${toPrefix}${file.slice(fromPrefix.length)}`);
4948
5142
  const sourceLines = fs3.readFileSync(sourcePath, "utf-8").split("\n").filter(Boolean);
4949
5143
  const rewritten = sourceLines.map((line) => {
4950
5144
  try {
@@ -4973,10 +5167,61 @@ var ChatHistoryWriter = class {
4973
5167
  } catch {
4974
5168
  }
4975
5169
  }
5170
+ compactHistorySession(agentType, historySessionId) {
5171
+ const sessionId = String(historySessionId || "").trim();
5172
+ if (!sessionId) return;
5173
+ try {
5174
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
5175
+ if (!fs3.existsSync(dir)) return;
5176
+ const prefix = `${this.sanitize(sessionId)}_`;
5177
+ const files = fs3.readdirSync(dir).filter((file) => file.startsWith(prefix) && file.endsWith(".jsonl")).sort();
5178
+ const seen = /* @__PURE__ */ new Set();
5179
+ for (const file of files) {
5180
+ const filePath = path7.join(dir, file);
5181
+ const lines = fs3.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
5182
+ const next = [];
5183
+ for (const line of lines) {
5184
+ let parsed = null;
5185
+ try {
5186
+ parsed = JSON.parse(line);
5187
+ } catch {
5188
+ parsed = null;
5189
+ }
5190
+ if (!parsed || parsed.historySessionId !== sessionId) continue;
5191
+ const sanitized = sanitizeHistoryMessage(agentType, parsed);
5192
+ if (!sanitized) continue;
5193
+ const hash = buildHistoryMessageHash(agentType, sanitized);
5194
+ if (seen.has(hash)) continue;
5195
+ seen.add(hash);
5196
+ next.push(sanitized);
5197
+ }
5198
+ next.sort((a, b) => a.receivedAt - b.receivedAt);
5199
+ const dedupedAdjacent = [];
5200
+ let lastTurn = null;
5201
+ for (const entry of next) {
5202
+ const previous = dedupedAdjacent[dedupedAdjacent.length - 1];
5203
+ if (isAdjacentHistoryDuplicate(agentType, previous, entry)) continue;
5204
+ if (entry.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, entry)) continue;
5205
+ dedupedAdjacent.push(entry);
5206
+ if (entry.role !== "system") lastTurn = entry;
5207
+ }
5208
+ const collapsed = collapseReplayAssistantTurns(agentType, dedupedAdjacent);
5209
+ if (collapsed.length === 0) {
5210
+ fs3.unlinkSync(filePath);
5211
+ continue;
5212
+ }
5213
+ fs3.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
5214
+ `, "utf-8");
5215
+ }
5216
+ } catch {
5217
+ }
5218
+ }
4976
5219
  /** Called when agent session is explicitly changed */
4977
5220
  onSessionChange(agentType) {
4978
5221
  this.lastSeenHashes.delete(agentType);
4979
5222
  this.lastSeenCounts.delete(agentType);
5223
+ this.lastSeenSignatures.delete(agentType);
5224
+ this.lastSeenTurnSignatures.delete(agentType);
4980
5225
  }
4981
5226
  /** Delete history files older than 30 days */
4982
5227
  async rotateOldFiles() {
@@ -4985,10 +5230,10 @@ var ChatHistoryWriter = class {
4985
5230
  const cutoff = Date.now() - RETAIN_DAYS * 24 * 60 * 60 * 1e3;
4986
5231
  const agentDirs = fs3.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
4987
5232
  for (const dir of agentDirs) {
4988
- const dirPath = path5.join(HISTORY_DIR, dir.name);
5233
+ const dirPath = path7.join(HISTORY_DIR, dir.name);
4989
5234
  const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
4990
5235
  for (const file of files) {
4991
- const filePath = path5.join(dirPath, file);
5236
+ const filePath = path7.join(dirPath, file);
4992
5237
  const stat = fs3.statSync(filePath);
4993
5238
  if (stat.mtimeMs < cutoff) {
4994
5239
  fs3.unlinkSync(filePath);
@@ -5006,7 +5251,7 @@ var ChatHistoryWriter = class {
5006
5251
  function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
5007
5252
  try {
5008
5253
  const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
5009
- const dir = path5.join(HISTORY_DIR, sanitized);
5254
+ const dir = path7.join(HISTORY_DIR, sanitized);
5010
5255
  if (!fs3.existsSync(dir)) return { messages: [], hasMore: false };
5011
5256
  const sanitizedInstance = historySessionId?.replace(/[^a-zA-Z0-9_-]/g, "_");
5012
5257
  const files = fs3.readdirSync(dir).filter((f) => {
@@ -5017,23 +5262,37 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
5017
5262
  return true;
5018
5263
  }).sort().reverse();
5019
5264
  const allMessages = [];
5020
- const needed = offset + limit + 1;
5265
+ const seen = /* @__PURE__ */ new Set();
5021
5266
  for (const file of files) {
5022
- if (allMessages.length >= needed) break;
5023
- const filePath = path5.join(dir, file);
5267
+ const filePath = path7.join(dir, file);
5024
5268
  const content = fs3.readFileSync(filePath, "utf-8");
5025
5269
  const lines = content.trim().split("\n").filter(Boolean);
5026
- for (let i = lines.length - 1; i >= 0; i--) {
5027
- if (allMessages.length >= needed) break;
5270
+ for (let i = 0; i < lines.length; i++) {
5028
5271
  try {
5029
- allMessages.push(JSON.parse(lines[i]));
5272
+ const parsed = JSON.parse(lines[i]);
5273
+ const sanitizedMessage = sanitizeHistoryMessage(agentType, parsed);
5274
+ if (!sanitizedMessage) continue;
5275
+ const hash = buildHistoryMessageHash(agentType, sanitizedMessage);
5276
+ if (seen.has(hash)) continue;
5277
+ seen.add(hash);
5278
+ allMessages.push(sanitizedMessage);
5030
5279
  } catch {
5031
5280
  }
5032
5281
  }
5033
5282
  }
5034
- const sliced = allMessages.slice(offset, offset + limit);
5035
- const hasMore = allMessages.length > offset + limit;
5036
- sliced.reverse();
5283
+ allMessages.sort((a, b) => a.receivedAt - b.receivedAt);
5284
+ const chronological = [];
5285
+ let lastTurn = null;
5286
+ for (const message of allMessages) {
5287
+ const previous = chronological[chronological.length - 1];
5288
+ if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
5289
+ if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
5290
+ chronological.push(message);
5291
+ if (message.role !== "system") lastTurn = message;
5292
+ }
5293
+ const collapsed = collapseReplayAssistantTurns(agentType, chronological);
5294
+ const sliced = collapsed.slice(offset, offset + limit);
5295
+ const hasMore = collapsed.length > offset + limit;
5037
5296
  return { messages: sliced, hasMore };
5038
5297
  } catch {
5039
5298
  return { messages: [], hasMore: false };
@@ -5042,7 +5301,7 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
5042
5301
  function listSavedHistorySessions(agentType, options = {}) {
5043
5302
  try {
5044
5303
  const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
5045
- const dir = path5.join(HISTORY_DIR, sanitized);
5304
+ const dir = path7.join(HISTORY_DIR, sanitized);
5046
5305
  if (!fs3.existsSync(dir)) return { sessions: [], hasMore: false };
5047
5306
  const groupedFiles = /* @__PURE__ */ new Map();
5048
5307
  const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
@@ -5063,7 +5322,7 @@ function listSavedHistorySessions(agentType, options = {}) {
5063
5322
  let sessionTitle = "";
5064
5323
  let preview = "";
5065
5324
  for (const file of files.sort()) {
5066
- const filePath = path5.join(dir, file);
5325
+ const filePath = path7.join(dir, file);
5067
5326
  const content = fs3.readFileSync(filePath, "utf-8");
5068
5327
  const lines = content.split("\n").filter(Boolean);
5069
5328
  for (const line of lines) {
@@ -6887,6 +7146,46 @@ function didProviderConfirmSend(result) {
6887
7146
  if (!parsed || typeof parsed !== "object") return false;
6888
7147
  return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
6889
7148
  }
7149
+ async function readExtensionChatState(h) {
7150
+ try {
7151
+ const evalResult = await h.evaluateProviderScript("readChat", void 0, 5e4);
7152
+ if (!evalResult?.result) return null;
7153
+ const parsed = parseMaybeJson(evalResult.result);
7154
+ return parsed && typeof parsed === "object" ? parsed : null;
7155
+ } catch {
7156
+ return null;
7157
+ }
7158
+ }
7159
+ function getStateMessageCount(state) {
7160
+ return Array.isArray(state?.messages) ? state.messages.length : 0;
7161
+ }
7162
+ function getStateLastSignature(state) {
7163
+ const messages = Array.isArray(state?.messages) ? state.messages : [];
7164
+ const last = messages[messages.length - 1];
7165
+ if (!last) return "";
7166
+ return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
7167
+ }
7168
+ async function getStableExtensionBaseline(h) {
7169
+ const first = await readExtensionChatState(h);
7170
+ if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
7171
+ await new Promise((resolve12) => setTimeout(resolve12, 150));
7172
+ const second = await readExtensionChatState(h);
7173
+ return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
7174
+ }
7175
+ async function verifyExtensionSendObserved(h, before) {
7176
+ const beforeCount = getStateMessageCount(before);
7177
+ const beforeSignature = getStateLastSignature(before);
7178
+ for (let attempt = 0; attempt < 12; attempt += 1) {
7179
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
7180
+ const state = await readExtensionChatState(h);
7181
+ if (state?.status === "waiting_approval") return true;
7182
+ const afterCount = getStateMessageCount(state);
7183
+ const afterSignature = getStateLastSignature(state);
7184
+ if (afterCount > beforeCount) return true;
7185
+ if (afterSignature && afterSignature !== beforeSignature) return true;
7186
+ }
7187
+ return false;
7188
+ }
6890
7189
  async function handleChatHistory(h, args) {
6891
7190
  const { agentType, offset, limit } = args;
6892
7191
  const historySessionId = getHistorySessionId(h, args);
@@ -7067,12 +7366,17 @@ async function handleSendChat(h, args) {
7067
7366
  if (isExtensionTransport(transport)) {
7068
7367
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
7069
7368
  try {
7369
+ const beforeState = await getStableExtensionBaseline(h);
7070
7370
  const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
7071
7371
  if (evalResult?.result) {
7072
7372
  const parsed = parseMaybeJson(evalResult.result);
7073
7373
  if (didProviderConfirmSend(parsed)) {
7074
- _log(`Extension script sent OK`);
7075
- return _logSendSuccess("extension-script");
7374
+ const observed = await verifyExtensionSendObserved(h, beforeState);
7375
+ if (observed) {
7376
+ _log(`Extension script sent OK`);
7377
+ return _logSendSuccess("extension-script");
7378
+ }
7379
+ _log(`Extension script reported send but no chat-state change was observed`);
7076
7380
  }
7077
7381
  if (parsed?.needsTypeAndSend) {
7078
7382
  _log(`Extension needsTypeAndSend \u2192 AgentStreamManager`);
@@ -7238,6 +7542,14 @@ async function handleListChats(h, args) {
7238
7542
  } catch {
7239
7543
  }
7240
7544
  }
7545
+ if (parsed?.sessions && Array.isArray(parsed.sessions)) {
7546
+ LOG.info("Command", `[list_chats] OK: ${parsed.sessions.length} chats`);
7547
+ return { success: true, chats: parsed.sessions };
7548
+ }
7549
+ if (parsed?.chats && Array.isArray(parsed.chats)) {
7550
+ LOG.info("Command", `[list_chats] OK: ${parsed.chats.length} chats`);
7551
+ return { success: true, chats: parsed.chats };
7552
+ }
7241
7553
  if (Array.isArray(parsed)) {
7242
7554
  LOG.info("Command", `[list_chats] OK: ${parsed.length} chats`);
7243
7555
  return { success: true, chats: parsed };
@@ -7307,7 +7619,13 @@ async function handleSwitchChat(h, args) {
7307
7619
  } catch (e) {
7308
7620
  return { success: false, error: `webviewSwitchSession failed: ${e.message}` };
7309
7621
  }
7310
- const script = h.getProviderScript("switchSession", { SESSION_ID: JSON.stringify(sessionId) }) || h.getProviderScript("switch_session", { SESSION_ID: JSON.stringify(sessionId) });
7622
+ const switchParams = {
7623
+ sessionId,
7624
+ title: sessionId,
7625
+ id: sessionId,
7626
+ SESSION_ID: JSON.stringify(sessionId)
7627
+ };
7628
+ const script = h.getProviderScript("switchSession", switchParams) || h.getProviderScript("switch_session", switchParams);
7311
7629
  if (!script) return { success: false, error: "switch_session script not available" };
7312
7630
  try {
7313
7631
  const raw = await cdp.evaluate(script, 15e3);
@@ -7386,8 +7704,8 @@ async function handleSetMode(h, args) {
7386
7704
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
7387
7705
  if (adapter) {
7388
7706
  const acpInstance = adapter._acpInstance;
7389
- if (acpInstance && typeof acpInstance.onEvent === "function") {
7390
- acpInstance.onEvent("set_mode", { mode });
7707
+ if (acpInstance && typeof acpInstance.setMode === "function") {
7708
+ await acpInstance.setMode(mode);
7391
7709
  return { success: true, mode };
7392
7710
  }
7393
7711
  }
@@ -7444,9 +7762,9 @@ async function handleChangeModel(h, args) {
7444
7762
  LOG.info("Command", `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
7445
7763
  if (adapter) {
7446
7764
  const acpInstance = adapter._acpInstance;
7447
- if (acpInstance && typeof acpInstance.onEvent === "function") {
7448
- acpInstance.onEvent("change_model", { model });
7449
- LOG.info("Command", `[change_model] Dispatched change_model event to ACP instance`);
7765
+ if (acpInstance && typeof acpInstance.setConfigOption === "function") {
7766
+ await acpInstance.setConfigOption("model", model);
7767
+ LOG.info("Command", `[change_model] Updated ACP model to ${model}`);
7450
7768
  return { success: true, model };
7451
7769
  }
7452
7770
  }
@@ -7563,9 +7881,21 @@ async function handleResolveAction(h, args) {
7563
7881
  return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
7564
7882
  }
7565
7883
  if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
7566
- const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
7884
+ const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action, button);
7567
7885
  return { success: ok };
7568
7886
  }
7887
+ if (transport === "acp") {
7888
+ const adapter = getTargetedCliAdapter(h, args, provider?.type);
7889
+ const acpInstance = adapter?._acpInstance;
7890
+ if (!acpInstance) return { success: false, error: "ACP instance not found" };
7891
+ try {
7892
+ await acpInstance.resolvePermission(action === "approve" || action === "accept" || action === "always");
7893
+ LOG.info("Command", `[resolveAction] ACP \u2192 ${action}`);
7894
+ return { success: true, action };
7895
+ } catch (e) {
7896
+ return { success: false, error: e?.message || "ACP resolve action failed" };
7897
+ }
7898
+ }
7569
7899
  if (provider?.scripts?.webviewResolveAction || provider?.scripts?.webview_resolve_action) {
7570
7900
  const script = h.getProviderScript("webviewResolveAction", { action, button, buttonText: button }) || h.getProviderScript("webview_resolve_action", { action, button, buttonText: button });
7571
7901
  if (script) {
@@ -7644,7 +7974,7 @@ async function handleResolveAction(h, args) {
7644
7974
 
7645
7975
  // src/commands/cdp-commands.ts
7646
7976
  import * as fs4 from "fs";
7647
- import * as path6 from "path";
7977
+ import * as path8 from "path";
7648
7978
  import * as os6 from "os";
7649
7979
  var KEY_TO_VK = {
7650
7980
  Backspace: 8,
@@ -7896,25 +8226,25 @@ function resolveSafePath(requestedPath) {
7896
8226
  const inputPath = rawPath || ".";
7897
8227
  const home = os6.homedir();
7898
8228
  if (inputPath.startsWith("~")) {
7899
- return path6.resolve(path6.join(home, inputPath.slice(1)));
8229
+ return path8.resolve(path8.join(home, inputPath.slice(1)));
7900
8230
  }
7901
8231
  if (process.platform === "win32") {
7902
8232
  const normalized = normalizeWindowsRequestedPath(inputPath);
7903
- if (path6.win32.isAbsolute(normalized)) {
7904
- return path6.win32.normalize(normalized);
8233
+ if (path8.win32.isAbsolute(normalized)) {
8234
+ return path8.win32.normalize(normalized);
7905
8235
  }
7906
- return path6.win32.resolve(normalized);
8236
+ return path8.win32.resolve(normalized);
7907
8237
  }
7908
- if (path6.isAbsolute(inputPath)) {
7909
- return path6.normalize(inputPath);
8238
+ if (path8.isAbsolute(inputPath)) {
8239
+ return path8.normalize(inputPath);
7910
8240
  }
7911
- return path6.resolve(inputPath);
8241
+ return path8.resolve(inputPath);
7912
8242
  }
7913
8243
  function listDirectoryEntriesSafe(dirPath) {
7914
8244
  const entries = fs4.readdirSync(dirPath, { withFileTypes: true });
7915
8245
  const files = [];
7916
8246
  for (const entry of entries) {
7917
- const entryPath = path6.join(dirPath, entry.name);
8247
+ const entryPath = path8.join(dirPath, entry.name);
7918
8248
  try {
7919
8249
  if (entry.isDirectory()) {
7920
8250
  files.push({ name: entry.name, type: "directory" });
@@ -7953,7 +8283,7 @@ async function handleFileRead(h, args) {
7953
8283
  async function handleFileWrite(h, args) {
7954
8284
  try {
7955
8285
  const filePath = resolveSafePath(args?.path);
7956
- fs4.mkdirSync(path6.dirname(filePath), { recursive: true });
8286
+ fs4.mkdirSync(path8.dirname(filePath), { recursive: true });
7957
8287
  fs4.writeFileSync(filePath, args?.content || "", "utf-8");
7958
8288
  return { success: true, path: filePath };
7959
8289
  } catch (e) {
@@ -8041,7 +8371,7 @@ function handleGetProviderSettings(h, args) {
8041
8371
  }
8042
8372
  return { success: true, settings: allSettings, values: allValues };
8043
8373
  }
8044
- function handleSetProviderSetting(h, args) {
8374
+ async function handleSetProviderSetting(h, args) {
8045
8375
  const loader = h.ctx.providerLoader;
8046
8376
  const { providerType, key, value } = args || {};
8047
8377
  if (!providerType || !key || value === void 0) {
@@ -8054,6 +8384,7 @@ function handleSetProviderSetting(h, args) {
8054
8384
  const updated = h.ctx.instanceManager.updateInstanceSettings(providerType, allSettings);
8055
8385
  LOG.info("Command", `[set_provider_setting] ${providerType}.${key}=${JSON.stringify(value)} \u2192 ${updated} instance(s) updated`);
8056
8386
  }
8387
+ await h.ctx.onProviderSettingChanged?.(providerType, key, value);
8057
8388
  return { success: true, providerType, key, value };
8058
8389
  }
8059
8390
  return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
@@ -8091,10 +8422,10 @@ function getCliScriptCommand(payload) {
8091
8422
  }
8092
8423
  const command = payload.command;
8093
8424
  if (!command || typeof command !== "object") return null;
8094
- if (command.type !== "send_message") return null;
8425
+ if (command.type !== "send_message" && command.type !== "pty_write") return null;
8095
8426
  const text = typeof command.text === "string" ? command.text.trim() : typeof command.message === "string" ? command.message.trim() : "";
8096
8427
  if (!text) return null;
8097
- return { type: "send_message", text };
8428
+ return { type: command.type, text };
8098
8429
  }
8099
8430
  function applyProviderPatch(h, args, payload) {
8100
8431
  if (!payload || typeof payload !== "object") return;
@@ -8135,6 +8466,8 @@ async function executeProviderScript(h, args, scriptName) {
8135
8466
  const cliCommand = getCliScriptCommand(parsed.payload);
8136
8467
  if (cliCommand?.type === "send_message" && cliCommand.text) {
8137
8468
  await adapter.sendMessage(cliCommand.text);
8469
+ } else if (cliCommand?.type === "pty_write" && cliCommand.text && adapter.writeRaw) {
8470
+ adapter.writeRaw(cliCommand.text + "\r");
8138
8471
  }
8139
8472
  applyProviderPatch(h, args, parsed.payload);
8140
8473
  return { success: true, ...parsed.payload && typeof parsed.payload === "object" ? parsed.payload : { result: parsed.payload } };
@@ -8813,7 +9146,7 @@ var DaemonCommandHandler = class {
8813
9146
  try {
8814
9147
  const http3 = await import("http");
8815
9148
  const postData = JSON.stringify(body);
8816
- const result = await new Promise((resolve9, reject) => {
9149
+ const result = await new Promise((resolve12, reject) => {
8817
9150
  const req = http3.request({
8818
9151
  hostname: "127.0.0.1",
8819
9152
  port: 19280,
@@ -8825,9 +9158,9 @@ var DaemonCommandHandler = class {
8825
9158
  res.on("data", (chunk) => data += chunk);
8826
9159
  res.on("end", () => {
8827
9160
  try {
8828
- resolve9(JSON.parse(data));
9161
+ resolve12(JSON.parse(data));
8829
9162
  } catch {
8830
- resolve9({ raw: data });
9163
+ resolve12({ raw: data });
8831
9164
  }
8832
9165
  });
8833
9166
  });
@@ -8845,15 +9178,15 @@ var DaemonCommandHandler = class {
8845
9178
  if (!providerType) return { success: false, error: "providerType required" };
8846
9179
  try {
8847
9180
  const http3 = await import("http");
8848
- const result = await new Promise((resolve9, reject) => {
9181
+ const result = await new Promise((resolve12, reject) => {
8849
9182
  http3.get(`http://127.0.0.1:19280/api/providers/${providerType}/${endpoint}`, (res) => {
8850
9183
  let data = "";
8851
9184
  res.on("data", (chunk) => data += chunk);
8852
9185
  res.on("end", () => {
8853
9186
  try {
8854
- resolve9(JSON.parse(data));
9187
+ resolve12(JSON.parse(data));
8855
9188
  } catch {
8856
- resolve9({ raw: data });
9189
+ resolve12({ raw: data });
8857
9190
  }
8858
9191
  });
8859
9192
  }).on("error", reject);
@@ -8867,7 +9200,7 @@ var DaemonCommandHandler = class {
8867
9200
  try {
8868
9201
  const http3 = await import("http");
8869
9202
  const postData = JSON.stringify(args || {});
8870
- const result = await new Promise((resolve9, reject) => {
9203
+ const result = await new Promise((resolve12, reject) => {
8871
9204
  const req = http3.request({
8872
9205
  hostname: "127.0.0.1",
8873
9206
  port: 19280,
@@ -8879,9 +9212,9 @@ var DaemonCommandHandler = class {
8879
9212
  res.on("data", (chunk) => data += chunk);
8880
9213
  res.on("end", () => {
8881
9214
  try {
8882
- resolve9(JSON.parse(data));
9215
+ resolve12(JSON.parse(data));
8883
9216
  } catch {
8884
- resolve9({ raw: data });
9217
+ resolve12({ raw: data });
8885
9218
  }
8886
9219
  });
8887
9220
  });
@@ -8899,7 +9232,7 @@ var DaemonCommandHandler = class {
8899
9232
  // src/commands/cli-manager.ts
8900
9233
  init_provider_cli_adapter();
8901
9234
  import * as os10 from "os";
8902
- import * as path9 from "path";
9235
+ import * as path11 from "path";
8903
9236
  import * as crypto4 from "crypto";
8904
9237
  import chalk from "chalk";
8905
9238
  init_config();
@@ -8907,7 +9240,7 @@ init_config();
8907
9240
  // src/providers/cli-provider-instance.ts
8908
9241
  init_provider_cli_adapter();
8909
9242
  import * as os9 from "os";
8910
- import * as path8 from "path";
9243
+ import * as path10 from "path";
8911
9244
  import * as crypto3 from "crypto";
8912
9245
  import * as fs5 from "fs";
8913
9246
  import { createRequire } from "module";
@@ -8915,7 +9248,7 @@ init_logger();
8915
9248
  var CachedDatabaseSync = null;
8916
9249
  function getDatabaseSync() {
8917
9250
  if (CachedDatabaseSync) return CachedDatabaseSync;
8918
- const requireFn = typeof __require === "function" ? __require : createRequire(path8.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
9251
+ const requireFn = typeof __require === "function" ? __require : createRequire(path10.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
8919
9252
  const sqliteModule = requireFn(`node:${"sqlite"}`);
8920
9253
  CachedDatabaseSync = sqliteModule.DatabaseSync;
8921
9254
  if (!CachedDatabaseSync) {
@@ -8955,6 +9288,7 @@ var CliProviderInstance = class {
8955
9288
  historyWriter;
8956
9289
  runtimeMessages = [];
8957
9290
  instanceId;
9291
+ suppressIdleHistoryReplay = false;
8958
9292
  presentationMode;
8959
9293
  providerSessionId;
8960
9294
  launchMode;
@@ -8982,7 +9316,15 @@ var CliProviderInstance = class {
8982
9316
  await this.adapter.spawn();
8983
9317
  this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
8984
9318
  if (this.providerSessionId) {
9319
+ this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
8985
9320
  const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
9321
+ this.historyWriter.seedSessionHistory(
9322
+ this.type,
9323
+ restoredHistory.messages,
9324
+ this.providerSessionId,
9325
+ this.instanceId
9326
+ );
9327
+ this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
8986
9328
  if (restoredHistory.messages.length > 0) {
8987
9329
  this.adapter.seedCommittedMessages(
8988
9330
  restoredHistory.messages.map((message) => ({
@@ -9026,7 +9368,7 @@ var CliProviderInstance = class {
9026
9368
  } else if (this.type === "codex-cli") {
9027
9369
  probedSessionId = this.probeSessionIdFromConfig({
9028
9370
  dbPath: "~/.codex/state_5.sqlite",
9029
- query: "select id from threads where cwd in ({dirs}) and created_at >= ? and archived = 0 order by created_at desc limit 1",
9371
+ query: "select id from threads where cwd in ({dirs}) and updated_at >= ? and archived = 0 order by updated_at desc limit 1",
9030
9372
  timestampFormat: "unix_s"
9031
9373
  });
9032
9374
  } else if (this.type === "goose-cli") {
@@ -9086,6 +9428,7 @@ var CliProviderInstance = class {
9086
9428
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
9087
9429
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
9088
9430
  if (parsedMessages.length > 0) {
9431
+ const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
9089
9432
  let messagesToSave = parsedMessages;
9090
9433
  if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
9091
9434
  const lastIdx = messagesToSave.length - 1;
@@ -9093,7 +9436,7 @@ var CliProviderInstance = class {
9093
9436
  messagesToSave = messagesToSave.slice(0, lastIdx);
9094
9437
  }
9095
9438
  }
9096
- if (messagesToSave.length > 0) {
9439
+ if (!shouldSkipReplayPersist && messagesToSave.length > 0) {
9097
9440
  this.historyWriter.appendNewMessages(
9098
9441
  this.type,
9099
9442
  messagesToSave,
@@ -9188,6 +9531,7 @@ var CliProviderInstance = class {
9188
9531
  if (newStatus !== this.lastStatus) {
9189
9532
  LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
9190
9533
  if (this.lastStatus === "idle" && newStatus === "generating") {
9534
+ this.suppressIdleHistoryReplay = false;
9191
9535
  if (this.completedDebouncePending) {
9192
9536
  LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
9193
9537
  if (this.completedDebounceTimer) {
@@ -9207,6 +9551,7 @@ var CliProviderInstance = class {
9207
9551
  this.generatingDebounceTimer = null;
9208
9552
  }, 1e3);
9209
9553
  } else if (newStatus === "waiting_approval") {
9554
+ this.suppressIdleHistoryReplay = false;
9210
9555
  if (this.generatingDebouncePending) {
9211
9556
  if (this.generatingDebounceTimer) {
9212
9557
  clearTimeout(this.generatingDebounceTimer);
@@ -9749,8 +10094,9 @@ var AcpProviderInstance = class {
9749
10094
  async setConfigOption(category, value) {
9750
10095
  const opt = this.configOptions.find((c) => c.category === category);
9751
10096
  if (!opt) {
9752
- this.log.warn(`[${this.type}] No config option for category: ${category}`);
9753
- return;
10097
+ const message = `[${this.type}] No config option for category: ${category}`;
10098
+ this.log.warn(message);
10099
+ throw new Error(message);
9754
10100
  }
9755
10101
  if (this.useStaticConfig) {
9756
10102
  opt.currentValue = value;
@@ -9762,8 +10108,9 @@ var AcpProviderInstance = class {
9762
10108
  return;
9763
10109
  }
9764
10110
  if (!this.connection || !this.sessionId) {
9765
- this.log.warn(`[${this.type}] Cannot set config: no active connection/session`);
9766
- return;
10111
+ const message = `[${this.type}] Cannot set config: no active connection/session`;
10112
+ this.log.warn(message);
10113
+ throw new Error(message);
9767
10114
  }
9768
10115
  try {
9769
10116
  this.log.info(`[${this.type}] Sending session/set_config_option: configId=${opt.configId} value=${value} sessionId=${this.sessionId}`);
@@ -9777,7 +10124,9 @@ var AcpProviderInstance = class {
9777
10124
  if (result?.configOptions) this.parseConfigOptions(result.configOptions);
9778
10125
  this.log.info(`[${this.type}] Config ${category} set to: ${value} | response: ${JSON.stringify(result)?.slice(0, 300)}`);
9779
10126
  } catch (e) {
9780
- this.log.warn(`[${this.type}] set_config_option failed: ${e?.message}`);
10127
+ const message = e?.message || "Unknown ACP config error";
10128
+ this.log.warn(`[${this.type}] set_config_option failed: ${message}`);
10129
+ throw new Error(message);
9781
10130
  }
9782
10131
  }
9783
10132
  async setMode(modeId) {
@@ -9793,8 +10142,9 @@ var AcpProviderInstance = class {
9793
10142
  return;
9794
10143
  }
9795
10144
  if (!this.connection || !this.sessionId) {
9796
- this.log.warn(`[${this.type}] Cannot set mode: no active connection/session`);
9797
- return;
10145
+ const message = `[${this.type}] Cannot set mode: no active connection/session`;
10146
+ this.log.warn(message);
10147
+ throw new Error(message);
9798
10148
  }
9799
10149
  try {
9800
10150
  await this.connection.setSessionMode({
@@ -9804,7 +10154,9 @@ var AcpProviderInstance = class {
9804
10154
  this.currentMode = modeId;
9805
10155
  this.log.info(`[${this.type}] Mode set to: ${modeId}`);
9806
10156
  } catch (e) {
9807
- this.log.warn(`[${this.type}] set_mode failed: ${e?.message}`);
10157
+ const message = e?.message || "Unknown ACP mode error";
10158
+ this.log.warn(`[${this.type}] set_mode failed: ${message}`);
10159
+ throw new Error(message);
9808
10160
  }
9809
10161
  }
9810
10162
  /** Static config: kill process and restart with new args */
@@ -9852,7 +10204,7 @@ var AcpProviderInstance = class {
9852
10204
  if (!spawnConfig) {
9853
10205
  throw new Error(`[ACP:${this.type}] No spawn config defined`);
9854
10206
  }
9855
- const command = spawnConfig.command;
10207
+ const command = typeof this.settings.executablePath === "string" && this.settings.executablePath.trim() ? this.settings.executablePath.trim() : spawnConfig.command;
9856
10208
  let baseArgs = spawnConfig.args || [];
9857
10209
  if (this.provider.spawnArgBuilder && Object.keys(this.selectedConfig).length > 0) {
9858
10210
  baseArgs = this.provider.spawnArgBuilder(this.selectedConfig);
@@ -9968,13 +10320,13 @@ var AcpProviderInstance = class {
9968
10320
  }
9969
10321
  this.currentStatus = "waiting_approval";
9970
10322
  this.detectStatusTransition();
9971
- const approved = await new Promise((resolve9) => {
9972
- this.permissionResolvers.push(resolve9);
10323
+ const approved = await new Promise((resolve12) => {
10324
+ this.permissionResolvers.push(resolve12);
9973
10325
  setTimeout(() => {
9974
- const idx = this.permissionResolvers.indexOf(resolve9);
10326
+ const idx = this.permissionResolvers.indexOf(resolve12);
9975
10327
  if (idx >= 0) {
9976
10328
  this.permissionResolvers.splice(idx, 1);
9977
- resolve9(false);
10329
+ resolve12(false);
9978
10330
  }
9979
10331
  }, 3e5);
9980
10332
  });
@@ -10681,7 +11033,7 @@ var DaemonCliManager = class {
10681
11033
  async startSession(cliType, workingDir, cliArgs, initialModel, options) {
10682
11034
  const trimmed = (workingDir || "").trim();
10683
11035
  if (!trimmed) throw new Error("working directory required");
10684
- const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path9.resolve(trimmed);
11036
+ const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path11.resolve(trimmed);
10685
11037
  const normalizedType = this.providerLoader.resolveAlias(cliType);
10686
11038
  const provider = this.providerLoader.getByAlias(cliType);
10687
11039
  const key = crypto4.randomUUID();
@@ -10768,10 +11120,10 @@ ${installInfo}`
10768
11120
  if (!cliInfo) {
10769
11121
  const installHint = provider?.install || "";
10770
11122
  const displayName = provider?.displayName || provider?.name || cliType;
10771
- const spawnCmd = provider?.spawn?.command || cliType;
11123
+ const spawnCmd = this.providerLoader.getSpawnCommand(normalizedType, provider?.spawn?.command || cliType);
10772
11124
  throw new Error(
10773
11125
  `${displayName} is not installed.
10774
- Command '${spawnCmd}' not found on PATH.
11126
+ Command '${spawnCmd}' is not available.
10775
11127
  ` + (installHint ? `
10776
11128
  ${installHint}
10777
11129
  ` : "") + `
@@ -10917,6 +11269,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
10917
11269
  if (!instanceManager) return 0;
10918
11270
  const sessions = records || await this.deps.listHostedCliRuntimes?.() || [];
10919
11271
  let restored = 0;
11272
+ const restoredBindings = /* @__PURE__ */ new Set();
10920
11273
  for (const record of sessions) {
10921
11274
  if (!record?.runtimeId || !record?.cliType || !record?.workspace) continue;
10922
11275
  if (this.adapters.has(record.runtimeId) || instanceManager.getInstance(record.runtimeId)) continue;
@@ -10930,6 +11283,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
10930
11283
  record.cliArgs,
10931
11284
  record.providerSessionId
10932
11285
  );
11286
+ const bindingKey = [
11287
+ normalizedType,
11288
+ record.workspace,
11289
+ sessionBinding.providerSessionId || record.runtimeId
11290
+ ].join("::");
11291
+ if (restoredBindings.has(bindingKey)) {
11292
+ LOG.info(
11293
+ "CLI",
11294
+ `\u21B7 Skipping duplicate hosted runtime restore: ${record.runtimeKey || record.runtimeId} (${normalizedType} @ ${record.workspace}) binding=${sessionBinding.providerSessionId || "runtime"}`
11295
+ );
11296
+ continue;
11297
+ }
10933
11298
  try {
10934
11299
  await this.registerCliInstance(
10935
11300
  record.runtimeId,
@@ -10945,6 +11310,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
10945
11310
  launchMode: "manual"
10946
11311
  }
10947
11312
  );
11313
+ restoredBindings.add(bindingKey);
10948
11314
  restored += 1;
10949
11315
  LOG.info("CLI", `\u267B Restored hosted runtime: ${record.runtimeKey || record.runtimeId} (${record.displayName || record.workspace})`);
10950
11316
  } catch (error) {
@@ -11131,16 +11497,17 @@ Run 'adhdev doctor' for detailed diagnostics.`
11131
11497
  import { execSync as execSync4, spawn as spawn2 } from "child_process";
11132
11498
  import * as net from "net";
11133
11499
  import * as os12 from "os";
11134
- import * as path11 from "path";
11500
+ import * as path13 from "path";
11135
11501
 
11136
11502
  // src/providers/provider-loader.ts
11137
11503
  import * as fs6 from "fs";
11138
- import * as path10 from "path";
11504
+ import * as path12 from "path";
11139
11505
  import * as os11 from "os";
11140
11506
  import * as chokidar from "chokidar";
11141
11507
  init_logger();
11142
11508
  var ProviderLoader = class _ProviderLoader {
11143
11509
  providers = /* @__PURE__ */ new Map();
11510
+ providerAvailability = /* @__PURE__ */ new Map();
11144
11511
  userDir;
11145
11512
  upstreamDir;
11146
11513
  disableUpstream;
@@ -11156,12 +11523,12 @@ var ProviderLoader = class _ProviderLoader {
11156
11523
  static META_FILE = ".meta.json";
11157
11524
  constructor(options) {
11158
11525
  this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
11159
- const defaultProvidersDir = path10.join(os11.homedir(), ".adhdev", "providers");
11526
+ const defaultProvidersDir = path12.join(os11.homedir(), ".adhdev", "providers");
11160
11527
  if (options?.userDir) {
11161
11528
  this.userDir = options.userDir;
11162
11529
  this.log(`Config 'providerDir' applied: ${this.userDir}`);
11163
11530
  } else {
11164
- const localRepoPath = path10.resolve(__dirname, "../../../../../adhdev-providers");
11531
+ const localRepoPath = path12.resolve(__dirname, "../../../../../adhdev-providers");
11165
11532
  if (fs6.existsSync(localRepoPath)) {
11166
11533
  this.userDir = localRepoPath;
11167
11534
  this.log(`Auto-detected local public repository: ${this.userDir} (Dev workspace speedup)`);
@@ -11170,7 +11537,7 @@ var ProviderLoader = class _ProviderLoader {
11170
11537
  this.log(`Using default user providers directory: ${this.userDir}`);
11171
11538
  }
11172
11539
  }
11173
- this.upstreamDir = path10.join(defaultProvidersDir, ".upstream");
11540
+ this.upstreamDir = path12.join(defaultProvidersDir, ".upstream");
11174
11541
  this.disableUpstream = options?.disableUpstream ?? false;
11175
11542
  }
11176
11543
  log(msg) {
@@ -11200,7 +11567,7 @@ var ProviderLoader = class _ProviderLoader {
11200
11567
  * Canonical provider directory shape for a given root.
11201
11568
  */
11202
11569
  getProviderDir(root, category, type) {
11203
- return path10.join(root, category, type);
11570
+ return path12.join(root, category, type);
11204
11571
  }
11205
11572
  /**
11206
11573
  * Canonical user override directory for a provider.
@@ -11227,7 +11594,7 @@ var ProviderLoader = class _ProviderLoader {
11227
11594
  resolveProviderFile(type, ...segments) {
11228
11595
  const dir = this.findProviderDirInternal(type);
11229
11596
  if (!dir) return null;
11230
- return path10.join(dir, ...segments);
11597
+ return path12.join(dir, ...segments);
11231
11598
  }
11232
11599
  /**
11233
11600
  * Load all providers (3-tier priority)
@@ -11238,6 +11605,7 @@ var ProviderLoader = class _ProviderLoader {
11238
11605
  */
11239
11606
  loadAll() {
11240
11607
  this.providers.clear();
11608
+ this.providerAvailability.clear();
11241
11609
  let upstreamCount = 0;
11242
11610
  if (!this.disableUpstream && fs6.existsSync(this.upstreamDir)) {
11243
11611
  upstreamCount = this.loadDir(this.upstreamDir);
@@ -11265,7 +11633,7 @@ var ProviderLoader = class _ProviderLoader {
11265
11633
  if (!fs6.existsSync(this.upstreamDir)) return false;
11266
11634
  try {
11267
11635
  return fs6.readdirSync(this.upstreamDir).some(
11268
- (d) => fs6.statSync(path10.join(this.upstreamDir, d)).isDirectory()
11636
+ (d) => fs6.statSync(path12.join(this.upstreamDir, d)).isDirectory()
11269
11637
  );
11270
11638
  } catch {
11271
11639
  return false;
@@ -11308,11 +11676,12 @@ var ProviderLoader = class _ProviderLoader {
11308
11676
  if ((p.category === "cli" || p.category === "acp") && p.spawn?.command) {
11309
11677
  const verCmdConfig = p.versionCommand;
11310
11678
  const versionCommand = typeof verCmdConfig === "object" && verCmdConfig !== null ? verCmdConfig[process.platform] : verCmdConfig;
11679
+ const command = this.getSpawnCommand(p.type, p.spawn.command);
11311
11680
  result.push({
11312
11681
  id: p.type,
11313
11682
  displayName: p.displayName || p.name,
11314
11683
  icon: p.icon || "\u{1F527}",
11315
- command: p.spawn.command,
11684
+ command,
11316
11685
  category: p.category,
11317
11686
  ...typeof versionCommand === "string" && versionCommand.trim() ? { versionCommand: versionCommand.trim() } : {}
11318
11687
  });
@@ -11441,6 +11810,71 @@ var ProviderLoader = class _ProviderLoader {
11441
11810
  getAvailableIdeTypes() {
11442
11811
  return [...this.providers.values()].filter((p) => p.category === "ide" && p.cdpPorts).map((p) => p.type);
11443
11812
  }
11813
+ getSpawnCommand(type, fallback) {
11814
+ const override = this.getOptionalStringSetting(type, "executablePath");
11815
+ if (override) return override;
11816
+ return fallback || this.providers.get(type)?.spawn?.command || type;
11817
+ }
11818
+ getIdeCliCommand(type, fallback) {
11819
+ const override = this.getOptionalStringSetting(type, "cliPathOverride");
11820
+ if (override) return override;
11821
+ return fallback || this.providers.get(type)?.cli || null;
11822
+ }
11823
+ getIdePathCandidates(type, fallback) {
11824
+ const override = this.getOptionalStringSetting(type, "appPathOverride");
11825
+ if (override) return [override];
11826
+ if (fallback && fallback.length > 0) return fallback;
11827
+ const osPaths = this.providers.get(type)?.paths?.[process.platform];
11828
+ return Array.isArray(osPaths) ? [...osPaths] : [];
11829
+ }
11830
+ setProviderAvailability(type, state) {
11831
+ this.providerAvailability.set(type, {
11832
+ installed: !!state.installed,
11833
+ detectedPath: state.detectedPath ?? null
11834
+ });
11835
+ }
11836
+ setCliDetectionResults(results, replace = true) {
11837
+ if (replace) {
11838
+ for (const provider of this.providers.values()) {
11839
+ if (provider.category === "cli" || provider.category === "acp") {
11840
+ this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
11841
+ }
11842
+ }
11843
+ }
11844
+ for (const result of results) {
11845
+ this.setProviderAvailability(result.id, {
11846
+ installed: !!result.installed,
11847
+ detectedPath: result.path || null
11848
+ });
11849
+ }
11850
+ }
11851
+ setIdeDetectionResults(results, replace = true) {
11852
+ if (replace) {
11853
+ for (const provider of this.providers.values()) {
11854
+ if (provider.category === "ide") {
11855
+ this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
11856
+ }
11857
+ }
11858
+ }
11859
+ for (const result of results) {
11860
+ this.setProviderAvailability(result.id, {
11861
+ installed: !!result.installed,
11862
+ detectedPath: result.cliCommand || result.path || null
11863
+ });
11864
+ }
11865
+ }
11866
+ getAvailableProviderInfos() {
11867
+ return this.getAll().map((provider) => {
11868
+ const availability = this.providerAvailability.get(provider.type);
11869
+ return {
11870
+ ...provider,
11871
+ ...availability ? {
11872
+ installed: availability.installed,
11873
+ detectedPath: availability.detectedPath
11874
+ } : {}
11875
+ };
11876
+ });
11877
+ }
11444
11878
  /**
11445
11879
  * Register IDE providers to core/detector registry
11446
11880
  * → Enables detectIDEs() to detect provider.js-based IDEs
@@ -11515,8 +11949,8 @@ var ProviderLoader = class _ProviderLoader {
11515
11949
  resolved._resolvedScriptDir = entry.scriptDir;
11516
11950
  resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
11517
11951
  if (providerDir) {
11518
- const fullDir = path10.join(providerDir, entry.scriptDir);
11519
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
11952
+ const fullDir = path12.join(providerDir, entry.scriptDir);
11953
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11520
11954
  }
11521
11955
  matched = true;
11522
11956
  }
@@ -11531,8 +11965,8 @@ var ProviderLoader = class _ProviderLoader {
11531
11965
  resolved._resolvedScriptDir = base.defaultScriptDir;
11532
11966
  resolved._resolvedScriptsSource = "defaultScriptDir:version_miss";
11533
11967
  if (providerDir) {
11534
- const fullDir = path10.join(providerDir, base.defaultScriptDir);
11535
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
11968
+ const fullDir = path12.join(providerDir, base.defaultScriptDir);
11969
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11536
11970
  }
11537
11971
  }
11538
11972
  resolved._versionWarning = `Version ${currentVersion} not in compatibility matrix. Using default scripts.`;
@@ -11549,8 +11983,8 @@ var ProviderLoader = class _ProviderLoader {
11549
11983
  resolved._resolvedScriptDir = dirOverride;
11550
11984
  resolved._resolvedScriptsSource = `versions:${range}`;
11551
11985
  if (providerDir) {
11552
- const fullDir = path10.join(providerDir, dirOverride);
11553
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
11986
+ const fullDir = path12.join(providerDir, dirOverride);
11987
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11554
11988
  }
11555
11989
  }
11556
11990
  } else if (override.scripts) {
@@ -11566,8 +12000,8 @@ var ProviderLoader = class _ProviderLoader {
11566
12000
  resolved._resolvedScriptDir = base.defaultScriptDir;
11567
12001
  resolved._resolvedScriptsSource = "defaultScriptDir:no_version";
11568
12002
  if (providerDir) {
11569
- const fullDir = path10.join(providerDir, base.defaultScriptDir);
11570
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
12003
+ const fullDir = path12.join(providerDir, base.defaultScriptDir);
12004
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
11571
12005
  }
11572
12006
  }
11573
12007
  }
@@ -11592,14 +12026,14 @@ var ProviderLoader = class _ProviderLoader {
11592
12026
  this.log(` [loadScriptsFromDir] ${type}: providerDir not found`);
11593
12027
  return null;
11594
12028
  }
11595
- const dir = path10.join(providerDir, scriptDir);
12029
+ const dir = path12.join(providerDir, scriptDir);
11596
12030
  if (!fs6.existsSync(dir)) {
11597
12031
  this.log(` [loadScriptsFromDir] ${type}: dir not found: ${dir}`);
11598
12032
  return null;
11599
12033
  }
11600
12034
  const cached = this.scriptsCache.get(dir);
11601
12035
  if (cached) return cached;
11602
- const scriptsJs = path10.join(dir, "scripts.js");
12036
+ const scriptsJs = path12.join(dir, "scripts.js");
11603
12037
  if (fs6.existsSync(scriptsJs)) {
11604
12038
  try {
11605
12039
  delete __require.cache[__require.resolve(scriptsJs)];
@@ -11641,7 +12075,7 @@ var ProviderLoader = class _ProviderLoader {
11641
12075
  return;
11642
12076
  }
11643
12077
  if (filePath.endsWith(".js") || filePath.endsWith(".json")) {
11644
- this.log(`File changed: ${path10.basename(filePath)}, reloading...`);
12078
+ this.log(`File changed: ${path12.basename(filePath)}, reloading...`);
11645
12079
  this.reload();
11646
12080
  }
11647
12081
  };
@@ -11696,7 +12130,7 @@ var ProviderLoader = class _ProviderLoader {
11696
12130
  }
11697
12131
  const https = __require("https");
11698
12132
  const { execSync: execSync7 } = __require("child_process");
11699
- const metaPath = path10.join(this.upstreamDir, _ProviderLoader.META_FILE);
12133
+ const metaPath = path12.join(this.upstreamDir, _ProviderLoader.META_FILE);
11700
12134
  let prevEtag = "";
11701
12135
  let prevTimestamp = 0;
11702
12136
  try {
@@ -11713,7 +12147,7 @@ var ProviderLoader = class _ProviderLoader {
11713
12147
  return { updated: false };
11714
12148
  }
11715
12149
  try {
11716
- const etag = await new Promise((resolve9, reject) => {
12150
+ const etag = await new Promise((resolve12, reject) => {
11717
12151
  const options = {
11718
12152
  method: "HEAD",
11719
12153
  hostname: "github.com",
@@ -11731,7 +12165,7 @@ var ProviderLoader = class _ProviderLoader {
11731
12165
  headers: { "User-Agent": "adhdev-launcher" },
11732
12166
  timeout: 1e4
11733
12167
  }, (res2) => {
11734
- resolve9(res2.headers.etag || res2.headers["last-modified"] || "");
12168
+ resolve12(res2.headers.etag || res2.headers["last-modified"] || "");
11735
12169
  });
11736
12170
  req2.on("error", reject);
11737
12171
  req2.on("timeout", () => {
@@ -11740,7 +12174,7 @@ var ProviderLoader = class _ProviderLoader {
11740
12174
  });
11741
12175
  req2.end();
11742
12176
  } else {
11743
- resolve9(res.headers.etag || res.headers["last-modified"] || "");
12177
+ resolve12(res.headers.etag || res.headers["last-modified"] || "");
11744
12178
  }
11745
12179
  });
11746
12180
  req.on("error", reject);
@@ -11756,17 +12190,17 @@ var ProviderLoader = class _ProviderLoader {
11756
12190
  return { updated: false };
11757
12191
  }
11758
12192
  this.log("Downloading latest providers from GitHub...");
11759
- const tmpTar = path10.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
11760
- const tmpExtract = path10.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
12193
+ const tmpTar = path12.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
12194
+ const tmpExtract = path12.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
11761
12195
  await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
11762
12196
  fs6.mkdirSync(tmpExtract, { recursive: true });
11763
12197
  execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
11764
12198
  const extracted = fs6.readdirSync(tmpExtract);
11765
12199
  const rootDir = extracted.find(
11766
- (d) => fs6.statSync(path10.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
12200
+ (d) => fs6.statSync(path12.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
11767
12201
  );
11768
12202
  if (!rootDir) throw new Error("Unexpected tarball structure");
11769
- const sourceDir = path10.join(tmpExtract, rootDir);
12203
+ const sourceDir = path12.join(tmpExtract, rootDir);
11770
12204
  const backupDir = this.upstreamDir + ".bak";
11771
12205
  if (fs6.existsSync(this.upstreamDir)) {
11772
12206
  if (fs6.existsSync(backupDir)) fs6.rmSync(backupDir, { recursive: true, force: true });
@@ -11804,7 +12238,7 @@ var ProviderLoader = class _ProviderLoader {
11804
12238
  downloadFile(url, destPath) {
11805
12239
  const https = __require("https");
11806
12240
  const http3 = __require("http");
11807
- return new Promise((resolve9, reject) => {
12241
+ return new Promise((resolve12, reject) => {
11808
12242
  const doRequest = (reqUrl, redirectCount = 0) => {
11809
12243
  if (redirectCount > 5) {
11810
12244
  reject(new Error("Too many redirects"));
@@ -11824,7 +12258,7 @@ var ProviderLoader = class _ProviderLoader {
11824
12258
  res.pipe(ws);
11825
12259
  ws.on("finish", () => {
11826
12260
  ws.close();
11827
- resolve9();
12261
+ resolve12();
11828
12262
  });
11829
12263
  ws.on("error", reject);
11830
12264
  });
@@ -11841,8 +12275,8 @@ var ProviderLoader = class _ProviderLoader {
11841
12275
  copyDirRecursive(src, dest) {
11842
12276
  fs6.mkdirSync(dest, { recursive: true });
11843
12277
  for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
11844
- const srcPath = path10.join(src, entry.name);
11845
- const destPath = path10.join(dest, entry.name);
12278
+ const srcPath = path12.join(src, entry.name);
12279
+ const destPath = path12.join(dest, entry.name);
11846
12280
  if (entry.isDirectory()) {
11847
12281
  this.copyDirRecursive(srcPath, destPath);
11848
12282
  } else {
@@ -11853,7 +12287,7 @@ var ProviderLoader = class _ProviderLoader {
11853
12287
  /** .meta.json save */
11854
12288
  writeMeta(metaPath, etag, timestamp) {
11855
12289
  try {
11856
- fs6.mkdirSync(path10.dirname(metaPath), { recursive: true });
12290
+ fs6.mkdirSync(path12.dirname(metaPath), { recursive: true });
11857
12291
  fs6.writeFileSync(metaPath, JSON.stringify({
11858
12292
  etag,
11859
12293
  timestamp,
@@ -11870,7 +12304,7 @@ var ProviderLoader = class _ProviderLoader {
11870
12304
  const scan = (d) => {
11871
12305
  try {
11872
12306
  for (const entry of fs6.readdirSync(d, { withFileTypes: true })) {
11873
- if (entry.isDirectory()) scan(path10.join(d, entry.name));
12307
+ if (entry.isDirectory()) scan(path12.join(d, entry.name));
11874
12308
  else if (entry.name === "provider.json") count++;
11875
12309
  }
11876
12310
  } catch {
@@ -11884,9 +12318,8 @@ var ProviderLoader = class _ProviderLoader {
11884
12318
  * Get public settings schema for a provider (for dashboard UI rendering)
11885
12319
  */
11886
12320
  getPublicSettings(type) {
11887
- const provider = this.providers.get(type);
11888
- if (!provider?.settings) return [];
11889
- return Object.entries(provider.settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
12321
+ const settings = this.getSettingsSchema(type);
12322
+ return Object.entries(settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
11890
12323
  }
11891
12324
  /**
11892
12325
  * Get public settings schema for all providers
@@ -11903,8 +12336,7 @@ var ProviderLoader = class _ProviderLoader {
11903
12336
  * Resolved setting value for a provider (default + user override)
11904
12337
  */
11905
12338
  getSettingValue(type, key) {
11906
- const provider = this.providers.get(type);
11907
- const schemaDef = provider?.settings?.[key];
12339
+ const schemaDef = this.getSettingsSchema(type)[key];
11908
12340
  const defaultVal = schemaDef ? schemaDef.default : void 0;
11909
12341
  try {
11910
12342
  const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
@@ -11919,10 +12351,9 @@ var ProviderLoader = class _ProviderLoader {
11919
12351
  * All resolved settings for a provider (default + user override)
11920
12352
  */
11921
12353
  getSettings(type) {
11922
- const provider = this.providers.get(type);
11923
- if (!provider?.settings) return {};
12354
+ const settings = this.getSettingsSchema(type);
11924
12355
  const result = {};
11925
- for (const [key, def] of Object.entries(provider.settings)) {
12356
+ for (const [key] of Object.entries(settings)) {
11926
12357
  result[key] = this.getSettingValue(type, key);
11927
12358
  }
11928
12359
  return result;
@@ -11931,11 +12362,11 @@ var ProviderLoader = class _ProviderLoader {
11931
12362
  * Save provider setting value (writes to config.json)
11932
12363
  */
11933
12364
  setSetting(type, key, value) {
11934
- const provider = this.providers.get(type);
11935
- const schemaDef = provider?.settings?.[key];
12365
+ const schemaDef = this.getSettingsSchema(type)[key];
11936
12366
  if (!schemaDef) return false;
11937
12367
  if (!schemaDef.public) return false;
11938
12368
  if (schemaDef.type === "boolean" && typeof value !== "boolean") return false;
12369
+ if (schemaDef.type === "string" && typeof value !== "string") return false;
11939
12370
  if (schemaDef.type === "number") {
11940
12371
  if (typeof value !== "number") return false;
11941
12372
  if (schemaDef.min !== void 0 && value < schemaDef.min) return false;
@@ -11956,6 +12387,53 @@ var ProviderLoader = class _ProviderLoader {
11956
12387
  return false;
11957
12388
  }
11958
12389
  }
12390
+ getOptionalStringSetting(type, key) {
12391
+ const value = this.getSettingValue(type, key);
12392
+ if (typeof value !== "string") return null;
12393
+ const trimmed = value.trim();
12394
+ return trimmed ? trimmed : null;
12395
+ }
12396
+ getSettingsSchema(type) {
12397
+ const provider = this.providers.get(type);
12398
+ if (!provider) return {};
12399
+ return {
12400
+ ...this.getSyntheticSettings(type, provider),
12401
+ ...provider.settings || {}
12402
+ };
12403
+ }
12404
+ getSyntheticSettings(type, provider) {
12405
+ const result = {};
12406
+ if ((provider.category === "cli" || provider.category === "acp") && provider.spawn?.command && !provider.settings?.executablePath) {
12407
+ result.executablePath = {
12408
+ type: "string",
12409
+ default: "",
12410
+ public: true,
12411
+ label: "Executable path",
12412
+ description: "Optional absolute path for this provider binary. Leave blank to use the default PATH lookup."
12413
+ };
12414
+ }
12415
+ if (provider.category === "ide") {
12416
+ if (provider.cli && !provider.settings?.cliPathOverride) {
12417
+ result.cliPathOverride = {
12418
+ type: "string",
12419
+ default: "",
12420
+ public: true,
12421
+ label: "CLI path override",
12422
+ description: "Optional absolute path for the IDE CLI launcher. Leave blank to use the detected default."
12423
+ };
12424
+ }
12425
+ if (provider.paths && !provider.settings?.appPathOverride) {
12426
+ result.appPathOverride = {
12427
+ type: "string",
12428
+ default: "",
12429
+ public: true,
12430
+ label: "App path override",
12431
+ description: "Optional absolute path for the IDE app bundle or executable. Leave blank to use the default install locations."
12432
+ };
12433
+ }
12434
+ }
12435
+ return result;
12436
+ }
11959
12437
  // ─── Private ───────────────────────────────────
11960
12438
  /**
11961
12439
  * Find the on-disk directory for a provider by type.
@@ -11969,17 +12447,17 @@ var ProviderLoader = class _ProviderLoader {
11969
12447
  for (const root of searchRoots) {
11970
12448
  if (!fs6.existsSync(root)) continue;
11971
12449
  const candidate = this.getProviderDir(root, cat, type);
11972
- if (fs6.existsSync(path10.join(candidate, "provider.json"))) return candidate;
11973
- const catDir = path10.join(root, cat);
12450
+ if (fs6.existsSync(path12.join(candidate, "provider.json"))) return candidate;
12451
+ const catDir = path12.join(root, cat);
11974
12452
  if (fs6.existsSync(catDir)) {
11975
12453
  try {
11976
12454
  for (const entry of fs6.readdirSync(catDir, { withFileTypes: true })) {
11977
12455
  if (!entry.isDirectory()) continue;
11978
- const jsonPath = path10.join(catDir, entry.name, "provider.json");
12456
+ const jsonPath = path12.join(catDir, entry.name, "provider.json");
11979
12457
  if (fs6.existsSync(jsonPath)) {
11980
12458
  try {
11981
12459
  const data = JSON.parse(fs6.readFileSync(jsonPath, "utf-8"));
11982
- if (data.type === type) return path10.join(catDir, entry.name);
12460
+ if (data.type === type) return path12.join(catDir, entry.name);
11983
12461
  } catch {
11984
12462
  }
11985
12463
  }
@@ -11996,7 +12474,7 @@ var ProviderLoader = class _ProviderLoader {
11996
12474
  * (template substitution is NOT applied here — scripts.js handles that)
11997
12475
  */
11998
12476
  buildScriptWrappersFromDir(dir) {
11999
- const scriptsJs = path10.join(dir, "scripts.js");
12477
+ const scriptsJs = path12.join(dir, "scripts.js");
12000
12478
  if (fs6.existsSync(scriptsJs)) {
12001
12479
  try {
12002
12480
  delete __require.cache[__require.resolve(scriptsJs)];
@@ -12010,7 +12488,7 @@ var ProviderLoader = class _ProviderLoader {
12010
12488
  for (const file of fs6.readdirSync(dir)) {
12011
12489
  if (!file.endsWith(".js")) continue;
12012
12490
  const scriptName = toCamel(file.replace(".js", ""));
12013
- const filePath = path10.join(dir, file);
12491
+ const filePath = path12.join(dir, file);
12014
12492
  result[scriptName] = (...args) => {
12015
12493
  try {
12016
12494
  let content = fs6.readFileSync(filePath, "utf-8");
@@ -12070,7 +12548,7 @@ var ProviderLoader = class _ProviderLoader {
12070
12548
  }
12071
12549
  const hasJson = entries.some((e) => e.name === "provider.json");
12072
12550
  if (hasJson) {
12073
- const jsonPath = path10.join(d, "provider.json");
12551
+ const jsonPath = path12.join(d, "provider.json");
12074
12552
  try {
12075
12553
  const raw = fs6.readFileSync(jsonPath, "utf-8");
12076
12554
  const mod = JSON.parse(raw);
@@ -12083,7 +12561,7 @@ var ProviderLoader = class _ProviderLoader {
12083
12561
  delete mod.extensionIdPattern_flags;
12084
12562
  }
12085
12563
  const hasCompatibility = Array.isArray(mod.compatibility);
12086
- const scriptsPath = path10.join(d, "scripts.js");
12564
+ const scriptsPath = path12.join(d, "scripts.js");
12087
12565
  if (!hasCompatibility && fs6.existsSync(scriptsPath)) {
12088
12566
  try {
12089
12567
  delete __require.cache[__require.resolve(scriptsPath)];
@@ -12109,7 +12587,7 @@ var ProviderLoader = class _ProviderLoader {
12109
12587
  if (!entry.isDirectory()) continue;
12110
12588
  if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
12111
12589
  if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
12112
- scan(path10.join(d, entry.name));
12590
+ scan(path12.join(d, entry.name));
12113
12591
  }
12114
12592
  }
12115
12593
  };
@@ -12205,17 +12683,17 @@ async function findFreePort(ports) {
12205
12683
  throw new Error("No free port found");
12206
12684
  }
12207
12685
  function checkPortFree(port) {
12208
- return new Promise((resolve9) => {
12686
+ return new Promise((resolve12) => {
12209
12687
  const server = net.createServer();
12210
12688
  server.unref();
12211
- server.on("error", () => resolve9(false));
12689
+ server.on("error", () => resolve12(false));
12212
12690
  server.listen(port, "127.0.0.1", () => {
12213
- server.close(() => resolve9(true));
12691
+ server.close(() => resolve12(true));
12214
12692
  });
12215
12693
  });
12216
12694
  }
12217
12695
  async function isCdpActive(port) {
12218
- return new Promise((resolve9) => {
12696
+ return new Promise((resolve12) => {
12219
12697
  const req = __require("http").get(`http://127.0.0.1:${port}/json/version`, {
12220
12698
  timeout: 2e3
12221
12699
  }, (res) => {
@@ -12224,16 +12702,16 @@ async function isCdpActive(port) {
12224
12702
  res.on("end", () => {
12225
12703
  try {
12226
12704
  const info = JSON.parse(data);
12227
- resolve9(!!info["WebKit-Version"] || !!info["Browser"]);
12705
+ resolve12(!!info["WebKit-Version"] || !!info["Browser"]);
12228
12706
  } catch {
12229
- resolve9(false);
12707
+ resolve12(false);
12230
12708
  }
12231
12709
  });
12232
12710
  });
12233
- req.on("error", () => resolve9(false));
12711
+ req.on("error", () => resolve12(false));
12234
12712
  req.on("timeout", () => {
12235
12713
  req.destroy();
12236
- resolve9(false);
12714
+ resolve12(false);
12237
12715
  });
12238
12716
  });
12239
12717
  }
@@ -12367,8 +12845,8 @@ function detectCurrentWorkspace(ideId) {
12367
12845
  const appNameMap = getMacAppIdentifiers();
12368
12846
  const appName = appNameMap[ideId];
12369
12847
  if (appName) {
12370
- const storagePath = path11.join(
12371
- process.env.APPDATA || path11.join(os12.homedir(), "AppData", "Roaming"),
12848
+ const storagePath = path13.join(
12849
+ process.env.APPDATA || path13.join(os12.homedir(), "AppData", "Roaming"),
12372
12850
  appName,
12373
12851
  "storage.json"
12374
12852
  );
@@ -12392,7 +12870,7 @@ function detectCurrentWorkspace(ideId) {
12392
12870
  async function launchWithCdp(options = {}) {
12393
12871
  const platform9 = os12.platform();
12394
12872
  let targetIde;
12395
- const ides = await detectIDEs();
12873
+ const ides = await detectIDEs(getProviderLoader());
12396
12874
  if (options.ideId) {
12397
12875
  targetIde = ides.find((i) => i.id === options.ideId && i.installed);
12398
12876
  if (!targetIde) {
@@ -12546,9 +13024,9 @@ init_logger();
12546
13024
 
12547
13025
  // src/logging/command-log.ts
12548
13026
  import * as fs7 from "fs";
12549
- import * as path12 from "path";
13027
+ import * as path14 from "path";
12550
13028
  import * as os13 from "os";
12551
- 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");
13029
+ 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");
12552
13030
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
12553
13031
  var MAX_DAYS = 7;
12554
13032
  try {
@@ -12586,13 +13064,13 @@ function getDateStr2() {
12586
13064
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
12587
13065
  }
12588
13066
  var currentDate2 = getDateStr2();
12589
- var currentFile = path12.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
13067
+ var currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
12590
13068
  var writeCount2 = 0;
12591
13069
  function checkRotation() {
12592
13070
  const today = getDateStr2();
12593
13071
  if (today !== currentDate2) {
12594
13072
  currentDate2 = today;
12595
- currentFile = path12.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
13073
+ currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
12596
13074
  cleanOldFiles();
12597
13075
  }
12598
13076
  }
@@ -12606,7 +13084,7 @@ function cleanOldFiles() {
12606
13084
  const dateMatch = file.match(/commands-(\d{4}-\d{2}-\d{2})/);
12607
13085
  if (dateMatch && dateMatch[1] < cutoffStr) {
12608
13086
  try {
12609
- fs7.unlinkSync(path12.join(LOG_DIR2, file));
13087
+ fs7.unlinkSync(path14.join(LOG_DIR2, file));
12610
13088
  } catch {
12611
13089
  }
12612
13090
  }
@@ -12698,12 +13176,15 @@ function buildDetectedIdeInfos(detectedIdes, cdpManagers) {
12698
13176
  }));
12699
13177
  }
12700
13178
  function buildAvailableProviders(providerLoader) {
12701
- return providerLoader.getAll().map((provider) => ({
13179
+ const providers = providerLoader.getAvailableProviderInfos?.() || providerLoader.getAll();
13180
+ return providers.map((provider) => ({
12702
13181
  type: provider.type,
12703
13182
  name: provider.displayName || provider.type,
12704
13183
  displayName: provider.displayName || provider.type,
12705
13184
  icon: provider.icon || "\u{1F4BB}",
12706
- category: provider.category
13185
+ category: provider.category,
13186
+ ...provider.installed !== void 0 ? { installed: provider.installed } : {},
13187
+ ...provider.detectedPath !== void 0 ? { detectedPath: provider.detectedPath } : {}
12707
13188
  }));
12708
13189
  }
12709
13190
  function parseMessageTime(value) {
@@ -12831,13 +13312,13 @@ import { execFileSync } from "child_process";
12831
13312
  import { spawn as spawn3 } from "child_process";
12832
13313
  import * as fs8 from "fs";
12833
13314
  import * as os15 from "os";
12834
- import * as path13 from "path";
13315
+ import * as path15 from "path";
12835
13316
  var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
12836
13317
  function getUpgradeLogPath() {
12837
13318
  const home = os15.homedir();
12838
- const dir = path13.join(home, ".adhdev");
13319
+ const dir = path15.join(home, ".adhdev");
12839
13320
  fs8.mkdirSync(dir, { recursive: true });
12840
- return path13.join(dir, "daemon-upgrade.log");
13321
+ return path15.join(dir, "daemon-upgrade.log");
12841
13322
  }
12842
13323
  function appendUpgradeLog(message) {
12843
13324
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
@@ -12870,14 +13351,14 @@ async function waitForPidExit(pid, timeoutMs) {
12870
13351
  while (Date.now() - start < timeoutMs) {
12871
13352
  try {
12872
13353
  process.kill(pid, 0);
12873
- await new Promise((resolve9) => setTimeout(resolve9, 250));
13354
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
12874
13355
  } catch {
12875
13356
  return;
12876
13357
  }
12877
13358
  }
12878
13359
  }
12879
13360
  function stopSessionHostProcesses(appName) {
12880
- const pidFile = path13.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
13361
+ const pidFile = path15.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
12881
13362
  try {
12882
13363
  if (fs8.existsSync(pidFile)) {
12883
13364
  const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
@@ -12906,7 +13387,7 @@ function stopSessionHostProcesses(appName) {
12906
13387
  }
12907
13388
  }
12908
13389
  function removeDaemonPidFile() {
12909
- const pidFile = path13.join(os15.homedir(), ".adhdev", "daemon.pid");
13390
+ const pidFile = path15.join(os15.homedir(), ".adhdev", "daemon.pid");
12910
13391
  try {
12911
13392
  fs8.unlinkSync(pidFile);
12912
13393
  } catch {
@@ -12917,7 +13398,7 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
12917
13398
  const npmRoot = execFileSync(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
12918
13399
  if (!npmRoot) return;
12919
13400
  const npmPrefix = execFileSync(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
12920
- const binDir = process.platform === "win32" ? npmPrefix : path13.join(npmPrefix, "bin");
13401
+ const binDir = process.platform === "win32" ? npmPrefix : path15.join(npmPrefix, "bin");
12921
13402
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
12922
13403
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
12923
13404
  if (pkgName === "@adhdev/daemon-standalone") {
@@ -12925,25 +13406,25 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
12925
13406
  }
12926
13407
  if (pkgName.startsWith("@")) {
12927
13408
  const [scope, name] = pkgName.split("/");
12928
- const scopeDir = path13.join(npmRoot, scope);
13409
+ const scopeDir = path15.join(npmRoot, scope);
12929
13410
  if (!fs8.existsSync(scopeDir)) return;
12930
13411
  for (const entry of fs8.readdirSync(scopeDir)) {
12931
13412
  if (!entry.startsWith(`.${name}-`)) continue;
12932
- fs8.rmSync(path13.join(scopeDir, entry), { recursive: true, force: true });
12933
- appendUpgradeLog(`Removed stale scoped staging dir: ${path13.join(scopeDir, entry)}`);
13413
+ fs8.rmSync(path15.join(scopeDir, entry), { recursive: true, force: true });
13414
+ appendUpgradeLog(`Removed stale scoped staging dir: ${path15.join(scopeDir, entry)}`);
12934
13415
  }
12935
13416
  } else {
12936
13417
  for (const entry of fs8.readdirSync(npmRoot)) {
12937
13418
  if (!entry.startsWith(`.${pkgName}-`)) continue;
12938
- fs8.rmSync(path13.join(npmRoot, entry), { recursive: true, force: true });
12939
- appendUpgradeLog(`Removed stale staging dir: ${path13.join(npmRoot, entry)}`);
13419
+ fs8.rmSync(path15.join(npmRoot, entry), { recursive: true, force: true });
13420
+ appendUpgradeLog(`Removed stale staging dir: ${path15.join(npmRoot, entry)}`);
12940
13421
  }
12941
13422
  }
12942
13423
  if (fs8.existsSync(binDir)) {
12943
13424
  for (const entry of fs8.readdirSync(binDir)) {
12944
13425
  if (![...binNames].some((name) => entry.startsWith(`.${name}-`))) continue;
12945
- fs8.rmSync(path13.join(binDir, entry), { recursive: true, force: true });
12946
- appendUpgradeLog(`Removed stale bin staging entry: ${path13.join(binDir, entry)}`);
13426
+ fs8.rmSync(path15.join(binDir, entry), { recursive: true, force: true });
13427
+ appendUpgradeLog(`Removed stale bin staging entry: ${path15.join(binDir, entry)}`);
12947
13428
  }
12948
13429
  }
12949
13430
  }
@@ -12985,7 +13466,7 @@ async function runDaemonUpgradeHelper(payload) {
12985
13466
  appendUpgradeLog(installOutput.trim());
12986
13467
  }
12987
13468
  if (process.platform === "win32") {
12988
- await new Promise((resolve9) => setTimeout(resolve9, 500));
13469
+ await new Promise((resolve12) => setTimeout(resolve12, 500));
12989
13470
  cleanupStaleGlobalInstallDirs(payload.packageName);
12990
13471
  appendUpgradeLog("Post-install staging cleanup complete");
12991
13472
  }
@@ -13181,6 +13662,15 @@ var DaemonCommandRouter = class {
13181
13662
  const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
13182
13663
  return { success: true, record };
13183
13664
  }
13665
+ case "session_host_prune_duplicate_sessions": {
13666
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13667
+ const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
13668
+ providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
13669
+ workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
13670
+ dryRun: args?.dryRun === true
13671
+ });
13672
+ return { success: true, result };
13673
+ }
13184
13674
  case "session_host_acquire_write": {
13185
13675
  if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
13186
13676
  const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
@@ -13263,6 +13753,12 @@ var DaemonCommandRouter = class {
13263
13753
  if (!ideType) throw new Error("ideType required");
13264
13754
  const killProcess = args?.killProcess !== false;
13265
13755
  await this.stopIde(ideType, killProcess);
13756
+ try {
13757
+ const results = await detectIDEs(this.deps.providerLoader);
13758
+ this.deps.detectedIdes.value = results;
13759
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
13760
+ } catch {
13761
+ }
13266
13762
  return { success: true, ideType, stopped: true, processKilled: killProcess };
13267
13763
  }
13268
13764
  // ─── IDE restart ───
@@ -13305,6 +13801,12 @@ var DaemonCommandRouter = class {
13305
13801
  }
13306
13802
  }
13307
13803
  this.deps.onIdeConnected?.();
13804
+ try {
13805
+ const results = await detectIDEs(this.deps.providerLoader);
13806
+ this.deps.detectedIdes.value = results;
13807
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
13808
+ } catch {
13809
+ }
13308
13810
  if (result.success && resolvedWorkspace) {
13309
13811
  try {
13310
13812
  const next = appendRecentActivity(loadState(), {
@@ -13332,8 +13834,9 @@ var DaemonCommandRouter = class {
13332
13834
  }
13333
13835
  // ─── Detect IDEs ───
13334
13836
  case "detect_ides": {
13335
- const results = await detectIDEs();
13837
+ const results = await detectIDEs(this.deps.providerLoader);
13336
13838
  this.deps.detectedIdes.value = results;
13839
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
13337
13840
  return { success: true, detectedInfo: results };
13338
13841
  }
13339
13842
  // ─── Set User Name ───
@@ -13753,6 +14256,51 @@ var ProviderStreamAdapter = class {
13753
14256
  isTransportError(reason) {
13754
14257
  return /Session with given id not found/i.test(reason) || /CDP not connected/i.test(reason) || /Target closed/i.test(reason) || /WebSocket not open/i.test(reason) || /not connected/i.test(reason) || /execution context/i.test(reason) || /Cannot find context with specified id/i.test(reason);
13755
14258
  }
14259
+ titlesMatch(actual, expected) {
14260
+ const lhs = actual.trim().toLowerCase();
14261
+ const rhs = expected.trim().toLowerCase();
14262
+ if (!lhs || !rhs) return false;
14263
+ return lhs === rhs || lhs.includes(rhs) || rhs.includes(lhs);
14264
+ }
14265
+ messageCount(state) {
14266
+ return Array.isArray(state?.messages) ? state.messages.length : 0;
14267
+ }
14268
+ lastMessageSignature(state) {
14269
+ const messages = Array.isArray(state?.messages) ? state.messages : [];
14270
+ const last = messages[messages.length - 1];
14271
+ if (!last) return "";
14272
+ return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
14273
+ }
14274
+ async verifySendOutcome(evaluate, before) {
14275
+ const beforeCount = this.messageCount(before);
14276
+ const beforeSignature = this.lastMessageSignature(before);
14277
+ for (let attempt = 0; attempt < 12; attempt += 1) {
14278
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
14279
+ let state;
14280
+ try {
14281
+ state = await this.readChat(evaluate);
14282
+ } catch {
14283
+ continue;
14284
+ }
14285
+ if (state.status === "waiting_approval") {
14286
+ return true;
14287
+ }
14288
+ const afterCount = this.messageCount(state);
14289
+ const afterSignature = this.lastMessageSignature(state);
14290
+ if (afterCount > beforeCount) return true;
14291
+ if (afterSignature && afterSignature !== beforeSignature) return true;
14292
+ }
14293
+ return false;
14294
+ }
14295
+ async readStableBaselineState(evaluate) {
14296
+ const first = await this.readChat(evaluate);
14297
+ if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
14298
+ return first;
14299
+ }
14300
+ await new Promise((resolve12) => setTimeout(resolve12, 150));
14301
+ const second = await this.readChat(evaluate);
14302
+ return this.messageCount(second) >= this.messageCount(first) ? second : first;
14303
+ }
13756
14304
  async readChat(evaluate) {
13757
14305
  const script = this.callScript("readChat");
13758
14306
  if (!script) return this.errorState("readChat script not available");
@@ -13778,6 +14326,9 @@ var ProviderStreamAdapter = class {
13778
14326
  mode: data.mode,
13779
14327
  activeModal: data.activeModal
13780
14328
  };
14329
+ if (typeof data.title === "string" && data.title.trim()) {
14330
+ state.title = data.title.trim();
14331
+ }
13781
14332
  const controlValues = extractProviderControlValues(this.provider.controls, data);
13782
14333
  if (controlValues) state.controlValues = controlValues;
13783
14334
  const effects = normalizeProviderEffects(data);
@@ -13801,6 +14352,12 @@ var ProviderStreamAdapter = class {
13801
14352
  }
13802
14353
  }
13803
14354
  async sendMessage(evaluate, text) {
14355
+ let beforeState = null;
14356
+ try {
14357
+ beforeState = await this.readStableBaselineState(evaluate);
14358
+ } catch {
14359
+ beforeState = null;
14360
+ }
13804
14361
  const params = { message: text };
13805
14362
  const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
13806
14363
  if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
@@ -13818,7 +14375,9 @@ var ProviderStreamAdapter = class {
13818
14375
  }
13819
14376
  if (parsed && typeof parsed === "object") {
13820
14377
  if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
13821
- return;
14378
+ const verified = await this.verifySendOutcome(evaluate, beforeState);
14379
+ if (verified) return;
14380
+ throw new Error(`[${this.agentName}] sendMessage was not observed in chat state`);
13822
14381
  }
13823
14382
  if (typeof parsed.error === "string" && parsed.error.trim()) {
13824
14383
  throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
@@ -13829,7 +14388,15 @@ var ProviderStreamAdapter = class {
13829
14388
  async resolveAction(evaluate, action, button) {
13830
14389
  const script = this.callScript("resolveAction", { action, button });
13831
14390
  if (!script) return false;
13832
- return await evaluate(script) === true;
14391
+ const result = await evaluate(script);
14392
+ const parsed = this.parseMaybeJson(result);
14393
+ if (parsed === true) return true;
14394
+ if (typeof parsed === "string") {
14395
+ const normalized = parsed.trim().toLowerCase();
14396
+ return normalized === "ok" || normalized === "success" || normalized === "true" || normalized === "resolved" || normalized === "approved" || normalized === "rejected";
14397
+ }
14398
+ if (!parsed || typeof parsed !== "object") return false;
14399
+ return parsed.resolved === true || parsed.success === true || parsed.ok === true || parsed.found === true;
13833
14400
  }
13834
14401
  async newSession(evaluate) {
13835
14402
  const script = this.callScript("newSession");
@@ -13847,7 +14414,10 @@ var ProviderStreamAdapter = class {
13847
14414
  const raw = await evaluate(script, 1e4);
13848
14415
  const data = typeof raw === "string" ? JSON.parse(raw) : raw;
13849
14416
  if (data?.error) return [];
13850
- return Array.isArray(data) ? data : [];
14417
+ if (Array.isArray(data)) return data;
14418
+ if (Array.isArray(data?.sessions)) return data.sessions;
14419
+ if (Array.isArray(data?.chats)) return data.chats;
14420
+ return [];
13851
14421
  } catch {
13852
14422
  return [];
13853
14423
  }
@@ -13855,7 +14425,24 @@ var ProviderStreamAdapter = class {
13855
14425
  async switchSession(evaluate, sessionId) {
13856
14426
  const script = this.callScript("switchSession", sessionId);
13857
14427
  if (!script) return false;
13858
- return await evaluate(script, 1e4) === true;
14428
+ const raw = await evaluate(script, 1e4);
14429
+ const data = this.parseMaybeJson(raw);
14430
+ if (data === true) return true;
14431
+ if (typeof data === "string") {
14432
+ const normalized = data.trim().toLowerCase();
14433
+ return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
14434
+ }
14435
+ if (data && typeof data === "object") {
14436
+ if (data.switched === true || data.success === true || data.ok === true) return true;
14437
+ if (typeof data.error === "string" && data.error.trim()) return false;
14438
+ }
14439
+ for (let attempt = 0; attempt < 6; attempt += 1) {
14440
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
14441
+ const state = await this.readChat(evaluate);
14442
+ const title = typeof state.title === "string" ? state.title : "";
14443
+ if (this.titlesMatch(title, sessionId)) return true;
14444
+ }
14445
+ return false;
13859
14446
  }
13860
14447
  async focusEditor(evaluate) {
13861
14448
  const script = this.callScript("focusEditor");
@@ -14061,7 +14648,7 @@ var DaemonAgentStreamManager = class {
14061
14648
  return false;
14062
14649
  }
14063
14650
  }
14064
- async resolveSessionAction(cdp, sessionId, action) {
14651
+ async resolveSessionAction(cdp, sessionId, action, button) {
14065
14652
  await this.ensureSessionPanelOpen(sessionId);
14066
14653
  const target = this.getSessionTarget(sessionId);
14067
14654
  if (!target?.parentSessionId) return false;
@@ -14071,7 +14658,7 @@ var DaemonAgentStreamManager = class {
14071
14658
  if (!agent) return false;
14072
14659
  try {
14073
14660
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
14074
- return await agent.adapter.resolveAction(evaluate, action);
14661
+ return await agent.adapter.resolveAction(evaluate, action, button);
14075
14662
  } catch (e) {
14076
14663
  this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
14077
14664
  return false;
@@ -14545,11 +15132,11 @@ var ProviderInstanceManager = class {
14545
15132
 
14546
15133
  // src/providers/version-archive.ts
14547
15134
  import * as fs10 from "fs";
14548
- import * as path14 from "path";
15135
+ import * as path16 from "path";
14549
15136
  import * as os16 from "os";
14550
15137
  import { execSync as execSync5 } from "child_process";
14551
15138
  import { platform as platform7 } from "os";
14552
- var ARCHIVE_PATH = path14.join(os16.homedir(), ".adhdev", "version-history.json");
15139
+ var ARCHIVE_PATH = path16.join(os16.homedir(), ".adhdev", "version-history.json");
14553
15140
  var MAX_ENTRIES_PER_PROVIDER = 20;
14554
15141
  var VersionArchive = class {
14555
15142
  history = {};
@@ -14596,7 +15183,7 @@ var VersionArchive = class {
14596
15183
  }
14597
15184
  save() {
14598
15185
  try {
14599
- fs10.mkdirSync(path14.dirname(ARCHIVE_PATH), { recursive: true });
15186
+ fs10.mkdirSync(path16.dirname(ARCHIVE_PATH), { recursive: true });
14600
15187
  fs10.writeFileSync(ARCHIVE_PATH, JSON.stringify(this.history, null, 2));
14601
15188
  } catch {
14602
15189
  }
@@ -14637,7 +15224,7 @@ function checkPathExists2(paths) {
14637
15224
  for (const p of paths) {
14638
15225
  if (p.includes("*")) {
14639
15226
  const home = os16.homedir();
14640
- const resolved = p.replace(/\*/g, home.split(path14.sep).pop() || "");
15227
+ const resolved = p.replace(/\*/g, home.split(path16.sep).pop() || "");
14641
15228
  if (fs10.existsSync(resolved)) return resolved;
14642
15229
  } else {
14643
15230
  if (fs10.existsSync(p)) return p;
@@ -14647,7 +15234,7 @@ function checkPathExists2(paths) {
14647
15234
  }
14648
15235
  function getMacAppVersion(appPath) {
14649
15236
  if (platform7() !== "darwin" || !appPath.endsWith(".app")) return null;
14650
- const plistPath = path14.join(appPath, "Contents", "Info.plist");
15237
+ const plistPath = path16.join(appPath, "Contents", "Info.plist");
14651
15238
  if (!fs10.existsSync(plistPath)) return null;
14652
15239
  const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
14653
15240
  return raw || null;
@@ -14674,7 +15261,7 @@ async function detectAllVersions(loader, archive) {
14674
15261
  const cliBin = provider.cli ? findBinary2(provider.cli) : null;
14675
15262
  let resolvedBin = cliBin;
14676
15263
  if (!resolvedBin && appPath && currentOs === "darwin") {
14677
- const bundled = path14.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
15264
+ const bundled = path16.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
14678
15265
  if (provider.cli && fs10.existsSync(bundled)) resolvedBin = bundled;
14679
15266
  }
14680
15267
  info.installed = !!(appPath || resolvedBin);
@@ -14715,7 +15302,7 @@ async function detectAllVersions(loader, archive) {
14715
15302
  // src/daemon/dev-server.ts
14716
15303
  import * as http2 from "http";
14717
15304
  import * as fs14 from "fs";
14718
- import * as path18 from "path";
15305
+ import * as path20 from "path";
14719
15306
 
14720
15307
  // src/daemon/scaffold-template.ts
14721
15308
  function generateFiles(type, name, category, opts = {}) {
@@ -15052,7 +15639,7 @@ init_logger();
15052
15639
  // src/daemon/dev-cdp-handlers.ts
15053
15640
  init_logger();
15054
15641
  import * as fs11 from "fs";
15055
- import * as path15 from "path";
15642
+ import * as path17 from "path";
15056
15643
  async function handleCdpEvaluate(ctx, req, res) {
15057
15644
  const body = await ctx.readBody(req);
15058
15645
  const { expression, timeout, ideType } = body;
@@ -15230,17 +15817,17 @@ async function handleScriptHints(ctx, type, _req, res) {
15230
15817
  return;
15231
15818
  }
15232
15819
  let scriptsPath = "";
15233
- const directScripts = path15.join(dir, "scripts.js");
15820
+ const directScripts = path17.join(dir, "scripts.js");
15234
15821
  if (fs11.existsSync(directScripts)) {
15235
15822
  scriptsPath = directScripts;
15236
15823
  } else {
15237
- const scriptsDir = path15.join(dir, "scripts");
15824
+ const scriptsDir = path17.join(dir, "scripts");
15238
15825
  if (fs11.existsSync(scriptsDir)) {
15239
15826
  const versions = fs11.readdirSync(scriptsDir).filter((d) => {
15240
- return fs11.statSync(path15.join(scriptsDir, d)).isDirectory();
15827
+ return fs11.statSync(path17.join(scriptsDir, d)).isDirectory();
15241
15828
  }).sort().reverse();
15242
15829
  for (const ver of versions) {
15243
- const p = path15.join(scriptsDir, ver, "scripts.js");
15830
+ const p = path17.join(scriptsDir, ver, "scripts.js");
15244
15831
  if (fs11.existsSync(p)) {
15245
15832
  scriptsPath = p;
15246
15833
  break;
@@ -16059,7 +16646,7 @@ async function handleDomContext(ctx, type, req, res) {
16059
16646
 
16060
16647
  // src/daemon/dev-cli-debug.ts
16061
16648
  import * as fs12 from "fs";
16062
- import * as path16 from "path";
16649
+ import * as path18 from "path";
16063
16650
  function slugifyFixtureName(value) {
16064
16651
  const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
16065
16652
  return normalized || `fixture-${Date.now()}`;
@@ -16069,11 +16656,11 @@ function getCliFixtureDir(ctx, type) {
16069
16656
  if (!providerDir) {
16070
16657
  throw new Error(`Provider directory not found for '${type}'`);
16071
16658
  }
16072
- return path16.join(providerDir, "fixtures");
16659
+ return path18.join(providerDir, "fixtures");
16073
16660
  }
16074
16661
  function readCliFixture(ctx, type, name) {
16075
16662
  const fixtureDir = getCliFixtureDir(ctx, type);
16076
- const filePath = path16.join(fixtureDir, `${name}.json`);
16663
+ const filePath = path18.join(fixtureDir, `${name}.json`);
16077
16664
  if (!fs12.existsSync(filePath)) {
16078
16665
  throw new Error(`Fixture not found: ${filePath}`);
16079
16666
  }
@@ -16233,7 +16820,7 @@ function getCliTargetBundle(ctx, type, instanceId) {
16233
16820
  return { target, instance, adapter };
16234
16821
  }
16235
16822
  function sleep(ms) {
16236
- return new Promise((resolve9) => setTimeout(resolve9, ms));
16823
+ return new Promise((resolve12) => setTimeout(resolve12, ms));
16237
16824
  }
16238
16825
  async function waitForCliReady(ctx, type, instanceId, timeoutMs) {
16239
16826
  const startedAt = Date.now();
@@ -16832,7 +17419,7 @@ async function handleCliFixtureCapture(ctx, req, res) {
16832
17419
  },
16833
17420
  notes: typeof body?.notes === "string" ? body.notes : void 0
16834
17421
  };
16835
- const filePath = path16.join(fixtureDir, `${name}.json`);
17422
+ const filePath = path18.join(fixtureDir, `${name}.json`);
16836
17423
  fs12.writeFileSync(filePath, JSON.stringify(fixture, null, 2));
16837
17424
  ctx.json(res, 200, {
16838
17425
  saved: true,
@@ -16856,7 +17443,7 @@ async function handleCliFixtureList(ctx, type, _req, res) {
16856
17443
  return;
16857
17444
  }
16858
17445
  const fixtures = fs12.readdirSync(fixtureDir).filter((file) => file.endsWith(".json")).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" })).map((file) => {
16859
- const fullPath = path16.join(fixtureDir, file);
17446
+ const fullPath = path18.join(fixtureDir, file);
16860
17447
  try {
16861
17448
  const raw = JSON.parse(fs12.readFileSync(fullPath, "utf-8"));
16862
17449
  return {
@@ -16992,7 +17579,7 @@ async function handleCliRaw(ctx, req, res) {
16992
17579
 
16993
17580
  // src/daemon/dev-auto-implement.ts
16994
17581
  import * as fs13 from "fs";
16995
- import * as path17 from "path";
17582
+ import * as path19 from "path";
16996
17583
  import * as os17 from "os";
16997
17584
  function getAutoImplPid(ctx) {
16998
17585
  const proc = ctx.autoImplProcess;
@@ -17032,22 +17619,22 @@ function getLatestScriptVersionDir(scriptsDir) {
17032
17619
  if (!fs13.existsSync(scriptsDir)) return null;
17033
17620
  const versions = fs13.readdirSync(scriptsDir).filter((d) => {
17034
17621
  try {
17035
- return fs13.statSync(path17.join(scriptsDir, d)).isDirectory();
17622
+ return fs13.statSync(path19.join(scriptsDir, d)).isDirectory();
17036
17623
  } catch {
17037
17624
  return false;
17038
17625
  }
17039
17626
  }).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
17040
17627
  if (versions.length === 0) return null;
17041
- return path17.join(scriptsDir, versions[0]);
17628
+ return path19.join(scriptsDir, versions[0]);
17042
17629
  }
17043
17630
  function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
17044
- const canonicalUserDir = path17.resolve(ctx.providerLoader.getUserProviderDir(category, type));
17045
- const desiredDir = requestedDir ? path17.resolve(requestedDir) : canonicalUserDir;
17046
- const upstreamRoot = path17.resolve(ctx.providerLoader.getUpstreamDir());
17047
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path17.sep}`)) {
17631
+ const canonicalUserDir = path19.resolve(ctx.providerLoader.getUserProviderDir(category, type));
17632
+ const desiredDir = requestedDir ? path19.resolve(requestedDir) : canonicalUserDir;
17633
+ const upstreamRoot = path19.resolve(ctx.providerLoader.getUpstreamDir());
17634
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path19.sep}`)) {
17048
17635
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
17049
17636
  }
17050
- if (path17.basename(desiredDir) !== type) {
17637
+ if (path19.basename(desiredDir) !== type) {
17051
17638
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
17052
17639
  }
17053
17640
  const sourceDir = ctx.findProviderDir(type);
@@ -17055,11 +17642,11 @@ function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
17055
17642
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
17056
17643
  }
17057
17644
  if (!fs13.existsSync(desiredDir)) {
17058
- fs13.mkdirSync(path17.dirname(desiredDir), { recursive: true });
17645
+ fs13.mkdirSync(path19.dirname(desiredDir), { recursive: true });
17059
17646
  fs13.cpSync(sourceDir, desiredDir, { recursive: true });
17060
17647
  ctx.log(`Auto-implement writable copy created: ${desiredDir}`);
17061
17648
  }
17062
- const providerJson = path17.join(desiredDir, "provider.json");
17649
+ const providerJson = path19.join(desiredDir, "provider.json");
17063
17650
  if (!fs13.existsSync(providerJson)) {
17064
17651
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
17065
17652
  }
@@ -17082,13 +17669,13 @@ function loadAutoImplReferenceScripts(ctx, referenceType) {
17082
17669
  const refDir = ctx.findProviderDir(referenceType);
17083
17670
  if (!refDir || !fs13.existsSync(refDir)) return {};
17084
17671
  const referenceScripts = {};
17085
- const scriptsDir = path17.join(refDir, "scripts");
17672
+ const scriptsDir = path19.join(refDir, "scripts");
17086
17673
  const latestDir = getLatestScriptVersionDir(scriptsDir);
17087
17674
  if (!latestDir) return referenceScripts;
17088
17675
  for (const file of fs13.readdirSync(latestDir)) {
17089
17676
  if (!file.endsWith(".js")) continue;
17090
17677
  try {
17091
- referenceScripts[file] = fs13.readFileSync(path17.join(latestDir, file), "utf-8");
17678
+ referenceScripts[file] = fs13.readFileSync(path19.join(latestDir, file), "utf-8");
17092
17679
  } catch {
17093
17680
  }
17094
17681
  }
@@ -17196,9 +17783,9 @@ async function handleAutoImplement(ctx, type, req, res) {
17196
17783
  });
17197
17784
  const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
17198
17785
  const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
17199
- const tmpDir = path17.join(os17.tmpdir(), "adhdev-autoimpl");
17786
+ const tmpDir = path19.join(os17.tmpdir(), "adhdev-autoimpl");
17200
17787
  if (!fs13.existsSync(tmpDir)) fs13.mkdirSync(tmpDir, { recursive: true });
17201
- const promptFile = path17.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
17788
+ const promptFile = path19.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
17202
17789
  fs13.writeFileSync(promptFile, prompt, "utf-8");
17203
17790
  ctx.log(`Auto-implement prompt written to ${promptFile} (${prompt.length} chars)`);
17204
17791
  const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
@@ -17625,7 +18212,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17625
18212
  setMode: "set_mode.js"
17626
18213
  };
17627
18214
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
17628
- const scriptsDir = path17.join(providerDir, "scripts");
18215
+ const scriptsDir = path19.join(providerDir, "scripts");
17629
18216
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
17630
18217
  if (latestScriptsDir) {
17631
18218
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -17636,7 +18223,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17636
18223
  for (const file of fs13.readdirSync(latestScriptsDir)) {
17637
18224
  if (file.endsWith(".js") && targetFileNames.has(file)) {
17638
18225
  try {
17639
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
18226
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17640
18227
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
17641
18228
  lines.push("```javascript");
17642
18229
  lines.push(content);
@@ -17653,7 +18240,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17653
18240
  lines.push("");
17654
18241
  for (const file of refFiles) {
17655
18242
  try {
17656
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
18243
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17657
18244
  lines.push(`### \`${file}\` \u{1F512}`);
17658
18245
  lines.push("```javascript");
17659
18246
  lines.push(content);
@@ -17694,10 +18281,10 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
17694
18281
  lines.push("");
17695
18282
  }
17696
18283
  }
17697
- const docsDir = path17.join(providerDir, "../../docs");
18284
+ const docsDir = path19.join(providerDir, "../../docs");
17698
18285
  const loadGuide = (name) => {
17699
18286
  try {
17700
- const p = path17.join(docsDir, name);
18287
+ const p = path19.join(docsDir, name);
17701
18288
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
17702
18289
  } catch {
17703
18290
  }
@@ -17932,7 +18519,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17932
18519
  parseApproval: "parse_approval.js"
17933
18520
  };
17934
18521
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
17935
- const scriptsDir = path17.join(providerDir, "scripts");
18522
+ const scriptsDir = path19.join(providerDir, "scripts");
17936
18523
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
17937
18524
  if (latestScriptsDir) {
17938
18525
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -17944,7 +18531,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17944
18531
  if (!file.endsWith(".js")) continue;
17945
18532
  if (!targetFileNames.has(file)) continue;
17946
18533
  try {
17947
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
18534
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17948
18535
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
17949
18536
  lines.push("```javascript");
17950
18537
  lines.push(content);
@@ -17960,7 +18547,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17960
18547
  lines.push("");
17961
18548
  for (const file of refFiles) {
17962
18549
  try {
17963
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file), "utf-8");
18550
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
17964
18551
  lines.push(`### \`${file}\` \u{1F512}`);
17965
18552
  lines.push("```javascript");
17966
18553
  lines.push(content);
@@ -17993,10 +18580,10 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
17993
18580
  lines.push("");
17994
18581
  }
17995
18582
  }
17996
- const docsDir = path17.join(providerDir, "../../docs");
18583
+ const docsDir = path19.join(providerDir, "../../docs");
17997
18584
  const loadGuide = (name) => {
17998
18585
  try {
17999
- const p = path17.join(docsDir, name);
18586
+ const p = path19.join(docsDir, name);
18000
18587
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
18001
18588
  } catch {
18002
18589
  }
@@ -18407,8 +18994,8 @@ var DevServer = class _DevServer {
18407
18994
  }
18408
18995
  getEndpointList() {
18409
18996
  return this.routes.map((r) => {
18410
- const path19 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
18411
- return `${r.method.padEnd(5)} ${path19}`;
18997
+ const path21 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
18998
+ return `${r.method.padEnd(5)} ${path21}`;
18412
18999
  });
18413
19000
  }
18414
19001
  async start(port = DEV_SERVER_PORT) {
@@ -18439,15 +19026,15 @@ var DevServer = class _DevServer {
18439
19026
  this.json(res, 500, { error: e.message });
18440
19027
  }
18441
19028
  });
18442
- return new Promise((resolve9, reject) => {
19029
+ return new Promise((resolve12, reject) => {
18443
19030
  this.server.listen(port, "127.0.0.1", () => {
18444
19031
  this.log(`Dev server listening on http://127.0.0.1:${port}`);
18445
- resolve9();
19032
+ resolve12();
18446
19033
  });
18447
19034
  this.server.on("error", (e) => {
18448
19035
  if (e.code === "EADDRINUSE") {
18449
19036
  this.log(`Port ${port} in use, skipping dev server`);
18450
- resolve9();
19037
+ resolve12();
18451
19038
  } else {
18452
19039
  reject(e);
18453
19040
  }
@@ -18530,20 +19117,20 @@ var DevServer = class _DevServer {
18530
19117
  child.stderr?.on("data", (d) => {
18531
19118
  stderr += d.toString().slice(0, 2e3);
18532
19119
  });
18533
- await new Promise((resolve9) => {
19120
+ await new Promise((resolve12) => {
18534
19121
  const timer = setTimeout(() => {
18535
19122
  child.kill();
18536
- resolve9();
19123
+ resolve12();
18537
19124
  }, 3e3);
18538
19125
  child.on("exit", () => {
18539
19126
  clearTimeout(timer);
18540
- resolve9();
19127
+ resolve12();
18541
19128
  });
18542
19129
  child.stdout?.once("data", () => {
18543
19130
  setTimeout(() => {
18544
19131
  child.kill();
18545
19132
  clearTimeout(timer);
18546
- resolve9();
19133
+ resolve12();
18547
19134
  }, 500);
18548
19135
  });
18549
19136
  });
@@ -18690,12 +19277,12 @@ var DevServer = class _DevServer {
18690
19277
  // ─── DevConsole SPA ───
18691
19278
  getConsoleDistDir() {
18692
19279
  const candidates = [
18693
- path18.resolve(__dirname, "../../web-devconsole/dist"),
18694
- path18.resolve(__dirname, "../../../web-devconsole/dist"),
18695
- path18.join(process.cwd(), "packages/web-devconsole/dist")
19280
+ path20.resolve(__dirname, "../../web-devconsole/dist"),
19281
+ path20.resolve(__dirname, "../../../web-devconsole/dist"),
19282
+ path20.join(process.cwd(), "packages/web-devconsole/dist")
18696
19283
  ];
18697
19284
  for (const dir of candidates) {
18698
- if (fs14.existsSync(path18.join(dir, "index.html"))) return dir;
19285
+ if (fs14.existsSync(path20.join(dir, "index.html"))) return dir;
18699
19286
  }
18700
19287
  return null;
18701
19288
  }
@@ -18705,7 +19292,7 @@ var DevServer = class _DevServer {
18705
19292
  this.json(res, 500, { error: "DevConsole not found. Run: npm run build -w packages/web-devconsole" });
18706
19293
  return;
18707
19294
  }
18708
- const htmlPath = path18.join(distDir, "index.html");
19295
+ const htmlPath = path20.join(distDir, "index.html");
18709
19296
  try {
18710
19297
  const html = fs14.readFileSync(htmlPath, "utf-8");
18711
19298
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
@@ -18730,15 +19317,15 @@ var DevServer = class _DevServer {
18730
19317
  this.json(res, 404, { error: "Not found" });
18731
19318
  return;
18732
19319
  }
18733
- const safePath = path18.normalize(pathname).replace(/^\.\.\//, "");
18734
- const filePath = path18.join(distDir, safePath);
19320
+ const safePath = path20.normalize(pathname).replace(/^\.\.\//, "");
19321
+ const filePath = path20.join(distDir, safePath);
18735
19322
  if (!filePath.startsWith(distDir)) {
18736
19323
  this.json(res, 403, { error: "Forbidden" });
18737
19324
  return;
18738
19325
  }
18739
19326
  try {
18740
19327
  const content = fs14.readFileSync(filePath);
18741
- const ext = path18.extname(filePath);
19328
+ const ext = path20.extname(filePath);
18742
19329
  const contentType = _DevServer.MIME_MAP[ext] || "application/octet-stream";
18743
19330
  res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=31536000, immutable" });
18744
19331
  res.end(content);
@@ -18851,9 +19438,9 @@ var DevServer = class _DevServer {
18851
19438
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
18852
19439
  if (entry.isDirectory()) {
18853
19440
  files.push({ path: rel, size: 0, type: "dir" });
18854
- scan(path18.join(d, entry.name), rel);
19441
+ scan(path20.join(d, entry.name), rel);
18855
19442
  } else {
18856
- const stat = fs14.statSync(path18.join(d, entry.name));
19443
+ const stat = fs14.statSync(path20.join(d, entry.name));
18857
19444
  files.push({ path: rel, size: stat.size, type: "file" });
18858
19445
  }
18859
19446
  }
@@ -18876,7 +19463,7 @@ var DevServer = class _DevServer {
18876
19463
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
18877
19464
  return;
18878
19465
  }
18879
- const fullPath = path18.resolve(dir, path18.normalize(filePath));
19466
+ const fullPath = path20.resolve(dir, path20.normalize(filePath));
18880
19467
  if (!fullPath.startsWith(dir)) {
18881
19468
  this.json(res, 403, { error: "Forbidden" });
18882
19469
  return;
@@ -18901,14 +19488,14 @@ var DevServer = class _DevServer {
18901
19488
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
18902
19489
  return;
18903
19490
  }
18904
- const fullPath = path18.resolve(dir, path18.normalize(filePath));
19491
+ const fullPath = path20.resolve(dir, path20.normalize(filePath));
18905
19492
  if (!fullPath.startsWith(dir)) {
18906
19493
  this.json(res, 403, { error: "Forbidden" });
18907
19494
  return;
18908
19495
  }
18909
19496
  try {
18910
19497
  if (fs14.existsSync(fullPath)) fs14.copyFileSync(fullPath, fullPath + ".bak");
18911
- fs14.mkdirSync(path18.dirname(fullPath), { recursive: true });
19498
+ fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
18912
19499
  fs14.writeFileSync(fullPath, content, "utf-8");
18913
19500
  this.log(`File saved: ${fullPath} (${content.length} chars)`);
18914
19501
  this.providerLoader.reload();
@@ -18925,7 +19512,7 @@ var DevServer = class _DevServer {
18925
19512
  return;
18926
19513
  }
18927
19514
  for (const name of ["scripts.js", "provider.json"]) {
18928
- const p = path18.join(dir, name);
19515
+ const p = path20.join(dir, name);
18929
19516
  if (fs14.existsSync(p)) {
18930
19517
  const source = fs14.readFileSync(p, "utf-8");
18931
19518
  this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
@@ -18946,8 +19533,8 @@ var DevServer = class _DevServer {
18946
19533
  this.json(res, 404, { error: `Provider not found: ${type}` });
18947
19534
  return;
18948
19535
  }
18949
- const target = fs14.existsSync(path18.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
18950
- const targetPath = path18.join(dir, target);
19536
+ const target = fs14.existsSync(path20.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
19537
+ const targetPath = path20.join(dir, target);
18951
19538
  try {
18952
19539
  if (fs14.existsSync(targetPath)) fs14.copyFileSync(targetPath, targetPath + ".bak");
18953
19540
  fs14.writeFileSync(targetPath, source, "utf-8");
@@ -19052,14 +19639,14 @@ var DevServer = class _DevServer {
19052
19639
  child.stderr?.on("data", (d) => {
19053
19640
  stderr += d.toString();
19054
19641
  });
19055
- await new Promise((resolve9) => {
19642
+ await new Promise((resolve12) => {
19056
19643
  const timer = setTimeout(() => {
19057
19644
  child.kill();
19058
- resolve9();
19645
+ resolve12();
19059
19646
  }, timeout);
19060
19647
  child.on("exit", () => {
19061
19648
  clearTimeout(timer);
19062
- resolve9();
19649
+ resolve12();
19063
19650
  });
19064
19651
  });
19065
19652
  const elapsed = Date.now() - start;
@@ -19107,7 +19694,7 @@ var DevServer = class _DevServer {
19107
19694
  }
19108
19695
  let targetDir;
19109
19696
  targetDir = this.providerLoader.getUserProviderDir(category, type);
19110
- const jsonPath = path18.join(targetDir, "provider.json");
19697
+ const jsonPath = path20.join(targetDir, "provider.json");
19111
19698
  if (fs14.existsSync(jsonPath)) {
19112
19699
  this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
19113
19700
  return;
@@ -19119,8 +19706,8 @@ var DevServer = class _DevServer {
19119
19706
  const createdFiles = ["provider.json"];
19120
19707
  if (result.files) {
19121
19708
  for (const [relPath, content] of Object.entries(result.files)) {
19122
- const fullPath = path18.join(targetDir, relPath);
19123
- fs14.mkdirSync(path18.dirname(fullPath), { recursive: true });
19709
+ const fullPath = path20.join(targetDir, relPath);
19710
+ fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
19124
19711
  fs14.writeFileSync(fullPath, content, "utf-8");
19125
19712
  createdFiles.push(relPath);
19126
19713
  }
@@ -19173,22 +19760,22 @@ var DevServer = class _DevServer {
19173
19760
  if (!fs14.existsSync(scriptsDir)) return null;
19174
19761
  const versions = fs14.readdirSync(scriptsDir).filter((d) => {
19175
19762
  try {
19176
- return fs14.statSync(path18.join(scriptsDir, d)).isDirectory();
19763
+ return fs14.statSync(path20.join(scriptsDir, d)).isDirectory();
19177
19764
  } catch {
19178
19765
  return false;
19179
19766
  }
19180
19767
  }).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
19181
19768
  if (versions.length === 0) return null;
19182
- return path18.join(scriptsDir, versions[0]);
19769
+ return path20.join(scriptsDir, versions[0]);
19183
19770
  }
19184
19771
  resolveAutoImplWritableProviderDir(category, type, requestedDir) {
19185
- const canonicalUserDir = path18.resolve(this.providerLoader.getUserProviderDir(category, type));
19186
- const desiredDir = requestedDir ? path18.resolve(requestedDir) : canonicalUserDir;
19187
- const upstreamRoot = path18.resolve(this.providerLoader.getUpstreamDir());
19188
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path18.sep}`)) {
19772
+ const canonicalUserDir = path20.resolve(this.providerLoader.getUserProviderDir(category, type));
19773
+ const desiredDir = requestedDir ? path20.resolve(requestedDir) : canonicalUserDir;
19774
+ const upstreamRoot = path20.resolve(this.providerLoader.getUpstreamDir());
19775
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path20.sep}`)) {
19189
19776
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
19190
19777
  }
19191
- if (path18.basename(desiredDir) !== type) {
19778
+ if (path20.basename(desiredDir) !== type) {
19192
19779
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
19193
19780
  }
19194
19781
  const sourceDir = this.findProviderDir(type);
@@ -19196,11 +19783,11 @@ var DevServer = class _DevServer {
19196
19783
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
19197
19784
  }
19198
19785
  if (!fs14.existsSync(desiredDir)) {
19199
- fs14.mkdirSync(path18.dirname(desiredDir), { recursive: true });
19786
+ fs14.mkdirSync(path20.dirname(desiredDir), { recursive: true });
19200
19787
  fs14.cpSync(sourceDir, desiredDir, { recursive: true });
19201
19788
  this.log(`Auto-implement writable copy created: ${desiredDir}`);
19202
19789
  }
19203
- const providerJson = path18.join(desiredDir, "provider.json");
19790
+ const providerJson = path20.join(desiredDir, "provider.json");
19204
19791
  if (!fs14.existsSync(providerJson)) {
19205
19792
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
19206
19793
  }
@@ -19248,7 +19835,7 @@ var DevServer = class _DevServer {
19248
19835
  setMode: "set_mode.js"
19249
19836
  };
19250
19837
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
19251
- const scriptsDir = path18.join(providerDir, "scripts");
19838
+ const scriptsDir = path20.join(providerDir, "scripts");
19252
19839
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
19253
19840
  if (latestScriptsDir) {
19254
19841
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -19259,7 +19846,7 @@ var DevServer = class _DevServer {
19259
19846
  for (const file of fs14.readdirSync(latestScriptsDir)) {
19260
19847
  if (file.endsWith(".js") && targetFileNames.has(file)) {
19261
19848
  try {
19262
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
19849
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19263
19850
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
19264
19851
  lines.push("```javascript");
19265
19852
  lines.push(content);
@@ -19276,7 +19863,7 @@ var DevServer = class _DevServer {
19276
19863
  lines.push("");
19277
19864
  for (const file of refFiles) {
19278
19865
  try {
19279
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
19866
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19280
19867
  lines.push(`### \`${file}\` \u{1F512}`);
19281
19868
  lines.push("```javascript");
19282
19869
  lines.push(content);
@@ -19317,10 +19904,10 @@ var DevServer = class _DevServer {
19317
19904
  lines.push("");
19318
19905
  }
19319
19906
  }
19320
- const docsDir = path18.join(providerDir, "../../docs");
19907
+ const docsDir = path20.join(providerDir, "../../docs");
19321
19908
  const loadGuide = (name) => {
19322
19909
  try {
19323
- const p = path18.join(docsDir, name);
19910
+ const p = path20.join(docsDir, name);
19324
19911
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
19325
19912
  } catch {
19326
19913
  }
@@ -19494,7 +20081,7 @@ var DevServer = class _DevServer {
19494
20081
  parseApproval: "parse_approval.js"
19495
20082
  };
19496
20083
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
19497
- const scriptsDir = path18.join(providerDir, "scripts");
20084
+ const scriptsDir = path20.join(providerDir, "scripts");
19498
20085
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
19499
20086
  if (latestScriptsDir) {
19500
20087
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -19506,7 +20093,7 @@ var DevServer = class _DevServer {
19506
20093
  if (!file.endsWith(".js")) continue;
19507
20094
  if (!targetFileNames.has(file)) continue;
19508
20095
  try {
19509
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
20096
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19510
20097
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
19511
20098
  lines.push("```javascript");
19512
20099
  lines.push(content);
@@ -19522,7 +20109,7 @@ var DevServer = class _DevServer {
19522
20109
  lines.push("");
19523
20110
  for (const file of refFiles) {
19524
20111
  try {
19525
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file), "utf-8");
20112
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
19526
20113
  lines.push(`### \`${file}\` \u{1F512}`);
19527
20114
  lines.push("```javascript");
19528
20115
  lines.push(content);
@@ -19555,10 +20142,10 @@ var DevServer = class _DevServer {
19555
20142
  lines.push("");
19556
20143
  }
19557
20144
  }
19558
- const docsDir = path18.join(providerDir, "../../docs");
20145
+ const docsDir = path20.join(providerDir, "../../docs");
19559
20146
  const loadGuide = (name) => {
19560
20147
  try {
19561
- const p = path18.join(docsDir, name);
20148
+ const p = path20.join(docsDir, name);
19562
20149
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
19563
20150
  } catch {
19564
20151
  }
@@ -19734,14 +20321,14 @@ data: ${JSON.stringify(msg.data)}
19734
20321
  res.end(JSON.stringify(data, null, 2));
19735
20322
  }
19736
20323
  async readBody(req) {
19737
- return new Promise((resolve9) => {
20324
+ return new Promise((resolve12) => {
19738
20325
  let body = "";
19739
20326
  req.on("data", (chunk) => body += chunk);
19740
20327
  req.on("end", () => {
19741
20328
  try {
19742
- resolve9(JSON.parse(body));
20329
+ resolve12(JSON.parse(body));
19743
20330
  } catch {
19744
- resolve9({});
20331
+ resolve12({});
19745
20332
  }
19746
20333
  });
19747
20334
  });
@@ -20214,7 +20801,7 @@ async function waitForReady(endpoint, timeoutMs = STARTUP_TIMEOUT_MS) {
20214
20801
  const deadline = Date.now() + timeoutMs;
20215
20802
  while (Date.now() < deadline) {
20216
20803
  if (await canConnect(endpoint)) return;
20217
- await new Promise((resolve9) => setTimeout(resolve9, STARTUP_POLL_MS));
20804
+ await new Promise((resolve12) => setTimeout(resolve12, STARTUP_POLL_MS));
20218
20805
  }
20219
20806
  throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
20220
20807
  }
@@ -20370,10 +20957,10 @@ async function installExtension(ide, extension) {
20370
20957
  const buffer = Buffer.from(await res.arrayBuffer());
20371
20958
  const fs15 = await import("fs");
20372
20959
  fs15.writeFileSync(vsixPath, buffer);
20373
- return new Promise((resolve9) => {
20960
+ return new Promise((resolve12) => {
20374
20961
  const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
20375
20962
  exec2(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
20376
- resolve9({
20963
+ resolve12({
20377
20964
  extensionId: extension.id,
20378
20965
  marketplaceId: extension.marketplaceId,
20379
20966
  success: !error,
@@ -20386,11 +20973,11 @@ async function installExtension(ide, extension) {
20386
20973
  } catch (e) {
20387
20974
  }
20388
20975
  }
20389
- return new Promise((resolve9) => {
20976
+ return new Promise((resolve12) => {
20390
20977
  const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
20391
20978
  exec2(cmd, { timeout: 6e4 }, (error, stdout, stderr) => {
20392
20979
  if (error) {
20393
- resolve9({
20980
+ resolve12({
20394
20981
  extensionId: extension.id,
20395
20982
  marketplaceId: extension.marketplaceId,
20396
20983
  success: false,
@@ -20398,7 +20985,7 @@ async function installExtension(ide, extension) {
20398
20985
  error: stderr || error.message
20399
20986
  });
20400
20987
  } else {
20401
- resolve9({
20988
+ resolve12({
20402
20989
  extensionId: extension.id,
20403
20990
  marketplaceId: extension.marketplaceId,
20404
20991
  success: true,
@@ -20534,13 +21121,32 @@ async function initDaemonComponents(config) {
20534
21121
  const detectedIdesRef = { value: [] };
20535
21122
  let agentStreamManager = null;
20536
21123
  let poller = null;
21124
+ const refreshProviderAvailability = async (providerType) => {
21125
+ const targetProvider = providerType ? providerLoader.getMeta(providerLoader.resolveAlias(providerType)) : null;
21126
+ const targetCategory = targetProvider?.category;
21127
+ if (!providerType || targetCategory === "cli" || targetCategory === "acp") {
21128
+ if (providerType && targetProvider) {
21129
+ const detected = await detectCLI(targetProvider.type, providerLoader, { includeVersion: false });
21130
+ providerLoader.setProviderAvailability(targetProvider.type, {
21131
+ installed: !!detected,
21132
+ detectedPath: detected?.path || null
21133
+ });
21134
+ } else {
21135
+ providerLoader.setCliDetectionResults(await detectCLIs(providerLoader, { includeVersion: false }), true);
21136
+ }
21137
+ }
21138
+ if (!providerType || targetCategory === "ide") {
21139
+ detectedIdesRef.value = await detectIDEs(providerLoader);
21140
+ providerLoader.setIdeDetectionResults(detectedIdesRef.value, true);
21141
+ }
21142
+ };
20537
21143
  const cliManager = new DaemonCliManager({
20538
21144
  ...config.cliManagerDeps,
20539
21145
  getInstanceManager: () => instanceManager,
20540
21146
  getSessionRegistry: () => sessionRegistry
20541
21147
  }, providerLoader);
20542
21148
  LOG.info("Init", "Detecting IDEs...");
20543
- detectedIdesRef.value = await detectIDEs();
21149
+ await refreshProviderAvailability();
20544
21150
  const installed = detectedIdesRef.value.filter((i) => i.installed);
20545
21151
  LOG.info("Init", `Found ${installed.length} IDE(s): ${installed.map((i) => i.id).join(", ") || "none"}`);
20546
21152
  const cdpSetupContext = {
@@ -20580,7 +21186,11 @@ async function initDaemonComponents(config) {
20580
21186
  adapters: cliManager.adapters,
20581
21187
  providerLoader,
20582
21188
  instanceManager,
20583
- sessionRegistry
21189
+ sessionRegistry,
21190
+ onProviderSettingChanged: async (providerType) => {
21191
+ await refreshProviderAvailability(providerType);
21192
+ config.onStatusChange?.();
21193
+ }
20584
21194
  });
20585
21195
  agentStreamManager = new DaemonAgentStreamManager(
20586
21196
  LOG.forComponent("AgentStream").asLogFn(),