@adhdev/daemon-standalone 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.
package/dist/index.js CHANGED
@@ -9495,6 +9495,9 @@ var require_dist = __commonJS({
9495
9495
  state.record.lastActivityAt = Date.now();
9496
9496
  return this.cloneRecord(state.record);
9497
9497
  }
9498
+ deleteSession(sessionId) {
9499
+ return this.sessions.delete(sessionId);
9500
+ }
9498
9501
  requireSession(sessionId) {
9499
9502
  const state = this.sessions.get(sessionId);
9500
9503
  if (!state) throw new Error(`Unknown session: ${sessionId}`);
@@ -9676,10 +9679,12 @@ var require_dist = __commonJS({
9676
9679
  env[key] = value;
9677
9680
  }
9678
9681
  for (const key of Object.keys(env)) {
9679
- if (key === "INIT_CWD" || key === "npm_command" || key === "npm_execpath" || key === "npm_node_execpath" || key.startsWith("npm_") || key.startsWith("npm_config_") || key.startsWith("npm_package_") || key.startsWith("npm_lifecycle_") || key.startsWith("PNPM_") || key.startsWith("YARN_") || key.startsWith("BUN_")) {
9682
+ if (key === "INIT_CWD" || key === "npm_command" || key === "npm_execpath" || key === "npm_node_execpath" || key.startsWith("npm_") || key.startsWith("npm_config_") || key.startsWith("npm_package_") || key.startsWith("npm_lifecycle_") || key.startsWith("PNPM_") || key.startsWith("YARN_") || key.startsWith("BUN_") || key.startsWith("VSCODE_") || key.startsWith("ELECTRON_")) {
9680
9683
  delete env[key];
9681
9684
  }
9682
9685
  }
9686
+ delete env.CODEX_THREAD_ID;
9687
+ delete env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE;
9683
9688
  applyTerminalColorEnv(env);
9684
9689
  return env;
9685
9690
  }
@@ -28029,13 +28034,13 @@ var require_dist2 = __commonJS({
28029
28034
  return LOG_DIR;
28030
28035
  }
28031
28036
  function getCurrentDaemonLogPath(date5 = /* @__PURE__ */ new Date()) {
28032
- return path42.join(LOG_DIR, `daemon-${date5.toISOString().slice(0, 10)}.log`);
28037
+ return path6.join(LOG_DIR, `daemon-${date5.toISOString().slice(0, 10)}.log`);
28033
28038
  }
28034
28039
  function checkDateRotation() {
28035
28040
  const today = getDateStr();
28036
28041
  if (today !== currentDate) {
28037
28042
  currentDate = today;
28038
- currentLogFile = path42.join(LOG_DIR, `daemon-${currentDate}.log`);
28043
+ currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
28039
28044
  cleanOldLogs();
28040
28045
  }
28041
28046
  }
@@ -28049,7 +28054,7 @@ var require_dist2 = __commonJS({
28049
28054
  const dateMatch = file2.match(/daemon-(\d{4}-\d{2}-\d{2})/);
28050
28055
  if (dateMatch && dateMatch[1] < cutoffStr) {
28051
28056
  try {
28052
- fs22.unlinkSync(path42.join(LOG_DIR, file2));
28057
+ fs22.unlinkSync(path6.join(LOG_DIR, file2));
28053
28058
  } catch {
28054
28059
  }
28055
28060
  }
@@ -28167,7 +28172,7 @@ var require_dist2 = __commonJS({
28167
28172
  writeToFile(`Log level: ${currentLevel}`);
28168
28173
  }
28169
28174
  var fs22;
28170
- var path42;
28175
+ var path6;
28171
28176
  var os42;
28172
28177
  var LEVEL_NUM;
28173
28178
  var LEVEL_LABEL;
@@ -28190,12 +28195,12 @@ var require_dist2 = __commonJS({
28190
28195
  "src/logging/logger.ts"() {
28191
28196
  "use strict";
28192
28197
  fs22 = __toESM2(require("fs"));
28193
- path42 = __toESM2(require("path"));
28198
+ path6 = __toESM2(require("path"));
28194
28199
  os42 = __toESM2(require("os"));
28195
28200
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
28196
28201
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
28197
28202
  currentLevel = "info";
28198
- LOG_DIR = process.platform === "win32" ? path42.join(process.env.LOCALAPPDATA || process.env.APPDATA || path42.join(os42.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path42.join(os42.homedir(), "Library", "Logs", "adhdev") : path42.join(os42.homedir(), ".local", "share", "adhdev", "logs");
28203
+ LOG_DIR = process.platform === "win32" ? path6.join(process.env.LOCALAPPDATA || process.env.APPDATA || path6.join(os42.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path6.join(os42.homedir(), "Library", "Logs", "adhdev") : path6.join(os42.homedir(), ".local", "share", "adhdev", "logs");
28199
28204
  MAX_LOG_SIZE = 5 * 1024 * 1024;
28200
28205
  MAX_LOG_DAYS = 7;
28201
28206
  try {
@@ -28203,16 +28208,16 @@ var require_dist2 = __commonJS({
28203
28208
  } catch {
28204
28209
  }
28205
28210
  currentDate = getDateStr();
28206
- currentLogFile = path42.join(LOG_DIR, `daemon-${currentDate}.log`);
28211
+ currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
28207
28212
  cleanOldLogs();
28208
28213
  try {
28209
- const oldLog = path42.join(LOG_DIR, "daemon.log");
28214
+ const oldLog = path6.join(LOG_DIR, "daemon.log");
28210
28215
  if (fs22.existsSync(oldLog)) {
28211
28216
  const stat4 = fs22.statSync(oldLog);
28212
28217
  const oldDate = stat4.mtime.toISOString().slice(0, 10);
28213
- fs22.renameSync(oldLog, path42.join(LOG_DIR, `daemon-${oldDate}.log`));
28218
+ fs22.renameSync(oldLog, path6.join(LOG_DIR, `daemon-${oldDate}.log`));
28214
28219
  }
28215
- const oldLogBackup = path42.join(LOG_DIR, "daemon.log.old");
28220
+ const oldLogBackup = path6.join(LOG_DIR, "daemon.log.old");
28216
28221
  if (fs22.existsSync(oldLogBackup)) {
28217
28222
  fs22.unlinkSync(oldLogBackup);
28218
28223
  }
@@ -28244,7 +28249,7 @@ var require_dist2 = __commonJS({
28244
28249
  }
28245
28250
  };
28246
28251
  interceptorInstalled = false;
28247
- LOG_PATH = path42.join(LOG_DIR, `daemon-${getDateStr()}.log`);
28252
+ LOG_PATH = path6.join(LOG_DIR, `daemon-${getDateStr()}.log`);
28248
28253
  }
28249
28254
  });
28250
28255
  function isModuleNotFoundError(error48, ref) {
@@ -28666,16 +28671,22 @@ var require_dist2 = __commonJS({
28666
28671
  return "";
28667
28672
  }
28668
28673
  function findBinary(name) {
28674
+ const trimmed = String(name || "").trim();
28675
+ if (!trimmed) return trimmed;
28676
+ const expanded = trimmed.startsWith("~") ? path9.join(os8.homedir(), trimmed.slice(1)) : trimmed;
28677
+ if (path9.isAbsolute(expanded) || expanded.includes("/") || expanded.includes("\\")) {
28678
+ return path9.isAbsolute(expanded) ? expanded : path9.resolve(expanded);
28679
+ }
28669
28680
  const isWin = os8.platform() === "win32";
28670
28681
  try {
28671
- const cmd = isWin ? `where ${name}` : `which ${name}`;
28682
+ const cmd = isWin ? `where ${trimmed}` : `which ${trimmed}`;
28672
28683
  return (0, import_child_process4.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n")[0].trim();
28673
28684
  } catch {
28674
- return isWin ? `${name}.cmd` : name;
28685
+ return isWin ? `${trimmed}.cmd` : trimmed;
28675
28686
  }
28676
28687
  }
28677
28688
  function isScriptBinary(binaryPath) {
28678
- if (!path7.isAbsolute(binaryPath)) return false;
28689
+ if (!path9.isAbsolute(binaryPath)) return false;
28679
28690
  try {
28680
28691
  const fs15 = require("fs");
28681
28692
  const resolved = fs15.realpathSync(binaryPath);
@@ -28691,7 +28702,7 @@ var require_dist2 = __commonJS({
28691
28702
  }
28692
28703
  }
28693
28704
  function looksLikeMachOOrElf(filePath) {
28694
- if (!path7.isAbsolute(filePath)) return false;
28705
+ if (!path9.isAbsolute(filePath)) return false;
28695
28706
  try {
28696
28707
  const fs15 = require("fs");
28697
28708
  const resolved = fs15.realpathSync(filePath);
@@ -28816,7 +28827,7 @@ var require_dist2 = __commonJS({
28816
28827
  };
28817
28828
  }
28818
28829
  var os8;
28819
- var path7;
28830
+ var path9;
28820
28831
  var import_child_process4;
28821
28832
  var buildCliSpawnEnv;
28822
28833
  var ProviderCliAdapter;
@@ -28824,7 +28835,7 @@ var require_dist2 = __commonJS({
28824
28835
  "src/cli-adapters/provider-cli-adapter.ts"() {
28825
28836
  "use strict";
28826
28837
  os8 = __toESM2(require("os"));
28827
- path7 = __toESM2(require("path"));
28838
+ path9 = __toESM2(require("path"));
28828
28839
  import_child_process4 = require("child_process");
28829
28840
  init_logger();
28830
28841
  init_terminal_screen();
@@ -29179,16 +29190,17 @@ var require_dist2 = __commonJS({
29179
29190
  async spawn() {
29180
29191
  if (this.ptyProcess) return;
29181
29192
  const { spawn: spawnConfig } = this.provider;
29182
- const binaryPath = findBinary(spawnConfig.command);
29193
+ const configuredCommand = typeof this.runtimeSettings.executablePath === "string" && this.runtimeSettings.executablePath.trim() ? this.runtimeSettings.executablePath.trim() : spawnConfig.command;
29194
+ const binaryPath = findBinary(configuredCommand);
29183
29195
  const isWin = os8.platform() === "win32";
29184
29196
  const allArgs = [...spawnConfig.args, ...this.extraArgs];
29185
29197
  LOG2.info("CLI", `[${this.cliType}] Spawning in ${this.workingDir}`);
29186
29198
  this.resetTraceSession();
29187
29199
  let shellCmd;
29188
29200
  let shellArgs;
29189
- const useShellUnix = !isWin && (!!spawnConfig.shell || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
29201
+ const useShellUnix = !isWin && (!!spawnConfig.shell || !path9.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
29190
29202
  const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
29191
- const useShellWin = !!spawnConfig.shell || isCmdShim || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
29203
+ const useShellWin = !!spawnConfig.shell || isCmdShim || !path9.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
29192
29204
  const useShell = isWin ? useShellWin : useShellUnix;
29193
29205
  if (useShell) {
29194
29206
  if (!spawnConfig.shell && !isWin) {
@@ -29433,6 +29445,9 @@ var require_dist2 = __commonJS({
29433
29445
  looksLikeVisibleIdlePrompt(screenText) {
29434
29446
  const text = String(screenText || "");
29435
29447
  if (!text.trim()) return false;
29448
+ 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)) {
29449
+ return true;
29450
+ }
29436
29451
  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);
29437
29452
  }
29438
29453
  findLastMatchingLineIndex(lines, predicate) {
@@ -29562,7 +29577,7 @@ var require_dist2 = __commonJS({
29562
29577
  `[${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)}`
29563
29578
  );
29564
29579
  }
29565
- await new Promise((resolve9) => setTimeout(resolve9, 50));
29580
+ await new Promise((resolve12) => setTimeout(resolve12, 50));
29566
29581
  }
29567
29582
  const finalScreenText = this.terminalScreen.getText() || "";
29568
29583
  LOG2.warn(
@@ -30073,7 +30088,7 @@ ${data.message || ""}`.trim();
30073
30088
  const deadline = Date.now() + 1e4;
30074
30089
  while (this.startupParseGate && Date.now() < deadline) {
30075
30090
  this.resolveStartupState("send_wait");
30076
- await new Promise((resolve9) => setTimeout(resolve9, 50));
30091
+ await new Promise((resolve12) => setTimeout(resolve12, 50));
30077
30092
  }
30078
30093
  }
30079
30094
  await this.waitForInteractivePrompt();
@@ -30298,17 +30313,17 @@ ${data.message || ""}`.trim();
30298
30313
  }
30299
30314
  }
30300
30315
  waitForStopped(timeoutMs) {
30301
- return new Promise((resolve9) => {
30316
+ return new Promise((resolve12) => {
30302
30317
  const startedAt = Date.now();
30303
30318
  const timer = setInterval(() => {
30304
30319
  if (!this.ptyProcess || this.currentStatus === "stopped") {
30305
30320
  clearInterval(timer);
30306
- resolve9(true);
30321
+ resolve12(true);
30307
30322
  return;
30308
30323
  }
30309
30324
  if (Date.now() - startedAt >= timeoutMs) {
30310
30325
  clearInterval(timer);
30311
- resolve9(false);
30326
+ resolve12(false);
30312
30327
  }
30313
30328
  }, 100);
30314
30329
  });
@@ -30995,6 +31010,7 @@ ${data.message || ""}`.trim();
30995
31010
  var import_child_process2 = require("child_process");
30996
31011
  var import_fs3 = require("fs");
30997
31012
  var import_os22 = require("os");
31013
+ var path42 = __toESM2(require("path"));
30998
31014
  var BUILTIN_IDE_DEFINITIONS = [];
30999
31015
  var registeredIDEs = /* @__PURE__ */ new Map();
31000
31016
  function registerIDEDefinition(def) {
@@ -31011,9 +31027,16 @@ ${data.message || ""}`.trim();
31011
31027
  return [...merged.values()];
31012
31028
  }
31013
31029
  function findCliCommand(command) {
31030
+ const trimmed = String(command || "").trim();
31031
+ if (!trimmed) return null;
31032
+ if (path42.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
31033
+ const candidate = trimmed.startsWith("~") ? path42.join((0, import_os22.homedir)(), trimmed.slice(1)) : trimmed;
31034
+ const resolved = path42.isAbsolute(candidate) ? candidate : path42.resolve(candidate);
31035
+ return (0, import_fs3.existsSync)(resolved) ? resolved : null;
31036
+ }
31014
31037
  try {
31015
31038
  const result = (0, import_child_process2.execSync)(
31016
- (0, import_os22.platform)() === "win32" ? `where ${command}` : `which ${command}`,
31039
+ (0, import_os22.platform)() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
31017
31040
  { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
31018
31041
  ).trim();
31019
31042
  return result.split("\n")[0] || null;
@@ -31036,23 +31059,23 @@ ${data.message || ""}`.trim();
31036
31059
  function checkPathExists(paths) {
31037
31060
  const home = (0, import_os22.homedir)();
31038
31061
  for (const p of paths) {
31039
- if (p.includes("*")) {
31062
+ const normalized = p.startsWith("~") ? path42.join(home, p.slice(1)) : p;
31063
+ if (normalized.includes("*")) {
31040
31064
  const username = home.split(/[\\/]/).pop() || "";
31041
- const resolved = p.replace("*", username);
31065
+ const resolved = normalized.replace("*", username);
31042
31066
  if ((0, import_fs3.existsSync)(resolved)) return resolved;
31043
31067
  } else {
31044
- if ((0, import_fs3.existsSync)(p)) return p;
31068
+ if ((0, import_fs3.existsSync)(normalized)) return normalized;
31045
31069
  }
31046
31070
  }
31047
31071
  return null;
31048
31072
  }
31049
- async function detectIDEs() {
31073
+ async function detectIDEs(providerLoader) {
31050
31074
  const os18 = (0, import_os22.platform)();
31051
31075
  const results = [];
31052
31076
  for (const def of getMergedDefinitions()) {
31053
- const cliPath = findCliCommand(def.cli);
31054
- const appPath = checkPathExists(def.paths[os18] || []);
31055
- const installed = !!(cliPath || appPath);
31077
+ const cliPath = findCliCommand(providerLoader?.getIdeCliCommand(def.id, def.cli) || def.cli);
31078
+ const appPath = checkPathExists(providerLoader?.getIdePathCandidates(def.id, def.paths[os18] || []) || []);
31056
31079
  let resolvedCli = cliPath;
31057
31080
  if (!resolvedCli && appPath && os18 === "darwin") {
31058
31081
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
@@ -31075,6 +31098,7 @@ ${data.message || ""}`.trim();
31075
31098
  }
31076
31099
  }
31077
31100
  }
31101
+ const installed = os18 === "darwin" ? !!(resolvedCli || appPath) : !!resolvedCli;
31078
31102
  const version2 = resolvedCli ? getIdeVersion(resolvedCli) : null;
31079
31103
  results.push({
31080
31104
  id: def.id,
@@ -31091,48 +31115,77 @@ ${data.message || ""}`.trim();
31091
31115
  }
31092
31116
  var import_child_process22 = require("child_process");
31093
31117
  var os22 = __toESM2(require("os"));
31118
+ var path52 = __toESM2(require("path"));
31119
+ var import_fs4 = require("fs");
31094
31120
  function parseVersion(raw) {
31095
31121
  const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
31096
31122
  return match ? match[1] : raw.split("\n")[0].slice(0, 100);
31097
31123
  }
31124
+ function shellQuote(value) {
31125
+ if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
31126
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
31127
+ }
31128
+ function expandHome(value) {
31129
+ const trimmed = value.trim();
31130
+ if (!trimmed.startsWith("~")) return trimmed;
31131
+ return path52.join(os22.homedir(), trimmed.slice(1));
31132
+ }
31133
+ function isExplicitCommandPath(command) {
31134
+ const trimmed = command.trim();
31135
+ return path52.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
31136
+ }
31137
+ function resolveCommandPath(command) {
31138
+ const trimmed = command.trim();
31139
+ if (!trimmed) return null;
31140
+ if (isExplicitCommandPath(trimmed)) {
31141
+ const expanded = expandHome(trimmed);
31142
+ const candidate = path52.isAbsolute(expanded) ? expanded : path52.resolve(expanded);
31143
+ return (0, import_fs4.existsSync)(candidate) ? candidate : null;
31144
+ }
31145
+ return null;
31146
+ }
31098
31147
  function execAsync(cmd, timeoutMs = 5e3) {
31099
- return new Promise((resolve9) => {
31148
+ return new Promise((resolve12) => {
31100
31149
  const child = (0, import_child_process22.exec)(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout) => {
31101
31150
  if (err || !stdout?.trim()) {
31102
- resolve9(null);
31151
+ resolve12(null);
31103
31152
  } else {
31104
- resolve9(stdout.trim());
31153
+ resolve12(stdout.trim());
31105
31154
  }
31106
31155
  });
31107
- child.on("error", () => resolve9(null));
31156
+ child.on("error", () => resolve12(null));
31108
31157
  });
31109
31158
  }
31110
- async function detectCLIs(providerLoader) {
31159
+ async function detectCLIs(providerLoader, options) {
31111
31160
  const platform9 = os22.platform();
31112
31161
  const whichCmd = platform9 === "win32" ? "where" : "which";
31162
+ const includeVersion = options?.includeVersion !== false;
31113
31163
  const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
31114
31164
  const results = await Promise.all(
31115
31165
  cliList.map(async (cli) => {
31116
31166
  try {
31117
- const pathResult = await execAsync(`${whichCmd} ${cli.command}`);
31167
+ const explicitPath = resolveCommandPath(cli.command);
31168
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
31118
31169
  if (!pathResult) return { ...cli, installed: false };
31119
- const firstPath = pathResult.split("\n")[0];
31170
+ const firstPath = explicitPath || pathResult.split("\n")[0];
31120
31171
  let version2;
31121
- try {
31172
+ if (includeVersion) {
31122
31173
  const versionCommands = [
31123
- cli.versionCommand,
31124
- `${cli.command} --version`,
31125
- `${cli.command} -V`,
31126
- `${cli.command} -v`
31174
+ `"${firstPath}" --version`,
31175
+ `"${firstPath}" -V`,
31176
+ `"${firstPath}" -v`,
31177
+ cli.versionCommand
31127
31178
  ].filter((v2) => !!v2);
31128
- for (const versionCommand of versionCommands) {
31129
- const versionResult = await execAsync(versionCommand, 3e3);
31130
- if (versionResult) {
31131
- version2 = parseVersion(versionResult);
31132
- break;
31179
+ try {
31180
+ for (const versionCommand of versionCommands) {
31181
+ const versionResult = await execAsync(versionCommand, 3e3);
31182
+ if (versionResult) {
31183
+ version2 = parseVersion(versionResult);
31184
+ break;
31185
+ }
31133
31186
  }
31187
+ } catch {
31134
31188
  }
31135
- } catch {
31136
31189
  }
31137
31190
  return { ...cli, installed: true, version: version2, path: firstPath };
31138
31191
  } catch {
@@ -31142,7 +31195,7 @@ ${data.message || ""}`.trim();
31142
31195
  );
31143
31196
  return results;
31144
31197
  }
31145
- async function detectCLI(cliId, providerLoader) {
31198
+ async function detectCLI(cliId, providerLoader, options) {
31146
31199
  const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
31147
31200
  if (providerLoader) {
31148
31201
  const cliList = providerLoader.getCliDetectionList();
@@ -31151,25 +31204,28 @@ ${data.message || ""}`.trim();
31151
31204
  const platform9 = os22.platform();
31152
31205
  const whichCmd = platform9 === "win32" ? "where" : "which";
31153
31206
  try {
31154
- const pathResult = await execAsync(`${whichCmd} ${target.command}`);
31207
+ const explicitPath = resolveCommandPath(target.command);
31208
+ const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
31155
31209
  if (!pathResult) return null;
31156
- const firstPath = pathResult.split("\n")[0];
31210
+ const firstPath = explicitPath || pathResult.split("\n")[0];
31157
31211
  let version2;
31158
- try {
31212
+ if (options?.includeVersion !== false) {
31159
31213
  const versionCommands = [
31160
- target.versionCommand,
31161
- `${target.command} --version`,
31162
- `${target.command} -V`,
31163
- `${target.command} -v`
31214
+ `"${firstPath}" --version`,
31215
+ `"${firstPath}" -V`,
31216
+ `"${firstPath}" -v`,
31217
+ target.versionCommand
31164
31218
  ].filter((v2) => !!v2);
31165
- for (const versionCommand of versionCommands) {
31166
- const versionResult = await execAsync(versionCommand, 3e3);
31167
- if (versionResult) {
31168
- version2 = parseVersion(versionResult);
31169
- break;
31219
+ try {
31220
+ for (const versionCommand of versionCommands) {
31221
+ const versionResult = await execAsync(versionCommand, 3e3);
31222
+ if (versionResult) {
31223
+ version2 = parseVersion(versionResult);
31224
+ break;
31225
+ }
31170
31226
  }
31227
+ } catch {
31171
31228
  }
31172
- } catch {
31173
31229
  }
31174
31230
  return { ...target, installed: true, version: version2, path: firstPath };
31175
31231
  } catch {
@@ -31177,7 +31233,7 @@ ${data.message || ""}`.trim();
31177
31233
  }
31178
31234
  }
31179
31235
  }
31180
- const all = await detectCLIs(providerLoader);
31236
+ const all = await detectCLIs(providerLoader, options);
31181
31237
  return all.find((c) => c.id === resolvedId && c.installed) || null;
31182
31238
  }
31183
31239
  var os32 = __toESM2(require("os"));
@@ -31300,7 +31356,7 @@ ${data.message || ""}`.trim();
31300
31356
  * Returns multiple entries if multiple IDE windows are open on same port
31301
31357
  */
31302
31358
  static listAllTargets(port) {
31303
- return new Promise((resolve9) => {
31359
+ return new Promise((resolve12) => {
31304
31360
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
31305
31361
  let data = "";
31306
31362
  res.on("data", (chunk) => data += chunk.toString());
@@ -31316,16 +31372,16 @@ ${data.message || ""}`.trim();
31316
31372
  (t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
31317
31373
  );
31318
31374
  const fallbackPages = pages.filter((t) => !isNonMain(t.title || ""));
31319
- resolve9(mainPages.length > 0 ? mainPages : fallbackPages);
31375
+ resolve12(mainPages.length > 0 ? mainPages : fallbackPages);
31320
31376
  } catch {
31321
- resolve9([]);
31377
+ resolve12([]);
31322
31378
  }
31323
31379
  });
31324
31380
  });
31325
- req.on("error", () => resolve9([]));
31381
+ req.on("error", () => resolve12([]));
31326
31382
  req.setTimeout(2e3, () => {
31327
31383
  req.destroy();
31328
- resolve9([]);
31384
+ resolve12([]);
31329
31385
  });
31330
31386
  });
31331
31387
  }
@@ -31365,7 +31421,7 @@ ${data.message || ""}`.trim();
31365
31421
  }
31366
31422
  }
31367
31423
  findTargetOnPort(port) {
31368
- return new Promise((resolve9) => {
31424
+ return new Promise((resolve12) => {
31369
31425
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
31370
31426
  let data = "";
31371
31427
  res.on("data", (chunk) => data += chunk.toString());
@@ -31376,7 +31432,7 @@ ${data.message || ""}`.trim();
31376
31432
  (t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
31377
31433
  );
31378
31434
  if (pages.length === 0) {
31379
- resolve9(targets.find((t) => t.webSocketDebuggerUrl) || null);
31435
+ resolve12(targets.find((t) => t.webSocketDebuggerUrl) || null);
31380
31436
  return;
31381
31437
  }
31382
31438
  const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
@@ -31386,24 +31442,24 @@ ${data.message || ""}`.trim();
31386
31442
  const specific = list.find((t) => t.id === this._targetId);
31387
31443
  if (specific) {
31388
31444
  this._pageTitle = specific.title || "";
31389
- resolve9(specific);
31445
+ resolve12(specific);
31390
31446
  } else {
31391
31447
  this.log(`[CDP] Target ${this._targetId} not found in page list`);
31392
- resolve9(null);
31448
+ resolve12(null);
31393
31449
  }
31394
31450
  return;
31395
31451
  }
31396
31452
  this._pageTitle = list[0]?.title || "";
31397
- resolve9(list[0]);
31453
+ resolve12(list[0]);
31398
31454
  } catch {
31399
- resolve9(null);
31455
+ resolve12(null);
31400
31456
  }
31401
31457
  });
31402
31458
  });
31403
- req.on("error", () => resolve9(null));
31459
+ req.on("error", () => resolve12(null));
31404
31460
  req.setTimeout(2e3, () => {
31405
31461
  req.destroy();
31406
- resolve9(null);
31462
+ resolve12(null);
31407
31463
  });
31408
31464
  });
31409
31465
  }
@@ -31414,7 +31470,7 @@ ${data.message || ""}`.trim();
31414
31470
  this.extensionProviders = providers;
31415
31471
  }
31416
31472
  connectToTarget(wsUrl) {
31417
- return new Promise((resolve9) => {
31473
+ return new Promise((resolve12) => {
31418
31474
  this.ws = new import_ws2.default(wsUrl);
31419
31475
  this.ws.on("open", async () => {
31420
31476
  this._connected = true;
@@ -31424,17 +31480,17 @@ ${data.message || ""}`.trim();
31424
31480
  }
31425
31481
  this.connectBrowserWs().catch(() => {
31426
31482
  });
31427
- resolve9(true);
31483
+ resolve12(true);
31428
31484
  });
31429
31485
  this.ws.on("message", (data) => {
31430
31486
  try {
31431
31487
  const msg = JSON.parse(data.toString());
31432
31488
  if (msg.id && this.pending.has(msg.id)) {
31433
- const { resolve: resolve10, reject } = this.pending.get(msg.id);
31489
+ const { resolve: resolve13, reject } = this.pending.get(msg.id);
31434
31490
  this.pending.delete(msg.id);
31435
31491
  this.failureCount = 0;
31436
31492
  if (msg.error) reject(new Error(msg.error.message));
31437
- else resolve10(msg.result);
31493
+ else resolve13(msg.result);
31438
31494
  } else if (msg.method === "Runtime.executionContextCreated") {
31439
31495
  this.contexts.add(msg.params.context.id);
31440
31496
  } else if (msg.method === "Runtime.executionContextDestroyed") {
@@ -31457,7 +31513,7 @@ ${data.message || ""}`.trim();
31457
31513
  this.ws.on("error", (err) => {
31458
31514
  this.log(`[CDP] WebSocket error: ${err.message}`);
31459
31515
  this._connected = false;
31460
- resolve9(false);
31516
+ resolve12(false);
31461
31517
  });
31462
31518
  });
31463
31519
  }
@@ -31471,7 +31527,7 @@ ${data.message || ""}`.trim();
31471
31527
  return;
31472
31528
  }
31473
31529
  this.log(`[CDP] Connecting browser WS for target discovery...`);
31474
- await new Promise((resolve9, reject) => {
31530
+ await new Promise((resolve12, reject) => {
31475
31531
  this.browserWs = new import_ws2.default(browserWsUrl);
31476
31532
  this.browserWs.on("open", async () => {
31477
31533
  this._browserConnected = true;
@@ -31481,16 +31537,16 @@ ${data.message || ""}`.trim();
31481
31537
  } catch (e) {
31482
31538
  this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
31483
31539
  }
31484
- resolve9();
31540
+ resolve12();
31485
31541
  });
31486
31542
  this.browserWs.on("message", (data) => {
31487
31543
  try {
31488
31544
  const msg = JSON.parse(data.toString());
31489
31545
  if (msg.id && this.browserPending.has(msg.id)) {
31490
- const { resolve: resolve10, reject: reject2 } = this.browserPending.get(msg.id);
31546
+ const { resolve: resolve13, reject: reject2 } = this.browserPending.get(msg.id);
31491
31547
  this.browserPending.delete(msg.id);
31492
31548
  if (msg.error) reject2(new Error(msg.error.message));
31493
- else resolve10(msg.result);
31549
+ else resolve13(msg.result);
31494
31550
  }
31495
31551
  } catch {
31496
31552
  }
@@ -31510,31 +31566,31 @@ ${data.message || ""}`.trim();
31510
31566
  }
31511
31567
  }
31512
31568
  getBrowserWsUrl() {
31513
- return new Promise((resolve9) => {
31569
+ return new Promise((resolve12) => {
31514
31570
  const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
31515
31571
  let data = "";
31516
31572
  res.on("data", (chunk) => data += chunk.toString());
31517
31573
  res.on("end", () => {
31518
31574
  try {
31519
31575
  const info = JSON.parse(data);
31520
- resolve9(info.webSocketDebuggerUrl || null);
31576
+ resolve12(info.webSocketDebuggerUrl || null);
31521
31577
  } catch {
31522
- resolve9(null);
31578
+ resolve12(null);
31523
31579
  }
31524
31580
  });
31525
31581
  });
31526
- req.on("error", () => resolve9(null));
31582
+ req.on("error", () => resolve12(null));
31527
31583
  req.setTimeout(3e3, () => {
31528
31584
  req.destroy();
31529
- resolve9(null);
31585
+ resolve12(null);
31530
31586
  });
31531
31587
  });
31532
31588
  }
31533
31589
  sendBrowser(method, params = {}, timeoutMs = 15e3) {
31534
- return new Promise((resolve9, reject) => {
31590
+ return new Promise((resolve12, reject) => {
31535
31591
  if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
31536
31592
  const id = this.browserMsgId++;
31537
- this.browserPending.set(id, { resolve: resolve9, reject });
31593
+ this.browserPending.set(id, { resolve: resolve12, reject });
31538
31594
  this.browserWs.send(JSON.stringify({ id, method, params }));
31539
31595
  setTimeout(() => {
31540
31596
  if (this.browserPending.has(id)) {
@@ -31574,11 +31630,11 @@ ${data.message || ""}`.trim();
31574
31630
  }
31575
31631
  // ─── CDP Protocol ────────────────────────────────────────
31576
31632
  sendInternal(method, params = {}, timeoutMs = 15e3) {
31577
- return new Promise((resolve9, reject) => {
31633
+ return new Promise((resolve12, reject) => {
31578
31634
  if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
31579
31635
  if (this.ws.readyState !== import_ws2.default.OPEN) return reject(new Error("WebSocket not open"));
31580
31636
  const id = this.msgId++;
31581
- this.pending.set(id, { resolve: resolve9, reject });
31637
+ this.pending.set(id, { resolve: resolve12, reject });
31582
31638
  this.ws.send(JSON.stringify({ id, method, params }));
31583
31639
  setTimeout(() => {
31584
31640
  if (this.pending.has(id)) {
@@ -31827,7 +31883,7 @@ ${data.message || ""}`.trim();
31827
31883
  const browserWs = this.browserWs;
31828
31884
  let msgId = this.browserMsgId;
31829
31885
  const sendWs = (method, params = {}, sessionId) => {
31830
- return new Promise((resolve9, reject) => {
31886
+ return new Promise((resolve12, reject) => {
31831
31887
  const mid = msgId++;
31832
31888
  this.browserMsgId = msgId;
31833
31889
  const handler = (raw) => {
@@ -31836,7 +31892,7 @@ ${data.message || ""}`.trim();
31836
31892
  if (msg.id === mid) {
31837
31893
  browserWs.removeListener("message", handler);
31838
31894
  if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
31839
- else resolve9(msg.result);
31895
+ else resolve12(msg.result);
31840
31896
  }
31841
31897
  } catch {
31842
31898
  }
@@ -32027,14 +32083,14 @@ ${data.message || ""}`.trim();
32027
32083
  if (!ws2 || ws2.readyState !== import_ws2.default.OPEN) {
32028
32084
  throw new Error("CDP not connected");
32029
32085
  }
32030
- return new Promise((resolve9, reject) => {
32086
+ return new Promise((resolve12, reject) => {
32031
32087
  const id = getNextId();
32032
32088
  pendingMap.set(id, {
32033
32089
  resolve: (result) => {
32034
32090
  if (result?.result?.subtype === "error") {
32035
32091
  reject(new Error(result.result.description));
32036
32092
  } else {
32037
- resolve9(result?.result?.value);
32093
+ resolve12(result?.result?.value);
32038
32094
  }
32039
32095
  },
32040
32096
  reject
@@ -32066,10 +32122,10 @@ ${data.message || ""}`.trim();
32066
32122
  throw new Error("CDP not connected");
32067
32123
  }
32068
32124
  const sendViaSession = (method, params = {}) => {
32069
- return new Promise((resolve9, reject) => {
32125
+ return new Promise((resolve12, reject) => {
32070
32126
  const pendingMap = this._browserConnected ? this.browserPending : this.pending;
32071
32127
  const id = this._browserConnected ? this.browserMsgId++ : this.msgId++;
32072
- pendingMap.set(id, { resolve: resolve9, reject });
32128
+ pendingMap.set(id, { resolve: resolve12, reject });
32073
32129
  ws2.send(JSON.stringify({ id, sessionId, method, params }));
32074
32130
  setTimeout(() => {
32075
32131
  if (pendingMap.has(id)) {
@@ -32687,15 +32743,76 @@ ${data.message || ""}`.trim();
32687
32743
  return String(value);
32688
32744
  }
32689
32745
  var fs32 = __toESM2(require("fs"));
32690
- var path52 = __toESM2(require("path"));
32746
+ var path7 = __toESM2(require("path"));
32691
32747
  var os52 = __toESM2(require("os"));
32692
- var HISTORY_DIR = path52.join(os52.homedir(), ".adhdev", "history");
32748
+ var HISTORY_DIR = path7.join(os52.homedir(), ".adhdev", "history");
32693
32749
  var RETAIN_DAYS = 30;
32750
+ 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;
32751
+ function normalizeHistoryComparable(text) {
32752
+ return String(text || "").replace(/\s+/g, " ").trim();
32753
+ }
32754
+ function cleanupHistoryContent(agentType, role, content) {
32755
+ let value = String(content || "").replace(/\r\n/g, "\n").trim();
32756
+ if (!value) return "";
32757
+ if (agentType === "codex-cli" && role === "assistant") {
32758
+ const filtered = value.split("\n").filter((line) => !CODEX_STARTER_PROMPT_RE.test(line.trim())).join("\n").replace(/\n{3,}/g, "\n\n").trim();
32759
+ value = filtered;
32760
+ }
32761
+ return value;
32762
+ }
32763
+ function buildHistoryMessageHash(agentType, message) {
32764
+ if (message.historyDedupKey) return message.historyDedupKey;
32765
+ const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
32766
+ return `${message.kind || "standard"}:${message.role}:${message.receivedAt || 0}:${normalizeHistoryComparable(cleaned)}`;
32767
+ }
32768
+ function buildHistoryMessageSignature(agentType, message) {
32769
+ const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
32770
+ return `${message.kind || "standard"}:${message.role}:${normalizeHistoryComparable(cleaned)}`;
32771
+ }
32772
+ function isAdjacentHistoryDuplicate(agentType, previous, next) {
32773
+ if (!previous || !next) return false;
32774
+ return buildHistoryMessageSignature(agentType, previous) === buildHistoryMessageSignature(agentType, next);
32775
+ }
32776
+ function collapseReplayAssistantTurns(agentType, messages) {
32777
+ if (agentType !== "codex-cli") return messages;
32778
+ const collapsed = [];
32779
+ let sawAssistantSinceLastUser = false;
32780
+ for (const message of messages) {
32781
+ if (message.role === "user") {
32782
+ sawAssistantSinceLastUser = false;
32783
+ collapsed.push(message);
32784
+ continue;
32785
+ }
32786
+ if (message.role === "assistant") {
32787
+ if (sawAssistantSinceLastUser) continue;
32788
+ sawAssistantSinceLastUser = true;
32789
+ collapsed.push(message);
32790
+ continue;
32791
+ }
32792
+ collapsed.push(message);
32793
+ }
32794
+ return collapsed;
32795
+ }
32796
+ function sanitizeHistoryMessage(agentType, message) {
32797
+ if (!message || message.role !== "user" && message.role !== "assistant" && message.role !== "system") {
32798
+ return null;
32799
+ }
32800
+ const content = cleanupHistoryContent(agentType, message.role, message.content);
32801
+ if (!content) return null;
32802
+ return {
32803
+ ...message,
32804
+ content
32805
+ };
32806
+ }
32694
32807
  var ChatHistoryWriter = class {
32695
32808
  /** Last seen message count per agent (deduplication) */
32696
32809
  lastSeenCounts = /* @__PURE__ */ new Map();
32697
32810
  /** Last seen message hash per agent (deduplication) */
32698
32811
  lastSeenHashes = /* @__PURE__ */ new Map();
32812
+ /** Last appended normalized message signature per agent/session */
32813
+ lastSeenSignatures = /* @__PURE__ */ new Map();
32814
+ /** Last appended normalized non-system turn signature per agent/session */
32815
+ lastSeenTurnSignatures = /* @__PURE__ */ new Map();
32699
32816
  rotated = false;
32700
32817
  /**
32701
32818
  * Append new messages to history
@@ -32717,14 +32834,36 @@ ${data.message || ""}`.trim();
32717
32834
  }
32718
32835
  const newMessages = [];
32719
32836
  for (const msg of messages) {
32720
- const hash2 = msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`;
32837
+ const role = msg.role;
32838
+ if (role !== "user" && role !== "assistant" && role !== "system") continue;
32839
+ const content = cleanupHistoryContent(agentType, role, msg.content || "");
32840
+ if (!content) continue;
32841
+ const receivedAt = msg.receivedAt || Date.now();
32842
+ const hash2 = buildHistoryMessageHash(agentType, {
32843
+ role,
32844
+ content,
32845
+ receivedAt,
32846
+ kind: typeof msg.kind === "string" ? msg.kind : void 0,
32847
+ historyDedupKey: msg.historyDedupKey
32848
+ });
32849
+ const signature = buildHistoryMessageSignature(agentType, {
32850
+ role,
32851
+ content,
32852
+ kind: typeof msg.kind === "string" ? msg.kind : void 0
32853
+ });
32721
32854
  if (seenHashes.has(hash2)) continue;
32855
+ if (this.lastSeenSignatures.get(dedupKey) === signature) continue;
32856
+ if (role !== "system" && this.lastSeenTurnSignatures.get(dedupKey) === signature) continue;
32722
32857
  seenHashes.add(hash2);
32858
+ this.lastSeenSignatures.set(dedupKey, signature);
32859
+ if (role !== "system") {
32860
+ this.lastSeenTurnSignatures.set(dedupKey, signature);
32861
+ }
32723
32862
  newMessages.push({
32724
- ts: new Date(msg.receivedAt || Date.now()).toISOString(),
32725
- receivedAt: msg.receivedAt || Date.now(),
32726
- role: msg.role,
32727
- content: msg.content || "",
32863
+ ts: new Date(receivedAt).toISOString(),
32864
+ receivedAt,
32865
+ role,
32866
+ content,
32728
32867
  kind: typeof msg.kind === "string" ? msg.kind : void 0,
32729
32868
  senderName: typeof msg.senderName === "string" ? msg.senderName : void 0,
32730
32869
  agent: agentType,
@@ -32734,16 +32873,18 @@ ${data.message || ""}`.trim();
32734
32873
  });
32735
32874
  }
32736
32875
  if (newMessages.length === 0) return;
32737
- const dir = path52.join(HISTORY_DIR, this.sanitize(agentType));
32876
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
32738
32877
  fs32.mkdirSync(dir, { recursive: true });
32739
32878
  const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
32740
32879
  const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
32741
- const filePath = path52.join(dir, `${filePrefix}${date5}.jsonl`);
32880
+ const filePath = path7.join(dir, `${filePrefix}${date5}.jsonl`);
32742
32881
  const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
32743
32882
  fs32.appendFileSync(filePath, lines, "utf-8");
32744
32883
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
32745
32884
  if (messages.length < prevCount * 0.5 && prevCount > 3) {
32746
32885
  seenHashes.clear();
32886
+ this.lastSeenSignatures.delete(dedupKey);
32887
+ this.lastSeenTurnSignatures.delete(dedupKey);
32747
32888
  for (const msg of messages) {
32748
32889
  seenHashes.add(msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`);
32749
32890
  }
@@ -32757,6 +32898,54 @@ ${data.message || ""}`.trim();
32757
32898
  } catch {
32758
32899
  }
32759
32900
  }
32901
+ seedSessionHistory(agentType, messages = [], historySessionId, instanceId) {
32902
+ const effectiveHistoryKey = historySessionId || instanceId;
32903
+ const dedupKey = effectiveHistoryKey ? `${agentType}:${effectiveHistoryKey}` : agentType;
32904
+ const seenHashes = /* @__PURE__ */ new Set();
32905
+ for (const raw of messages) {
32906
+ const role = raw?.role;
32907
+ if (role !== "user" && role !== "assistant" && role !== "system") continue;
32908
+ const content = cleanupHistoryContent(agentType, role, raw?.content || "");
32909
+ if (!content) continue;
32910
+ seenHashes.add(buildHistoryMessageHash(agentType, {
32911
+ role,
32912
+ content,
32913
+ receivedAt: raw?.receivedAt || 0,
32914
+ kind: typeof raw?.kind === "string" ? raw.kind : void 0,
32915
+ historyDedupKey: raw?.historyDedupKey
32916
+ }));
32917
+ }
32918
+ this.lastSeenHashes.set(dedupKey, seenHashes);
32919
+ this.lastSeenCounts.set(dedupKey, messages.length);
32920
+ const lastMessage = [...messages].reverse().find((raw) => {
32921
+ const role = raw?.role;
32922
+ if (role !== "user" && role !== "assistant" && role !== "system") return false;
32923
+ return !!cleanupHistoryContent(agentType, role, raw?.content || "");
32924
+ });
32925
+ const lastTurnMessage = [...messages].reverse().find((raw) => {
32926
+ const role = raw?.role;
32927
+ if (role !== "user" && role !== "assistant") return false;
32928
+ return !!cleanupHistoryContent(agentType, role, raw?.content || "");
32929
+ });
32930
+ if (lastMessage) {
32931
+ this.lastSeenSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
32932
+ role: lastMessage.role,
32933
+ content: lastMessage.content,
32934
+ kind: typeof lastMessage.kind === "string" ? lastMessage.kind : void 0
32935
+ }));
32936
+ } else {
32937
+ this.lastSeenSignatures.delete(dedupKey);
32938
+ }
32939
+ if (lastTurnMessage) {
32940
+ this.lastSeenTurnSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
32941
+ role: lastTurnMessage.role,
32942
+ content: lastTurnMessage.content,
32943
+ kind: typeof lastTurnMessage.kind === "string" ? lastTurnMessage.kind : void 0
32944
+ }));
32945
+ } else {
32946
+ this.lastSeenTurnSignatures.delete(dedupKey);
32947
+ }
32948
+ }
32760
32949
  appendSystemMarker(agentType, content, options = {}) {
32761
32950
  this.appendNewMessages(
32762
32951
  agentType,
@@ -32787,19 +32976,29 @@ ${data.message || ""}`.trim();
32787
32976
  this.lastSeenHashes.set(toDedupKey, nextHashes);
32788
32977
  this.lastSeenHashes.delete(fromDedupKey);
32789
32978
  }
32979
+ const fromSignature = this.lastSeenSignatures.get(fromDedupKey);
32980
+ if (fromSignature) {
32981
+ this.lastSeenSignatures.set(toDedupKey, fromSignature);
32982
+ this.lastSeenSignatures.delete(fromDedupKey);
32983
+ }
32984
+ const fromTurnSignature = this.lastSeenTurnSignatures.get(fromDedupKey);
32985
+ if (fromTurnSignature) {
32986
+ this.lastSeenTurnSignatures.set(toDedupKey, fromTurnSignature);
32987
+ this.lastSeenTurnSignatures.delete(fromDedupKey);
32988
+ }
32790
32989
  const fromCount = this.lastSeenCounts.get(fromDedupKey);
32791
32990
  if (typeof fromCount === "number") {
32792
32991
  this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
32793
32992
  this.lastSeenCounts.delete(fromDedupKey);
32794
32993
  }
32795
- const dir = path52.join(HISTORY_DIR, this.sanitize(agentType));
32994
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
32796
32995
  if (!fs32.existsSync(dir)) return;
32797
32996
  const fromPrefix = `${this.sanitize(fromId)}_`;
32798
32997
  const toPrefix = `${this.sanitize(toId)}_`;
32799
32998
  const files = fs32.readdirSync(dir).filter((file2) => file2.startsWith(fromPrefix) && file2.endsWith(".jsonl"));
32800
32999
  for (const file2 of files) {
32801
- const sourcePath = path52.join(dir, file2);
32802
- const targetPath = path52.join(dir, `${toPrefix}${file2.slice(fromPrefix.length)}`);
33000
+ const sourcePath = path7.join(dir, file2);
33001
+ const targetPath = path7.join(dir, `${toPrefix}${file2.slice(fromPrefix.length)}`);
32803
33002
  const sourceLines = fs32.readFileSync(sourcePath, "utf-8").split("\n").filter(Boolean);
32804
33003
  const rewritten = sourceLines.map((line) => {
32805
33004
  try {
@@ -32828,10 +33027,61 @@ ${data.message || ""}`.trim();
32828
33027
  } catch {
32829
33028
  }
32830
33029
  }
33030
+ compactHistorySession(agentType, historySessionId) {
33031
+ const sessionId = String(historySessionId || "").trim();
33032
+ if (!sessionId) return;
33033
+ try {
33034
+ const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
33035
+ if (!fs32.existsSync(dir)) return;
33036
+ const prefix = `${this.sanitize(sessionId)}_`;
33037
+ const files = fs32.readdirSync(dir).filter((file2) => file2.startsWith(prefix) && file2.endsWith(".jsonl")).sort();
33038
+ const seen = /* @__PURE__ */ new Set();
33039
+ for (const file2 of files) {
33040
+ const filePath = path7.join(dir, file2);
33041
+ const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
33042
+ const next = [];
33043
+ for (const line of lines) {
33044
+ let parsed = null;
33045
+ try {
33046
+ parsed = JSON.parse(line);
33047
+ } catch {
33048
+ parsed = null;
33049
+ }
33050
+ if (!parsed || parsed.historySessionId !== sessionId) continue;
33051
+ const sanitized = sanitizeHistoryMessage(agentType, parsed);
33052
+ if (!sanitized) continue;
33053
+ const hash2 = buildHistoryMessageHash(agentType, sanitized);
33054
+ if (seen.has(hash2)) continue;
33055
+ seen.add(hash2);
33056
+ next.push(sanitized);
33057
+ }
33058
+ next.sort((a, b2) => a.receivedAt - b2.receivedAt);
33059
+ const dedupedAdjacent = [];
33060
+ let lastTurn = null;
33061
+ for (const entry of next) {
33062
+ const previous = dedupedAdjacent[dedupedAdjacent.length - 1];
33063
+ if (isAdjacentHistoryDuplicate(agentType, previous, entry)) continue;
33064
+ if (entry.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, entry)) continue;
33065
+ dedupedAdjacent.push(entry);
33066
+ if (entry.role !== "system") lastTurn = entry;
33067
+ }
33068
+ const collapsed = collapseReplayAssistantTurns(agentType, dedupedAdjacent);
33069
+ if (collapsed.length === 0) {
33070
+ fs32.unlinkSync(filePath);
33071
+ continue;
33072
+ }
33073
+ fs32.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
33074
+ `, "utf-8");
33075
+ }
33076
+ } catch {
33077
+ }
33078
+ }
32831
33079
  /** Called when agent session is explicitly changed */
32832
33080
  onSessionChange(agentType) {
32833
33081
  this.lastSeenHashes.delete(agentType);
32834
33082
  this.lastSeenCounts.delete(agentType);
33083
+ this.lastSeenSignatures.delete(agentType);
33084
+ this.lastSeenTurnSignatures.delete(agentType);
32835
33085
  }
32836
33086
  /** Delete history files older than 30 days */
32837
33087
  async rotateOldFiles() {
@@ -32840,10 +33090,10 @@ ${data.message || ""}`.trim();
32840
33090
  const cutoff = Date.now() - RETAIN_DAYS * 24 * 60 * 60 * 1e3;
32841
33091
  const agentDirs = fs32.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
32842
33092
  for (const dir of agentDirs) {
32843
- const dirPath = path52.join(HISTORY_DIR, dir.name);
33093
+ const dirPath = path7.join(HISTORY_DIR, dir.name);
32844
33094
  const files = fs32.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
32845
33095
  for (const file2 of files) {
32846
- const filePath = path52.join(dirPath, file2);
33096
+ const filePath = path7.join(dirPath, file2);
32847
33097
  const stat4 = fs32.statSync(filePath);
32848
33098
  if (stat4.mtimeMs < cutoff) {
32849
33099
  fs32.unlinkSync(filePath);
@@ -32861,7 +33111,7 @@ ${data.message || ""}`.trim();
32861
33111
  function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
32862
33112
  try {
32863
33113
  const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
32864
- const dir = path52.join(HISTORY_DIR, sanitized);
33114
+ const dir = path7.join(HISTORY_DIR, sanitized);
32865
33115
  if (!fs32.existsSync(dir)) return { messages: [], hasMore: false };
32866
33116
  const sanitizedInstance = historySessionId?.replace(/[^a-zA-Z0-9_-]/g, "_");
32867
33117
  const files = fs32.readdirSync(dir).filter((f) => {
@@ -32872,23 +33122,37 @@ ${data.message || ""}`.trim();
32872
33122
  return true;
32873
33123
  }).sort().reverse();
32874
33124
  const allMessages = [];
32875
- const needed = offset + limit + 1;
33125
+ const seen = /* @__PURE__ */ new Set();
32876
33126
  for (const file2 of files) {
32877
- if (allMessages.length >= needed) break;
32878
- const filePath = path52.join(dir, file2);
33127
+ const filePath = path7.join(dir, file2);
32879
33128
  const content = fs32.readFileSync(filePath, "utf-8");
32880
33129
  const lines = content.trim().split("\n").filter(Boolean);
32881
- for (let i = lines.length - 1; i >= 0; i--) {
32882
- if (allMessages.length >= needed) break;
33130
+ for (let i = 0; i < lines.length; i++) {
32883
33131
  try {
32884
- allMessages.push(JSON.parse(lines[i]));
33132
+ const parsed = JSON.parse(lines[i]);
33133
+ const sanitizedMessage = sanitizeHistoryMessage(agentType, parsed);
33134
+ if (!sanitizedMessage) continue;
33135
+ const hash2 = buildHistoryMessageHash(agentType, sanitizedMessage);
33136
+ if (seen.has(hash2)) continue;
33137
+ seen.add(hash2);
33138
+ allMessages.push(sanitizedMessage);
32885
33139
  } catch {
32886
33140
  }
32887
33141
  }
32888
33142
  }
32889
- const sliced = allMessages.slice(offset, offset + limit);
32890
- const hasMore = allMessages.length > offset + limit;
32891
- sliced.reverse();
33143
+ allMessages.sort((a, b2) => a.receivedAt - b2.receivedAt);
33144
+ const chronological = [];
33145
+ let lastTurn = null;
33146
+ for (const message of allMessages) {
33147
+ const previous = chronological[chronological.length - 1];
33148
+ if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
33149
+ if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
33150
+ chronological.push(message);
33151
+ if (message.role !== "system") lastTurn = message;
33152
+ }
33153
+ const collapsed = collapseReplayAssistantTurns(agentType, chronological);
33154
+ const sliced = collapsed.slice(offset, offset + limit);
33155
+ const hasMore = collapsed.length > offset + limit;
32892
33156
  return { messages: sliced, hasMore };
32893
33157
  } catch {
32894
33158
  return { messages: [], hasMore: false };
@@ -32897,7 +33161,7 @@ ${data.message || ""}`.trim();
32897
33161
  function listSavedHistorySessions(agentType, options = {}) {
32898
33162
  try {
32899
33163
  const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
32900
- const dir = path52.join(HISTORY_DIR, sanitized);
33164
+ const dir = path7.join(HISTORY_DIR, sanitized);
32901
33165
  if (!fs32.existsSync(dir)) return { sessions: [], hasMore: false };
32902
33166
  const groupedFiles = /* @__PURE__ */ new Map();
32903
33167
  const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
@@ -32918,7 +33182,7 @@ ${data.message || ""}`.trim();
32918
33182
  let sessionTitle = "";
32919
33183
  let preview = "";
32920
33184
  for (const file2 of files.sort()) {
32921
- const filePath = path52.join(dir, file2);
33185
+ const filePath = path7.join(dir, file2);
32922
33186
  const content = fs32.readFileSync(filePath, "utf-8");
32923
33187
  const lines = content.split("\n").filter(Boolean);
32924
33188
  for (const line of lines) {
@@ -34722,6 +34986,46 @@ ${effect.notification.body || ""}`.trim();
34722
34986
  if (!parsed || typeof parsed !== "object") return false;
34723
34987
  return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
34724
34988
  }
34989
+ async function readExtensionChatState(h) {
34990
+ try {
34991
+ const evalResult = await h.evaluateProviderScript("readChat", void 0, 5e4);
34992
+ if (!evalResult?.result) return null;
34993
+ const parsed = parseMaybeJson(evalResult.result);
34994
+ return parsed && typeof parsed === "object" ? parsed : null;
34995
+ } catch {
34996
+ return null;
34997
+ }
34998
+ }
34999
+ function getStateMessageCount(state) {
35000
+ return Array.isArray(state?.messages) ? state.messages.length : 0;
35001
+ }
35002
+ function getStateLastSignature(state) {
35003
+ const messages = Array.isArray(state?.messages) ? state.messages : [];
35004
+ const last = messages[messages.length - 1];
35005
+ if (!last) return "";
35006
+ return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
35007
+ }
35008
+ async function getStableExtensionBaseline(h) {
35009
+ const first = await readExtensionChatState(h);
35010
+ if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
35011
+ await new Promise((resolve12) => setTimeout(resolve12, 150));
35012
+ const second = await readExtensionChatState(h);
35013
+ return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
35014
+ }
35015
+ async function verifyExtensionSendObserved(h, before) {
35016
+ const beforeCount = getStateMessageCount(before);
35017
+ const beforeSignature = getStateLastSignature(before);
35018
+ for (let attempt = 0; attempt < 12; attempt += 1) {
35019
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
35020
+ const state = await readExtensionChatState(h);
35021
+ if (state?.status === "waiting_approval") return true;
35022
+ const afterCount = getStateMessageCount(state);
35023
+ const afterSignature = getStateLastSignature(state);
35024
+ if (afterCount > beforeCount) return true;
35025
+ if (afterSignature && afterSignature !== beforeSignature) return true;
35026
+ }
35027
+ return false;
35028
+ }
34725
35029
  async function handleChatHistory(h, args) {
34726
35030
  const { agentType, offset, limit } = args;
34727
35031
  const historySessionId = getHistorySessionId(h, args);
@@ -34902,12 +35206,17 @@ ${effect.notification.body || ""}`.trim();
34902
35206
  if (isExtensionTransport(transport)) {
34903
35207
  _log(`Extension: ${provider?.type || "unknown_extension"}`);
34904
35208
  try {
35209
+ const beforeState = await getStableExtensionBaseline(h);
34905
35210
  const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
34906
35211
  if (evalResult?.result) {
34907
35212
  const parsed = parseMaybeJson(evalResult.result);
34908
35213
  if (didProviderConfirmSend(parsed)) {
34909
- _log(`Extension script sent OK`);
34910
- return _logSendSuccess("extension-script");
35214
+ const observed = await verifyExtensionSendObserved(h, beforeState);
35215
+ if (observed) {
35216
+ _log(`Extension script sent OK`);
35217
+ return _logSendSuccess("extension-script");
35218
+ }
35219
+ _log(`Extension script reported send but no chat-state change was observed`);
34911
35220
  }
34912
35221
  if (parsed?.needsTypeAndSend) {
34913
35222
  _log(`Extension needsTypeAndSend \u2192 AgentStreamManager`);
@@ -35073,6 +35382,14 @@ ${effect.notification.body || ""}`.trim();
35073
35382
  } catch {
35074
35383
  }
35075
35384
  }
35385
+ if (parsed?.sessions && Array.isArray(parsed.sessions)) {
35386
+ LOG2.info("Command", `[list_chats] OK: ${parsed.sessions.length} chats`);
35387
+ return { success: true, chats: parsed.sessions };
35388
+ }
35389
+ if (parsed?.chats && Array.isArray(parsed.chats)) {
35390
+ LOG2.info("Command", `[list_chats] OK: ${parsed.chats.length} chats`);
35391
+ return { success: true, chats: parsed.chats };
35392
+ }
35076
35393
  if (Array.isArray(parsed)) {
35077
35394
  LOG2.info("Command", `[list_chats] OK: ${parsed.length} chats`);
35078
35395
  return { success: true, chats: parsed };
@@ -35142,7 +35459,13 @@ ${effect.notification.body || ""}`.trim();
35142
35459
  } catch (e) {
35143
35460
  return { success: false, error: `webviewSwitchSession failed: ${e.message}` };
35144
35461
  }
35145
- const script = h.getProviderScript("switchSession", { SESSION_ID: JSON.stringify(sessionId) }) || h.getProviderScript("switch_session", { SESSION_ID: JSON.stringify(sessionId) });
35462
+ const switchParams = {
35463
+ sessionId,
35464
+ title: sessionId,
35465
+ id: sessionId,
35466
+ SESSION_ID: JSON.stringify(sessionId)
35467
+ };
35468
+ const script = h.getProviderScript("switchSession", switchParams) || h.getProviderScript("switch_session", switchParams);
35146
35469
  if (!script) return { success: false, error: "switch_session script not available" };
35147
35470
  try {
35148
35471
  const raw = await cdp.evaluate(script, 15e3);
@@ -35221,8 +35544,8 @@ ${effect.notification.body || ""}`.trim();
35221
35544
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
35222
35545
  if (adapter) {
35223
35546
  const acpInstance = adapter._acpInstance;
35224
- if (acpInstance && typeof acpInstance.onEvent === "function") {
35225
- acpInstance.onEvent("set_mode", { mode });
35547
+ if (acpInstance && typeof acpInstance.setMode === "function") {
35548
+ await acpInstance.setMode(mode);
35226
35549
  return { success: true, mode };
35227
35550
  }
35228
35551
  }
@@ -35279,9 +35602,9 @@ ${effect.notification.body || ""}`.trim();
35279
35602
  LOG2.info("Command", `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
35280
35603
  if (adapter) {
35281
35604
  const acpInstance = adapter._acpInstance;
35282
- if (acpInstance && typeof acpInstance.onEvent === "function") {
35283
- acpInstance.onEvent("change_model", { model });
35284
- LOG2.info("Command", `[change_model] Dispatched change_model event to ACP instance`);
35605
+ if (acpInstance && typeof acpInstance.setConfigOption === "function") {
35606
+ await acpInstance.setConfigOption("model", model);
35607
+ LOG2.info("Command", `[change_model] Updated ACP model to ${model}`);
35285
35608
  return { success: true, model };
35286
35609
  }
35287
35610
  }
@@ -35398,9 +35721,21 @@ ${effect.notification.body || ""}`.trim();
35398
35721
  return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
35399
35722
  }
35400
35723
  if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
35401
- const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
35724
+ const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action, button);
35402
35725
  return { success: ok };
35403
35726
  }
35727
+ if (transport === "acp") {
35728
+ const adapter = getTargetedCliAdapter(h, args, provider?.type);
35729
+ const acpInstance = adapter?._acpInstance;
35730
+ if (!acpInstance) return { success: false, error: "ACP instance not found" };
35731
+ try {
35732
+ await acpInstance.resolvePermission(action === "approve" || action === "accept" || action === "always");
35733
+ LOG2.info("Command", `[resolveAction] ACP \u2192 ${action}`);
35734
+ return { success: true, action };
35735
+ } catch (e) {
35736
+ return { success: false, error: e?.message || "ACP resolve action failed" };
35737
+ }
35738
+ }
35404
35739
  if (provider?.scripts?.webviewResolveAction || provider?.scripts?.webview_resolve_action) {
35405
35740
  const script = h.getProviderScript("webviewResolveAction", { action, button, buttonText: button }) || h.getProviderScript("webview_resolve_action", { action, button, buttonText: button });
35406
35741
  if (script) {
@@ -35477,7 +35812,7 @@ ${effect.notification.body || ""}`.trim();
35477
35812
  return { success: false, error: "resolveAction script not available for this provider" };
35478
35813
  }
35479
35814
  var fs42 = __toESM2(require("fs"));
35480
- var path6 = __toESM2(require("path"));
35815
+ var path8 = __toESM2(require("path"));
35481
35816
  var os62 = __toESM2(require("os"));
35482
35817
  var KEY_TO_VK = {
35483
35818
  Backspace: 8,
@@ -35729,25 +36064,25 @@ ${effect.notification.body || ""}`.trim();
35729
36064
  const inputPath = rawPath || ".";
35730
36065
  const home = os62.homedir();
35731
36066
  if (inputPath.startsWith("~")) {
35732
- return path6.resolve(path6.join(home, inputPath.slice(1)));
36067
+ return path8.resolve(path8.join(home, inputPath.slice(1)));
35733
36068
  }
35734
36069
  if (process.platform === "win32") {
35735
36070
  const normalized = normalizeWindowsRequestedPath(inputPath);
35736
- if (path6.win32.isAbsolute(normalized)) {
35737
- return path6.win32.normalize(normalized);
36071
+ if (path8.win32.isAbsolute(normalized)) {
36072
+ return path8.win32.normalize(normalized);
35738
36073
  }
35739
- return path6.win32.resolve(normalized);
36074
+ return path8.win32.resolve(normalized);
35740
36075
  }
35741
- if (path6.isAbsolute(inputPath)) {
35742
- return path6.normalize(inputPath);
36076
+ if (path8.isAbsolute(inputPath)) {
36077
+ return path8.normalize(inputPath);
35743
36078
  }
35744
- return path6.resolve(inputPath);
36079
+ return path8.resolve(inputPath);
35745
36080
  }
35746
36081
  function listDirectoryEntriesSafe(dirPath) {
35747
36082
  const entries = fs42.readdirSync(dirPath, { withFileTypes: true });
35748
36083
  const files = [];
35749
36084
  for (const entry of entries) {
35750
- const entryPath = path6.join(dirPath, entry.name);
36085
+ const entryPath = path8.join(dirPath, entry.name);
35751
36086
  try {
35752
36087
  if (entry.isDirectory()) {
35753
36088
  files.push({ name: entry.name, type: "directory" });
@@ -35786,7 +36121,7 @@ ${effect.notification.body || ""}`.trim();
35786
36121
  async function handleFileWrite(h, args) {
35787
36122
  try {
35788
36123
  const filePath = resolveSafePath(args?.path);
35789
- fs42.mkdirSync(path6.dirname(filePath), { recursive: true });
36124
+ fs42.mkdirSync(path8.dirname(filePath), { recursive: true });
35790
36125
  fs42.writeFileSync(filePath, args?.content || "", "utf-8");
35791
36126
  return { success: true, path: filePath };
35792
36127
  } catch (e) {
@@ -35872,7 +36207,7 @@ ${effect.notification.body || ""}`.trim();
35872
36207
  }
35873
36208
  return { success: true, settings: allSettings, values: allValues };
35874
36209
  }
35875
- function handleSetProviderSetting(h, args) {
36210
+ async function handleSetProviderSetting(h, args) {
35876
36211
  const loader = h.ctx.providerLoader;
35877
36212
  const { providerType, key, value } = args || {};
35878
36213
  if (!providerType || !key || value === void 0) {
@@ -35885,6 +36220,7 @@ ${effect.notification.body || ""}`.trim();
35885
36220
  const updated = h.ctx.instanceManager.updateInstanceSettings(providerType, allSettings);
35886
36221
  LOG2.info("Command", `[set_provider_setting] ${providerType}.${key}=${JSON.stringify(value)} \u2192 ${updated} instance(s) updated`);
35887
36222
  }
36223
+ await h.ctx.onProviderSettingChanged?.(providerType, key, value);
35888
36224
  return { success: true, providerType, key, value };
35889
36225
  }
35890
36226
  return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
@@ -35922,10 +36258,10 @@ ${effect.notification.body || ""}`.trim();
35922
36258
  }
35923
36259
  const command = payload.command;
35924
36260
  if (!command || typeof command !== "object") return null;
35925
- if (command.type !== "send_message") return null;
36261
+ if (command.type !== "send_message" && command.type !== "pty_write") return null;
35926
36262
  const text = typeof command.text === "string" ? command.text.trim() : typeof command.message === "string" ? command.message.trim() : "";
35927
36263
  if (!text) return null;
35928
- return { type: "send_message", text };
36264
+ return { type: command.type, text };
35929
36265
  }
35930
36266
  function applyProviderPatch(h, args, payload) {
35931
36267
  if (!payload || typeof payload !== "object") return;
@@ -35966,6 +36302,8 @@ ${effect.notification.body || ""}`.trim();
35966
36302
  const cliCommand = getCliScriptCommand(parsed.payload);
35967
36303
  if (cliCommand?.type === "send_message" && cliCommand.text) {
35968
36304
  await adapter.sendMessage(cliCommand.text);
36305
+ } else if (cliCommand?.type === "pty_write" && cliCommand.text && adapter.writeRaw) {
36306
+ adapter.writeRaw(cliCommand.text + "\r");
35969
36307
  }
35970
36308
  applyProviderPatch(h, args, parsed.payload);
35971
36309
  return { success: true, ...parsed.payload && typeof parsed.payload === "object" ? parsed.payload : { result: parsed.payload } };
@@ -36640,7 +36978,7 @@ ${effect.notification.body || ""}`.trim();
36640
36978
  try {
36641
36979
  const http3 = await import("http");
36642
36980
  const postData = JSON.stringify(body);
36643
- const result = await new Promise((resolve9, reject) => {
36981
+ const result = await new Promise((resolve12, reject) => {
36644
36982
  const req = http3.request({
36645
36983
  hostname: "127.0.0.1",
36646
36984
  port: 19280,
@@ -36652,9 +36990,9 @@ ${effect.notification.body || ""}`.trim();
36652
36990
  res.on("data", (chunk) => data += chunk);
36653
36991
  res.on("end", () => {
36654
36992
  try {
36655
- resolve9(JSON.parse(data));
36993
+ resolve12(JSON.parse(data));
36656
36994
  } catch {
36657
- resolve9({ raw: data });
36995
+ resolve12({ raw: data });
36658
36996
  }
36659
36997
  });
36660
36998
  });
@@ -36672,15 +37010,15 @@ ${effect.notification.body || ""}`.trim();
36672
37010
  if (!providerType) return { success: false, error: "providerType required" };
36673
37011
  try {
36674
37012
  const http3 = await import("http");
36675
- const result = await new Promise((resolve9, reject) => {
37013
+ const result = await new Promise((resolve12, reject) => {
36676
37014
  http3.get(`http://127.0.0.1:19280/api/providers/${providerType}/${endpoint}`, (res) => {
36677
37015
  let data = "";
36678
37016
  res.on("data", (chunk) => data += chunk);
36679
37017
  res.on("end", () => {
36680
37018
  try {
36681
- resolve9(JSON.parse(data));
37019
+ resolve12(JSON.parse(data));
36682
37020
  } catch {
36683
- resolve9({ raw: data });
37021
+ resolve12({ raw: data });
36684
37022
  }
36685
37023
  });
36686
37024
  }).on("error", reject);
@@ -36694,7 +37032,7 @@ ${effect.notification.body || ""}`.trim();
36694
37032
  try {
36695
37033
  const http3 = await import("http");
36696
37034
  const postData = JSON.stringify(args || {});
36697
- const result = await new Promise((resolve9, reject) => {
37035
+ const result = await new Promise((resolve12, reject) => {
36698
37036
  const req = http3.request({
36699
37037
  hostname: "127.0.0.1",
36700
37038
  port: 19280,
@@ -36706,9 +37044,9 @@ ${effect.notification.body || ""}`.trim();
36706
37044
  res.on("data", (chunk) => data += chunk);
36707
37045
  res.on("end", () => {
36708
37046
  try {
36709
- resolve9(JSON.parse(data));
37047
+ resolve12(JSON.parse(data));
36710
37048
  } catch {
36711
- resolve9({ raw: data });
37049
+ resolve12({ raw: data });
36712
37050
  }
36713
37051
  });
36714
37052
  });
@@ -36723,13 +37061,13 @@ ${effect.notification.body || ""}`.trim();
36723
37061
  }
36724
37062
  };
36725
37063
  var os10 = __toESM2(require("os"));
36726
- var path9 = __toESM2(require("path"));
37064
+ var path11 = __toESM2(require("path"));
36727
37065
  var crypto4 = __toESM2(require("crypto"));
36728
37066
  var import_chalk = __toESM2(require("chalk"));
36729
37067
  init_provider_cli_adapter();
36730
37068
  init_config();
36731
37069
  var os9 = __toESM2(require("os"));
36732
- var path8 = __toESM2(require("path"));
37070
+ var path10 = __toESM2(require("path"));
36733
37071
  var crypto3 = __toESM2(require("crypto"));
36734
37072
  var fs5 = __toESM2(require("fs"));
36735
37073
  var import_node_module = require("module");
@@ -36738,7 +37076,7 @@ ${effect.notification.body || ""}`.trim();
36738
37076
  var CachedDatabaseSync = null;
36739
37077
  function getDatabaseSync() {
36740
37078
  if (CachedDatabaseSync) return CachedDatabaseSync;
36741
- const requireFn = typeof require === "function" ? require : (0, import_node_module.createRequire)(path8.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
37079
+ const requireFn = typeof require === "function" ? require : (0, import_node_module.createRequire)(path10.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
36742
37080
  const sqliteModule = requireFn(`node:${"sqlite"}`);
36743
37081
  CachedDatabaseSync = sqliteModule.DatabaseSync;
36744
37082
  if (!CachedDatabaseSync) {
@@ -36778,6 +37116,7 @@ ${effect.notification.body || ""}`.trim();
36778
37116
  historyWriter;
36779
37117
  runtimeMessages = [];
36780
37118
  instanceId;
37119
+ suppressIdleHistoryReplay = false;
36781
37120
  presentationMode;
36782
37121
  providerSessionId;
36783
37122
  launchMode;
@@ -36805,7 +37144,15 @@ ${effect.notification.body || ""}`.trim();
36805
37144
  await this.adapter.spawn();
36806
37145
  this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
36807
37146
  if (this.providerSessionId) {
37147
+ this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
36808
37148
  const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
37149
+ this.historyWriter.seedSessionHistory(
37150
+ this.type,
37151
+ restoredHistory.messages,
37152
+ this.providerSessionId,
37153
+ this.instanceId
37154
+ );
37155
+ this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
36809
37156
  if (restoredHistory.messages.length > 0) {
36810
37157
  this.adapter.seedCommittedMessages(
36811
37158
  restoredHistory.messages.map((message) => ({
@@ -36849,7 +37196,7 @@ ${effect.notification.body || ""}`.trim();
36849
37196
  } else if (this.type === "codex-cli") {
36850
37197
  probedSessionId = this.probeSessionIdFromConfig({
36851
37198
  dbPath: "~/.codex/state_5.sqlite",
36852
- query: "select id from threads where cwd in ({dirs}) and created_at >= ? and archived = 0 order by created_at desc limit 1",
37199
+ query: "select id from threads where cwd in ({dirs}) and updated_at >= ? and archived = 0 order by updated_at desc limit 1",
36853
37200
  timestampFormat: "unix_s"
36854
37201
  });
36855
37202
  } else if (this.type === "goose-cli") {
@@ -36909,6 +37256,7 @@ ${effect.notification.body || ""}`.trim();
36909
37256
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
36910
37257
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
36911
37258
  if (parsedMessages.length > 0) {
37259
+ const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
36912
37260
  let messagesToSave = parsedMessages;
36913
37261
  if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
36914
37262
  const lastIdx = messagesToSave.length - 1;
@@ -36916,7 +37264,7 @@ ${effect.notification.body || ""}`.trim();
36916
37264
  messagesToSave = messagesToSave.slice(0, lastIdx);
36917
37265
  }
36918
37266
  }
36919
- if (messagesToSave.length > 0) {
37267
+ if (!shouldSkipReplayPersist && messagesToSave.length > 0) {
36920
37268
  this.historyWriter.appendNewMessages(
36921
37269
  this.type,
36922
37270
  messagesToSave,
@@ -37011,6 +37359,7 @@ ${effect.notification.body || ""}`.trim();
37011
37359
  if (newStatus !== this.lastStatus) {
37012
37360
  LOG2.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
37013
37361
  if (this.lastStatus === "idle" && newStatus === "generating") {
37362
+ this.suppressIdleHistoryReplay = false;
37014
37363
  if (this.completedDebouncePending) {
37015
37364
  LOG2.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
37016
37365
  if (this.completedDebounceTimer) {
@@ -37030,6 +37379,7 @@ ${effect.notification.body || ""}`.trim();
37030
37379
  this.generatingDebounceTimer = null;
37031
37380
  }, 1e3);
37032
37381
  } else if (newStatus === "waiting_approval") {
37382
+ this.suppressIdleHistoryReplay = false;
37033
37383
  if (this.generatingDebouncePending) {
37034
37384
  if (this.generatingDebounceTimer) {
37035
37385
  clearTimeout(this.generatingDebounceTimer);
@@ -37561,8 +37911,9 @@ ${effect.notification.body || ""}`.trim();
37561
37911
  async setConfigOption(category, value) {
37562
37912
  const opt = this.configOptions.find((c) => c.category === category);
37563
37913
  if (!opt) {
37564
- this.log.warn(`[${this.type}] No config option for category: ${category}`);
37565
- return;
37914
+ const message = `[${this.type}] No config option for category: ${category}`;
37915
+ this.log.warn(message);
37916
+ throw new Error(message);
37566
37917
  }
37567
37918
  if (this.useStaticConfig) {
37568
37919
  opt.currentValue = value;
@@ -37574,8 +37925,9 @@ ${effect.notification.body || ""}`.trim();
37574
37925
  return;
37575
37926
  }
37576
37927
  if (!this.connection || !this.sessionId) {
37577
- this.log.warn(`[${this.type}] Cannot set config: no active connection/session`);
37578
- return;
37928
+ const message = `[${this.type}] Cannot set config: no active connection/session`;
37929
+ this.log.warn(message);
37930
+ throw new Error(message);
37579
37931
  }
37580
37932
  try {
37581
37933
  this.log.info(`[${this.type}] Sending session/set_config_option: configId=${opt.configId} value=${value} sessionId=${this.sessionId}`);
@@ -37589,7 +37941,9 @@ ${effect.notification.body || ""}`.trim();
37589
37941
  if (result?.configOptions) this.parseConfigOptions(result.configOptions);
37590
37942
  this.log.info(`[${this.type}] Config ${category} set to: ${value} | response: ${JSON.stringify(result)?.slice(0, 300)}`);
37591
37943
  } catch (e) {
37592
- this.log.warn(`[${this.type}] set_config_option failed: ${e?.message}`);
37944
+ const message = e?.message || "Unknown ACP config error";
37945
+ this.log.warn(`[${this.type}] set_config_option failed: ${message}`);
37946
+ throw new Error(message);
37593
37947
  }
37594
37948
  }
37595
37949
  async setMode(modeId) {
@@ -37605,8 +37959,9 @@ ${effect.notification.body || ""}`.trim();
37605
37959
  return;
37606
37960
  }
37607
37961
  if (!this.connection || !this.sessionId) {
37608
- this.log.warn(`[${this.type}] Cannot set mode: no active connection/session`);
37609
- return;
37962
+ const message = `[${this.type}] Cannot set mode: no active connection/session`;
37963
+ this.log.warn(message);
37964
+ throw new Error(message);
37610
37965
  }
37611
37966
  try {
37612
37967
  await this.connection.setSessionMode({
@@ -37616,7 +37971,9 @@ ${effect.notification.body || ""}`.trim();
37616
37971
  this.currentMode = modeId;
37617
37972
  this.log.info(`[${this.type}] Mode set to: ${modeId}`);
37618
37973
  } catch (e) {
37619
- this.log.warn(`[${this.type}] set_mode failed: ${e?.message}`);
37974
+ const message = e?.message || "Unknown ACP mode error";
37975
+ this.log.warn(`[${this.type}] set_mode failed: ${message}`);
37976
+ throw new Error(message);
37620
37977
  }
37621
37978
  }
37622
37979
  /** Static config: kill process and restart with new args */
@@ -37664,7 +38021,7 @@ ${effect.notification.body || ""}`.trim();
37664
38021
  if (!spawnConfig) {
37665
38022
  throw new Error(`[ACP:${this.type}] No spawn config defined`);
37666
38023
  }
37667
- const command = spawnConfig.command;
38024
+ const command = typeof this.settings.executablePath === "string" && this.settings.executablePath.trim() ? this.settings.executablePath.trim() : spawnConfig.command;
37668
38025
  let baseArgs = spawnConfig.args || [];
37669
38026
  if (this.provider.spawnArgBuilder && Object.keys(this.selectedConfig).length > 0) {
37670
38027
  baseArgs = this.provider.spawnArgBuilder(this.selectedConfig);
@@ -37780,13 +38137,13 @@ ${effect.notification.body || ""}`.trim();
37780
38137
  }
37781
38138
  this.currentStatus = "waiting_approval";
37782
38139
  this.detectStatusTransition();
37783
- const approved = await new Promise((resolve9) => {
37784
- this.permissionResolvers.push(resolve9);
38140
+ const approved = await new Promise((resolve12) => {
38141
+ this.permissionResolvers.push(resolve12);
37785
38142
  setTimeout(() => {
37786
- const idx = this.permissionResolvers.indexOf(resolve9);
38143
+ const idx = this.permissionResolvers.indexOf(resolve12);
37787
38144
  if (idx >= 0) {
37788
38145
  this.permissionResolvers.splice(idx, 1);
37789
- resolve9(false);
38146
+ resolve12(false);
37790
38147
  }
37791
38148
  }, 3e5);
37792
38149
  });
@@ -38491,7 +38848,7 @@ ${effect.notification.body || ""}`.trim();
38491
38848
  async startSession(cliType, workingDir, cliArgs, initialModel, options) {
38492
38849
  const trimmed = (workingDir || "").trim();
38493
38850
  if (!trimmed) throw new Error("working directory required");
38494
- const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path9.resolve(trimmed);
38851
+ const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path11.resolve(trimmed);
38495
38852
  const normalizedType = this.providerLoader.resolveAlias(cliType);
38496
38853
  const provider = this.providerLoader.getByAlias(cliType);
38497
38854
  const key = crypto4.randomUUID();
@@ -38578,10 +38935,10 @@ ${installInfo}`
38578
38935
  if (!cliInfo) {
38579
38936
  const installHint = provider?.install || "";
38580
38937
  const displayName = provider?.displayName || provider?.name || cliType;
38581
- const spawnCmd = provider?.spawn?.command || cliType;
38938
+ const spawnCmd = this.providerLoader.getSpawnCommand(normalizedType, provider?.spawn?.command || cliType);
38582
38939
  throw new Error(
38583
38940
  `${displayName} is not installed.
38584
- Command '${spawnCmd}' not found on PATH.
38941
+ Command '${spawnCmd}' is not available.
38585
38942
  ` + (installHint ? `
38586
38943
  ${installHint}
38587
38944
  ` : "") + `
@@ -38727,6 +39084,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
38727
39084
  if (!instanceManager) return 0;
38728
39085
  const sessions = records || await this.deps.listHostedCliRuntimes?.() || [];
38729
39086
  let restored = 0;
39087
+ const restoredBindings = /* @__PURE__ */ new Set();
38730
39088
  for (const record2 of sessions) {
38731
39089
  if (!record2?.runtimeId || !record2?.cliType || !record2?.workspace) continue;
38732
39090
  if (this.adapters.has(record2.runtimeId) || instanceManager.getInstance(record2.runtimeId)) continue;
@@ -38740,6 +39098,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
38740
39098
  record2.cliArgs,
38741
39099
  record2.providerSessionId
38742
39100
  );
39101
+ const bindingKey = [
39102
+ normalizedType,
39103
+ record2.workspace,
39104
+ sessionBinding.providerSessionId || record2.runtimeId
39105
+ ].join("::");
39106
+ if (restoredBindings.has(bindingKey)) {
39107
+ LOG2.info(
39108
+ "CLI",
39109
+ `\u21B7 Skipping duplicate hosted runtime restore: ${record2.runtimeKey || record2.runtimeId} (${normalizedType} @ ${record2.workspace}) binding=${sessionBinding.providerSessionId || "runtime"}`
39110
+ );
39111
+ continue;
39112
+ }
38743
39113
  try {
38744
39114
  await this.registerCliInstance(
38745
39115
  record2.runtimeId,
@@ -38755,6 +39125,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
38755
39125
  launchMode: "manual"
38756
39126
  }
38757
39127
  );
39128
+ restoredBindings.add(bindingKey);
38758
39129
  restored += 1;
38759
39130
  LOG2.info("CLI", `\u267B Restored hosted runtime: ${record2.runtimeKey || record2.runtimeId} (${record2.displayName || record2.workspace})`);
38760
39131
  } catch (error48) {
@@ -38939,14 +39310,15 @@ Run 'adhdev doctor' for detailed diagnostics.`
38939
39310
  var import_child_process6 = require("child_process");
38940
39311
  var net3 = __toESM2(require("net"));
38941
39312
  var os12 = __toESM2(require("os"));
38942
- var path11 = __toESM2(require("path"));
39313
+ var path13 = __toESM2(require("path"));
38943
39314
  var fs6 = __toESM2(require("fs"));
38944
- var path10 = __toESM2(require("path"));
39315
+ var path12 = __toESM2(require("path"));
38945
39316
  var os11 = __toESM2(require("os"));
38946
39317
  var chokidar = __toESM2((init_chokidar(), __toCommonJS(chokidar_exports)));
38947
39318
  init_logger();
38948
39319
  var ProviderLoader = class _ProviderLoader {
38949
39320
  providers = /* @__PURE__ */ new Map();
39321
+ providerAvailability = /* @__PURE__ */ new Map();
38950
39322
  userDir;
38951
39323
  upstreamDir;
38952
39324
  disableUpstream;
@@ -38962,12 +39334,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
38962
39334
  static META_FILE = ".meta.json";
38963
39335
  constructor(options) {
38964
39336
  this.logFn = options?.logFn || LOG2.forComponent("Provider").asLogFn();
38965
- const defaultProvidersDir = path10.join(os11.homedir(), ".adhdev", "providers");
39337
+ const defaultProvidersDir = path12.join(os11.homedir(), ".adhdev", "providers");
38966
39338
  if (options?.userDir) {
38967
39339
  this.userDir = options.userDir;
38968
39340
  this.log(`Config 'providerDir' applied: ${this.userDir}`);
38969
39341
  } else {
38970
- const localRepoPath = path10.resolve(__dirname, "../../../../../adhdev-providers");
39342
+ const localRepoPath = path12.resolve(__dirname, "../../../../../adhdev-providers");
38971
39343
  if (fs6.existsSync(localRepoPath)) {
38972
39344
  this.userDir = localRepoPath;
38973
39345
  this.log(`Auto-detected local public repository: ${this.userDir} (Dev workspace speedup)`);
@@ -38976,7 +39348,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
38976
39348
  this.log(`Using default user providers directory: ${this.userDir}`);
38977
39349
  }
38978
39350
  }
38979
- this.upstreamDir = path10.join(defaultProvidersDir, ".upstream");
39351
+ this.upstreamDir = path12.join(defaultProvidersDir, ".upstream");
38980
39352
  this.disableUpstream = options?.disableUpstream ?? false;
38981
39353
  }
38982
39354
  log(msg) {
@@ -39006,7 +39378,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39006
39378
  * Canonical provider directory shape for a given root.
39007
39379
  */
39008
39380
  getProviderDir(root, category, type) {
39009
- return path10.join(root, category, type);
39381
+ return path12.join(root, category, type);
39010
39382
  }
39011
39383
  /**
39012
39384
  * Canonical user override directory for a provider.
@@ -39033,7 +39405,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39033
39405
  resolveProviderFile(type, ...segments) {
39034
39406
  const dir = this.findProviderDirInternal(type);
39035
39407
  if (!dir) return null;
39036
- return path10.join(dir, ...segments);
39408
+ return path12.join(dir, ...segments);
39037
39409
  }
39038
39410
  /**
39039
39411
  * Load all providers (3-tier priority)
@@ -39044,6 +39416,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39044
39416
  */
39045
39417
  loadAll() {
39046
39418
  this.providers.clear();
39419
+ this.providerAvailability.clear();
39047
39420
  let upstreamCount = 0;
39048
39421
  if (!this.disableUpstream && fs6.existsSync(this.upstreamDir)) {
39049
39422
  upstreamCount = this.loadDir(this.upstreamDir);
@@ -39071,7 +39444,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39071
39444
  if (!fs6.existsSync(this.upstreamDir)) return false;
39072
39445
  try {
39073
39446
  return fs6.readdirSync(this.upstreamDir).some(
39074
- (d) => fs6.statSync(path10.join(this.upstreamDir, d)).isDirectory()
39447
+ (d) => fs6.statSync(path12.join(this.upstreamDir, d)).isDirectory()
39075
39448
  );
39076
39449
  } catch {
39077
39450
  return false;
@@ -39114,11 +39487,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
39114
39487
  if ((p.category === "cli" || p.category === "acp") && p.spawn?.command) {
39115
39488
  const verCmdConfig = p.versionCommand;
39116
39489
  const versionCommand = typeof verCmdConfig === "object" && verCmdConfig !== null ? verCmdConfig[process.platform] : verCmdConfig;
39490
+ const command = this.getSpawnCommand(p.type, p.spawn.command);
39117
39491
  result.push({
39118
39492
  id: p.type,
39119
39493
  displayName: p.displayName || p.name,
39120
39494
  icon: p.icon || "\u{1F527}",
39121
- command: p.spawn.command,
39495
+ command,
39122
39496
  category: p.category,
39123
39497
  ...typeof versionCommand === "string" && versionCommand.trim() ? { versionCommand: versionCommand.trim() } : {}
39124
39498
  });
@@ -39247,6 +39621,71 @@ Run 'adhdev doctor' for detailed diagnostics.`
39247
39621
  getAvailableIdeTypes() {
39248
39622
  return [...this.providers.values()].filter((p) => p.category === "ide" && p.cdpPorts).map((p) => p.type);
39249
39623
  }
39624
+ getSpawnCommand(type, fallback) {
39625
+ const override = this.getOptionalStringSetting(type, "executablePath");
39626
+ if (override) return override;
39627
+ return fallback || this.providers.get(type)?.spawn?.command || type;
39628
+ }
39629
+ getIdeCliCommand(type, fallback) {
39630
+ const override = this.getOptionalStringSetting(type, "cliPathOverride");
39631
+ if (override) return override;
39632
+ return fallback || this.providers.get(type)?.cli || null;
39633
+ }
39634
+ getIdePathCandidates(type, fallback) {
39635
+ const override = this.getOptionalStringSetting(type, "appPathOverride");
39636
+ if (override) return [override];
39637
+ if (fallback && fallback.length > 0) return fallback;
39638
+ const osPaths = this.providers.get(type)?.paths?.[process.platform];
39639
+ return Array.isArray(osPaths) ? [...osPaths] : [];
39640
+ }
39641
+ setProviderAvailability(type, state) {
39642
+ this.providerAvailability.set(type, {
39643
+ installed: !!state.installed,
39644
+ detectedPath: state.detectedPath ?? null
39645
+ });
39646
+ }
39647
+ setCliDetectionResults(results, replace = true) {
39648
+ if (replace) {
39649
+ for (const provider of this.providers.values()) {
39650
+ if (provider.category === "cli" || provider.category === "acp") {
39651
+ this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
39652
+ }
39653
+ }
39654
+ }
39655
+ for (const result of results) {
39656
+ this.setProviderAvailability(result.id, {
39657
+ installed: !!result.installed,
39658
+ detectedPath: result.path || null
39659
+ });
39660
+ }
39661
+ }
39662
+ setIdeDetectionResults(results, replace = true) {
39663
+ if (replace) {
39664
+ for (const provider of this.providers.values()) {
39665
+ if (provider.category === "ide") {
39666
+ this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
39667
+ }
39668
+ }
39669
+ }
39670
+ for (const result of results) {
39671
+ this.setProviderAvailability(result.id, {
39672
+ installed: !!result.installed,
39673
+ detectedPath: result.cliCommand || result.path || null
39674
+ });
39675
+ }
39676
+ }
39677
+ getAvailableProviderInfos() {
39678
+ return this.getAll().map((provider) => {
39679
+ const availability = this.providerAvailability.get(provider.type);
39680
+ return {
39681
+ ...provider,
39682
+ ...availability ? {
39683
+ installed: availability.installed,
39684
+ detectedPath: availability.detectedPath
39685
+ } : {}
39686
+ };
39687
+ });
39688
+ }
39250
39689
  /**
39251
39690
  * Register IDE providers to core/detector registry
39252
39691
  * → Enables detectIDEs() to detect provider.js-based IDEs
@@ -39321,8 +39760,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
39321
39760
  resolved._resolvedScriptDir = entry.scriptDir;
39322
39761
  resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
39323
39762
  if (providerDir) {
39324
- const fullDir = path10.join(providerDir, entry.scriptDir);
39325
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
39763
+ const fullDir = path12.join(providerDir, entry.scriptDir);
39764
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
39326
39765
  }
39327
39766
  matched = true;
39328
39767
  }
@@ -39337,8 +39776,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
39337
39776
  resolved._resolvedScriptDir = base.defaultScriptDir;
39338
39777
  resolved._resolvedScriptsSource = "defaultScriptDir:version_miss";
39339
39778
  if (providerDir) {
39340
- const fullDir = path10.join(providerDir, base.defaultScriptDir);
39341
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
39779
+ const fullDir = path12.join(providerDir, base.defaultScriptDir);
39780
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
39342
39781
  }
39343
39782
  }
39344
39783
  resolved._versionWarning = `Version ${currentVersion} not in compatibility matrix. Using default scripts.`;
@@ -39355,8 +39794,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
39355
39794
  resolved._resolvedScriptDir = dirOverride;
39356
39795
  resolved._resolvedScriptsSource = `versions:${range}`;
39357
39796
  if (providerDir) {
39358
- const fullDir = path10.join(providerDir, dirOverride);
39359
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
39797
+ const fullDir = path12.join(providerDir, dirOverride);
39798
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
39360
39799
  }
39361
39800
  }
39362
39801
  } else if (override.scripts) {
@@ -39372,8 +39811,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
39372
39811
  resolved._resolvedScriptDir = base.defaultScriptDir;
39373
39812
  resolved._resolvedScriptsSource = "defaultScriptDir:no_version";
39374
39813
  if (providerDir) {
39375
- const fullDir = path10.join(providerDir, base.defaultScriptDir);
39376
- resolved._resolvedScriptsPath = fs6.existsSync(path10.join(fullDir, "scripts.js")) ? path10.join(fullDir, "scripts.js") : fullDir;
39814
+ const fullDir = path12.join(providerDir, base.defaultScriptDir);
39815
+ resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
39377
39816
  }
39378
39817
  }
39379
39818
  }
@@ -39398,14 +39837,14 @@ Run 'adhdev doctor' for detailed diagnostics.`
39398
39837
  this.log(` [loadScriptsFromDir] ${type}: providerDir not found`);
39399
39838
  return null;
39400
39839
  }
39401
- const dir = path10.join(providerDir, scriptDir);
39840
+ const dir = path12.join(providerDir, scriptDir);
39402
39841
  if (!fs6.existsSync(dir)) {
39403
39842
  this.log(` [loadScriptsFromDir] ${type}: dir not found: ${dir}`);
39404
39843
  return null;
39405
39844
  }
39406
39845
  const cached2 = this.scriptsCache.get(dir);
39407
39846
  if (cached2) return cached2;
39408
- const scriptsJs = path10.join(dir, "scripts.js");
39847
+ const scriptsJs = path12.join(dir, "scripts.js");
39409
39848
  if (fs6.existsSync(scriptsJs)) {
39410
39849
  try {
39411
39850
  delete require.cache[require.resolve(scriptsJs)];
@@ -39447,7 +39886,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39447
39886
  return;
39448
39887
  }
39449
39888
  if (filePath.endsWith(".js") || filePath.endsWith(".json")) {
39450
- this.log(`File changed: ${path10.basename(filePath)}, reloading...`);
39889
+ this.log(`File changed: ${path12.basename(filePath)}, reloading...`);
39451
39890
  this.reload();
39452
39891
  }
39453
39892
  };
@@ -39502,7 +39941,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39502
39941
  }
39503
39942
  const https = require("https");
39504
39943
  const { execSync: execSync7 } = require("child_process");
39505
- const metaPath = path10.join(this.upstreamDir, _ProviderLoader.META_FILE);
39944
+ const metaPath = path12.join(this.upstreamDir, _ProviderLoader.META_FILE);
39506
39945
  let prevEtag = "";
39507
39946
  let prevTimestamp = 0;
39508
39947
  try {
@@ -39519,7 +39958,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39519
39958
  return { updated: false };
39520
39959
  }
39521
39960
  try {
39522
- const etag = await new Promise((resolve9, reject) => {
39961
+ const etag = await new Promise((resolve12, reject) => {
39523
39962
  const options = {
39524
39963
  method: "HEAD",
39525
39964
  hostname: "github.com",
@@ -39537,7 +39976,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39537
39976
  headers: { "User-Agent": "adhdev-launcher" },
39538
39977
  timeout: 1e4
39539
39978
  }, (res2) => {
39540
- resolve9(res2.headers.etag || res2.headers["last-modified"] || "");
39979
+ resolve12(res2.headers.etag || res2.headers["last-modified"] || "");
39541
39980
  });
39542
39981
  req2.on("error", reject);
39543
39982
  req2.on("timeout", () => {
@@ -39546,7 +39985,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39546
39985
  });
39547
39986
  req2.end();
39548
39987
  } else {
39549
- resolve9(res.headers.etag || res.headers["last-modified"] || "");
39988
+ resolve12(res.headers.etag || res.headers["last-modified"] || "");
39550
39989
  }
39551
39990
  });
39552
39991
  req.on("error", reject);
@@ -39562,17 +40001,17 @@ Run 'adhdev doctor' for detailed diagnostics.`
39562
40001
  return { updated: false };
39563
40002
  }
39564
40003
  this.log("Downloading latest providers from GitHub...");
39565
- const tmpTar = path10.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
39566
- const tmpExtract = path10.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
40004
+ const tmpTar = path12.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
40005
+ const tmpExtract = path12.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
39567
40006
  await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
39568
40007
  fs6.mkdirSync(tmpExtract, { recursive: true });
39569
40008
  execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
39570
40009
  const extracted = fs6.readdirSync(tmpExtract);
39571
40010
  const rootDir = extracted.find(
39572
- (d) => fs6.statSync(path10.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
40011
+ (d) => fs6.statSync(path12.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
39573
40012
  );
39574
40013
  if (!rootDir) throw new Error("Unexpected tarball structure");
39575
- const sourceDir = path10.join(tmpExtract, rootDir);
40014
+ const sourceDir = path12.join(tmpExtract, rootDir);
39576
40015
  const backupDir = this.upstreamDir + ".bak";
39577
40016
  if (fs6.existsSync(this.upstreamDir)) {
39578
40017
  if (fs6.existsSync(backupDir)) fs6.rmSync(backupDir, { recursive: true, force: true });
@@ -39610,7 +40049,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39610
40049
  downloadFile(url2, destPath) {
39611
40050
  const https = require("https");
39612
40051
  const http3 = require("http");
39613
- return new Promise((resolve9, reject) => {
40052
+ return new Promise((resolve12, reject) => {
39614
40053
  const doRequest = (reqUrl, redirectCount = 0) => {
39615
40054
  if (redirectCount > 5) {
39616
40055
  reject(new Error("Too many redirects"));
@@ -39630,7 +40069,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39630
40069
  res.pipe(ws2);
39631
40070
  ws2.on("finish", () => {
39632
40071
  ws2.close();
39633
- resolve9();
40072
+ resolve12();
39634
40073
  });
39635
40074
  ws2.on("error", reject);
39636
40075
  });
@@ -39647,8 +40086,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
39647
40086
  copyDirRecursive(src, dest) {
39648
40087
  fs6.mkdirSync(dest, { recursive: true });
39649
40088
  for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
39650
- const srcPath = path10.join(src, entry.name);
39651
- const destPath = path10.join(dest, entry.name);
40089
+ const srcPath = path12.join(src, entry.name);
40090
+ const destPath = path12.join(dest, entry.name);
39652
40091
  if (entry.isDirectory()) {
39653
40092
  this.copyDirRecursive(srcPath, destPath);
39654
40093
  } else {
@@ -39659,7 +40098,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39659
40098
  /** .meta.json save */
39660
40099
  writeMeta(metaPath, etag, timestamp) {
39661
40100
  try {
39662
- fs6.mkdirSync(path10.dirname(metaPath), { recursive: true });
40101
+ fs6.mkdirSync(path12.dirname(metaPath), { recursive: true });
39663
40102
  fs6.writeFileSync(metaPath, JSON.stringify({
39664
40103
  etag,
39665
40104
  timestamp,
@@ -39676,7 +40115,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39676
40115
  const scan = (d) => {
39677
40116
  try {
39678
40117
  for (const entry of fs6.readdirSync(d, { withFileTypes: true })) {
39679
- if (entry.isDirectory()) scan(path10.join(d, entry.name));
40118
+ if (entry.isDirectory()) scan(path12.join(d, entry.name));
39680
40119
  else if (entry.name === "provider.json") count++;
39681
40120
  }
39682
40121
  } catch {
@@ -39690,9 +40129,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
39690
40129
  * Get public settings schema for a provider (for dashboard UI rendering)
39691
40130
  */
39692
40131
  getPublicSettings(type) {
39693
- const provider = this.providers.get(type);
39694
- if (!provider?.settings) return [];
39695
- return Object.entries(provider.settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
40132
+ const settings = this.getSettingsSchema(type);
40133
+ return Object.entries(settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
39696
40134
  }
39697
40135
  /**
39698
40136
  * Get public settings schema for all providers
@@ -39709,8 +40147,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39709
40147
  * Resolved setting value for a provider (default + user override)
39710
40148
  */
39711
40149
  getSettingValue(type, key) {
39712
- const provider = this.providers.get(type);
39713
- const schemaDef = provider?.settings?.[key];
40150
+ const schemaDef = this.getSettingsSchema(type)[key];
39714
40151
  const defaultVal = schemaDef ? schemaDef.default : void 0;
39715
40152
  try {
39716
40153
  const { loadConfig: loadConfig22 } = (init_config(), __toCommonJS2(config_exports));
@@ -39725,10 +40162,9 @@ Run 'adhdev doctor' for detailed diagnostics.`
39725
40162
  * All resolved settings for a provider (default + user override)
39726
40163
  */
39727
40164
  getSettings(type) {
39728
- const provider = this.providers.get(type);
39729
- if (!provider?.settings) return {};
40165
+ const settings = this.getSettingsSchema(type);
39730
40166
  const result = {};
39731
- for (const [key, def] of Object.entries(provider.settings)) {
40167
+ for (const [key] of Object.entries(settings)) {
39732
40168
  result[key] = this.getSettingValue(type, key);
39733
40169
  }
39734
40170
  return result;
@@ -39737,11 +40173,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
39737
40173
  * Save provider setting value (writes to config.json)
39738
40174
  */
39739
40175
  setSetting(type, key, value) {
39740
- const provider = this.providers.get(type);
39741
- const schemaDef = provider?.settings?.[key];
40176
+ const schemaDef = this.getSettingsSchema(type)[key];
39742
40177
  if (!schemaDef) return false;
39743
40178
  if (!schemaDef.public) return false;
39744
40179
  if (schemaDef.type === "boolean" && typeof value !== "boolean") return false;
40180
+ if (schemaDef.type === "string" && typeof value !== "string") return false;
39745
40181
  if (schemaDef.type === "number") {
39746
40182
  if (typeof value !== "number") return false;
39747
40183
  if (schemaDef.min !== void 0 && value < schemaDef.min) return false;
@@ -39762,6 +40198,53 @@ Run 'adhdev doctor' for detailed diagnostics.`
39762
40198
  return false;
39763
40199
  }
39764
40200
  }
40201
+ getOptionalStringSetting(type, key) {
40202
+ const value = this.getSettingValue(type, key);
40203
+ if (typeof value !== "string") return null;
40204
+ const trimmed = value.trim();
40205
+ return trimmed ? trimmed : null;
40206
+ }
40207
+ getSettingsSchema(type) {
40208
+ const provider = this.providers.get(type);
40209
+ if (!provider) return {};
40210
+ return {
40211
+ ...this.getSyntheticSettings(type, provider),
40212
+ ...provider.settings || {}
40213
+ };
40214
+ }
40215
+ getSyntheticSettings(type, provider) {
40216
+ const result = {};
40217
+ if ((provider.category === "cli" || provider.category === "acp") && provider.spawn?.command && !provider.settings?.executablePath) {
40218
+ result.executablePath = {
40219
+ type: "string",
40220
+ default: "",
40221
+ public: true,
40222
+ label: "Executable path",
40223
+ description: "Optional absolute path for this provider binary. Leave blank to use the default PATH lookup."
40224
+ };
40225
+ }
40226
+ if (provider.category === "ide") {
40227
+ if (provider.cli && !provider.settings?.cliPathOverride) {
40228
+ result.cliPathOverride = {
40229
+ type: "string",
40230
+ default: "",
40231
+ public: true,
40232
+ label: "CLI path override",
40233
+ description: "Optional absolute path for the IDE CLI launcher. Leave blank to use the detected default."
40234
+ };
40235
+ }
40236
+ if (provider.paths && !provider.settings?.appPathOverride) {
40237
+ result.appPathOverride = {
40238
+ type: "string",
40239
+ default: "",
40240
+ public: true,
40241
+ label: "App path override",
40242
+ description: "Optional absolute path for the IDE app bundle or executable. Leave blank to use the default install locations."
40243
+ };
40244
+ }
40245
+ }
40246
+ return result;
40247
+ }
39765
40248
  // ─── Private ───────────────────────────────────
39766
40249
  /**
39767
40250
  * Find the on-disk directory for a provider by type.
@@ -39775,17 +40258,17 @@ Run 'adhdev doctor' for detailed diagnostics.`
39775
40258
  for (const root of searchRoots) {
39776
40259
  if (!fs6.existsSync(root)) continue;
39777
40260
  const candidate = this.getProviderDir(root, cat, type);
39778
- if (fs6.existsSync(path10.join(candidate, "provider.json"))) return candidate;
39779
- const catDir = path10.join(root, cat);
40261
+ if (fs6.existsSync(path12.join(candidate, "provider.json"))) return candidate;
40262
+ const catDir = path12.join(root, cat);
39780
40263
  if (fs6.existsSync(catDir)) {
39781
40264
  try {
39782
40265
  for (const entry of fs6.readdirSync(catDir, { withFileTypes: true })) {
39783
40266
  if (!entry.isDirectory()) continue;
39784
- const jsonPath = path10.join(catDir, entry.name, "provider.json");
40267
+ const jsonPath = path12.join(catDir, entry.name, "provider.json");
39785
40268
  if (fs6.existsSync(jsonPath)) {
39786
40269
  try {
39787
40270
  const data = JSON.parse(fs6.readFileSync(jsonPath, "utf-8"));
39788
- if (data.type === type) return path10.join(catDir, entry.name);
40271
+ if (data.type === type) return path12.join(catDir, entry.name);
39789
40272
  } catch {
39790
40273
  }
39791
40274
  }
@@ -39802,7 +40285,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39802
40285
  * (template substitution is NOT applied here — scripts.js handles that)
39803
40286
  */
39804
40287
  buildScriptWrappersFromDir(dir) {
39805
- const scriptsJs = path10.join(dir, "scripts.js");
40288
+ const scriptsJs = path12.join(dir, "scripts.js");
39806
40289
  if (fs6.existsSync(scriptsJs)) {
39807
40290
  try {
39808
40291
  delete require.cache[require.resolve(scriptsJs)];
@@ -39816,7 +40299,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39816
40299
  for (const file2 of fs6.readdirSync(dir)) {
39817
40300
  if (!file2.endsWith(".js")) continue;
39818
40301
  const scriptName = toCamel(file2.replace(".js", ""));
39819
- const filePath = path10.join(dir, file2);
40302
+ const filePath = path12.join(dir, file2);
39820
40303
  result[scriptName] = (...args) => {
39821
40304
  try {
39822
40305
  let content = fs6.readFileSync(filePath, "utf-8");
@@ -39876,7 +40359,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39876
40359
  }
39877
40360
  const hasJson = entries.some((e) => e.name === "provider.json");
39878
40361
  if (hasJson) {
39879
- const jsonPath = path10.join(d, "provider.json");
40362
+ const jsonPath = path12.join(d, "provider.json");
39880
40363
  try {
39881
40364
  const raw = fs6.readFileSync(jsonPath, "utf-8");
39882
40365
  const mod = JSON.parse(raw);
@@ -39889,7 +40372,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39889
40372
  delete mod.extensionIdPattern_flags;
39890
40373
  }
39891
40374
  const hasCompatibility = Array.isArray(mod.compatibility);
39892
- const scriptsPath = path10.join(d, "scripts.js");
40375
+ const scriptsPath = path12.join(d, "scripts.js");
39893
40376
  if (!hasCompatibility && fs6.existsSync(scriptsPath)) {
39894
40377
  try {
39895
40378
  delete require.cache[require.resolve(scriptsPath)];
@@ -39915,7 +40398,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
39915
40398
  if (!entry.isDirectory()) continue;
39916
40399
  if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
39917
40400
  if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
39918
- scan(path10.join(d, entry.name));
40401
+ scan(path12.join(d, entry.name));
39919
40402
  }
39920
40403
  }
39921
40404
  };
@@ -40009,17 +40492,17 @@ Run 'adhdev doctor' for detailed diagnostics.`
40009
40492
  throw new Error("No free port found");
40010
40493
  }
40011
40494
  function checkPortFree(port) {
40012
- return new Promise((resolve9) => {
40495
+ return new Promise((resolve12) => {
40013
40496
  const server = net3.createServer();
40014
40497
  server.unref();
40015
- server.on("error", () => resolve9(false));
40498
+ server.on("error", () => resolve12(false));
40016
40499
  server.listen(port, "127.0.0.1", () => {
40017
- server.close(() => resolve9(true));
40500
+ server.close(() => resolve12(true));
40018
40501
  });
40019
40502
  });
40020
40503
  }
40021
40504
  async function isCdpActive(port) {
40022
- return new Promise((resolve9) => {
40505
+ return new Promise((resolve12) => {
40023
40506
  const req = require("http").get(`http://127.0.0.1:${port}/json/version`, {
40024
40507
  timeout: 2e3
40025
40508
  }, (res) => {
@@ -40028,16 +40511,16 @@ Run 'adhdev doctor' for detailed diagnostics.`
40028
40511
  res.on("end", () => {
40029
40512
  try {
40030
40513
  const info = JSON.parse(data);
40031
- resolve9(!!info["WebKit-Version"] || !!info["Browser"]);
40514
+ resolve12(!!info["WebKit-Version"] || !!info["Browser"]);
40032
40515
  } catch {
40033
- resolve9(false);
40516
+ resolve12(false);
40034
40517
  }
40035
40518
  });
40036
40519
  });
40037
- req.on("error", () => resolve9(false));
40520
+ req.on("error", () => resolve12(false));
40038
40521
  req.on("timeout", () => {
40039
40522
  req.destroy();
40040
- resolve9(false);
40523
+ resolve12(false);
40041
40524
  });
40042
40525
  });
40043
40526
  }
@@ -40171,8 +40654,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
40171
40654
  const appNameMap = getMacAppIdentifiers();
40172
40655
  const appName = appNameMap[ideId];
40173
40656
  if (appName) {
40174
- const storagePath = path11.join(
40175
- process.env.APPDATA || path11.join(os12.homedir(), "AppData", "Roaming"),
40657
+ const storagePath = path13.join(
40658
+ process.env.APPDATA || path13.join(os12.homedir(), "AppData", "Roaming"),
40176
40659
  appName,
40177
40660
  "storage.json"
40178
40661
  );
@@ -40196,7 +40679,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
40196
40679
  async function launchWithCdp(options = {}) {
40197
40680
  const platform9 = os12.platform();
40198
40681
  let targetIde;
40199
- const ides = await detectIDEs();
40682
+ const ides = await detectIDEs(getProviderLoader());
40200
40683
  if (options.ideId) {
40201
40684
  targetIde = ides.find((i) => i.id === options.ideId && i.installed);
40202
40685
  if (!targetIde) {
@@ -40346,9 +40829,9 @@ Run 'adhdev doctor' for detailed diagnostics.`
40346
40829
  init_config();
40347
40830
  init_logger();
40348
40831
  var fs7 = __toESM2(require("fs"));
40349
- var path12 = __toESM2(require("path"));
40832
+ var path14 = __toESM2(require("path"));
40350
40833
  var os13 = __toESM2(require("os"));
40351
- 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");
40834
+ 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");
40352
40835
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
40353
40836
  var MAX_DAYS = 7;
40354
40837
  try {
@@ -40386,13 +40869,13 @@ Run 'adhdev doctor' for detailed diagnostics.`
40386
40869
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
40387
40870
  }
40388
40871
  var currentDate2 = getDateStr2();
40389
- var currentFile = path12.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
40872
+ var currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
40390
40873
  var writeCount2 = 0;
40391
40874
  function checkRotation() {
40392
40875
  const today = getDateStr2();
40393
40876
  if (today !== currentDate2) {
40394
40877
  currentDate2 = today;
40395
- currentFile = path12.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
40878
+ currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
40396
40879
  cleanOldFiles();
40397
40880
  }
40398
40881
  }
@@ -40406,7 +40889,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
40406
40889
  const dateMatch = file2.match(/commands-(\d{4}-\d{2}-\d{2})/);
40407
40890
  if (dateMatch && dateMatch[1] < cutoffStr) {
40408
40891
  try {
40409
- fs7.unlinkSync(path12.join(LOG_DIR2, file2));
40892
+ fs7.unlinkSync(path14.join(LOG_DIR2, file2));
40410
40893
  } catch {
40411
40894
  }
40412
40895
  }
@@ -40494,12 +40977,15 @@ Run 'adhdev doctor' for detailed diagnostics.`
40494
40977
  }));
40495
40978
  }
40496
40979
  function buildAvailableProviders(providerLoader) {
40497
- return providerLoader.getAll().map((provider) => ({
40980
+ const providers = providerLoader.getAvailableProviderInfos?.() || providerLoader.getAll();
40981
+ return providers.map((provider) => ({
40498
40982
  type: provider.type,
40499
40983
  name: provider.displayName || provider.type,
40500
40984
  displayName: provider.displayName || provider.type,
40501
40985
  icon: provider.icon || "\u{1F4BB}",
40502
- category: provider.category
40986
+ category: provider.category,
40987
+ ...provider.installed !== void 0 ? { installed: provider.installed } : {},
40988
+ ...provider.detectedPath !== void 0 ? { detectedPath: provider.detectedPath } : {}
40503
40989
  }));
40504
40990
  }
40505
40991
  function parseMessageTime(value) {
@@ -40625,13 +41111,13 @@ Run 'adhdev doctor' for detailed diagnostics.`
40625
41111
  var import_child_process8 = require("child_process");
40626
41112
  var fs8 = __toESM2(require("fs"));
40627
41113
  var os15 = __toESM2(require("os"));
40628
- var path13 = __toESM2(require("path"));
41114
+ var path15 = __toESM2(require("path"));
40629
41115
  var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
40630
41116
  function getUpgradeLogPath() {
40631
41117
  const home = os15.homedir();
40632
- const dir = path13.join(home, ".adhdev");
41118
+ const dir = path15.join(home, ".adhdev");
40633
41119
  fs8.mkdirSync(dir, { recursive: true });
40634
- return path13.join(dir, "daemon-upgrade.log");
41120
+ return path15.join(dir, "daemon-upgrade.log");
40635
41121
  }
40636
41122
  function appendUpgradeLog(message) {
40637
41123
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
@@ -40664,14 +41150,14 @@ Run 'adhdev doctor' for detailed diagnostics.`
40664
41150
  while (Date.now() - start < timeoutMs) {
40665
41151
  try {
40666
41152
  process.kill(pid, 0);
40667
- await new Promise((resolve9) => setTimeout(resolve9, 250));
41153
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
40668
41154
  } catch {
40669
41155
  return;
40670
41156
  }
40671
41157
  }
40672
41158
  }
40673
41159
  function stopSessionHostProcesses(appName) {
40674
- const pidFile = path13.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
41160
+ const pidFile = path15.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
40675
41161
  try {
40676
41162
  if (fs8.existsSync(pidFile)) {
40677
41163
  const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
@@ -40700,7 +41186,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
40700
41186
  }
40701
41187
  }
40702
41188
  function removeDaemonPidFile() {
40703
- const pidFile = path13.join(os15.homedir(), ".adhdev", "daemon.pid");
41189
+ const pidFile = path15.join(os15.homedir(), ".adhdev", "daemon.pid");
40704
41190
  try {
40705
41191
  fs8.unlinkSync(pidFile);
40706
41192
  } catch {
@@ -40711,7 +41197,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
40711
41197
  const npmRoot = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
40712
41198
  if (!npmRoot) return;
40713
41199
  const npmPrefix = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
40714
- const binDir = process.platform === "win32" ? npmPrefix : path13.join(npmPrefix, "bin");
41200
+ const binDir = process.platform === "win32" ? npmPrefix : path15.join(npmPrefix, "bin");
40715
41201
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
40716
41202
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
40717
41203
  if (pkgName === "@adhdev/daemon-standalone") {
@@ -40719,25 +41205,25 @@ Run 'adhdev doctor' for detailed diagnostics.`
40719
41205
  }
40720
41206
  if (pkgName.startsWith("@")) {
40721
41207
  const [scope, name] = pkgName.split("/");
40722
- const scopeDir = path13.join(npmRoot, scope);
41208
+ const scopeDir = path15.join(npmRoot, scope);
40723
41209
  if (!fs8.existsSync(scopeDir)) return;
40724
41210
  for (const entry of fs8.readdirSync(scopeDir)) {
40725
41211
  if (!entry.startsWith(`.${name}-`)) continue;
40726
- fs8.rmSync(path13.join(scopeDir, entry), { recursive: true, force: true });
40727
- appendUpgradeLog(`Removed stale scoped staging dir: ${path13.join(scopeDir, entry)}`);
41212
+ fs8.rmSync(path15.join(scopeDir, entry), { recursive: true, force: true });
41213
+ appendUpgradeLog(`Removed stale scoped staging dir: ${path15.join(scopeDir, entry)}`);
40728
41214
  }
40729
41215
  } else {
40730
41216
  for (const entry of fs8.readdirSync(npmRoot)) {
40731
41217
  if (!entry.startsWith(`.${pkgName}-`)) continue;
40732
- fs8.rmSync(path13.join(npmRoot, entry), { recursive: true, force: true });
40733
- appendUpgradeLog(`Removed stale staging dir: ${path13.join(npmRoot, entry)}`);
41218
+ fs8.rmSync(path15.join(npmRoot, entry), { recursive: true, force: true });
41219
+ appendUpgradeLog(`Removed stale staging dir: ${path15.join(npmRoot, entry)}`);
40734
41220
  }
40735
41221
  }
40736
41222
  if (fs8.existsSync(binDir)) {
40737
41223
  for (const entry of fs8.readdirSync(binDir)) {
40738
41224
  if (![...binNames].some((name) => entry.startsWith(`.${name}-`))) continue;
40739
- fs8.rmSync(path13.join(binDir, entry), { recursive: true, force: true });
40740
- appendUpgradeLog(`Removed stale bin staging entry: ${path13.join(binDir, entry)}`);
41225
+ fs8.rmSync(path15.join(binDir, entry), { recursive: true, force: true });
41226
+ appendUpgradeLog(`Removed stale bin staging entry: ${path15.join(binDir, entry)}`);
40741
41227
  }
40742
41228
  }
40743
41229
  }
@@ -40779,7 +41265,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
40779
41265
  appendUpgradeLog(installOutput.trim());
40780
41266
  }
40781
41267
  if (process.platform === "win32") {
40782
- await new Promise((resolve9) => setTimeout(resolve9, 500));
41268
+ await new Promise((resolve12) => setTimeout(resolve12, 500));
40783
41269
  cleanupStaleGlobalInstallDirs(payload.packageName);
40784
41270
  appendUpgradeLog("Post-install staging cleanup complete");
40785
41271
  }
@@ -40973,6 +41459,15 @@ Run 'adhdev doctor' for detailed diagnostics.`
40973
41459
  const record2 = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
40974
41460
  return { success: true, record: record2 };
40975
41461
  }
41462
+ case "session_host_prune_duplicate_sessions": {
41463
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
41464
+ const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
41465
+ providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
41466
+ workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
41467
+ dryRun: args?.dryRun === true
41468
+ });
41469
+ return { success: true, result };
41470
+ }
40976
41471
  case "session_host_acquire_write": {
40977
41472
  if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
40978
41473
  const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
@@ -41055,6 +41550,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
41055
41550
  if (!ideType) throw new Error("ideType required");
41056
41551
  const killProcess = args?.killProcess !== false;
41057
41552
  await this.stopIde(ideType, killProcess);
41553
+ try {
41554
+ const results = await detectIDEs(this.deps.providerLoader);
41555
+ this.deps.detectedIdes.value = results;
41556
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
41557
+ } catch {
41558
+ }
41058
41559
  return { success: true, ideType, stopped: true, processKilled: killProcess };
41059
41560
  }
41060
41561
  // ─── IDE restart ───
@@ -41097,6 +41598,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
41097
41598
  }
41098
41599
  }
41099
41600
  this.deps.onIdeConnected?.();
41601
+ try {
41602
+ const results = await detectIDEs(this.deps.providerLoader);
41603
+ this.deps.detectedIdes.value = results;
41604
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
41605
+ } catch {
41606
+ }
41100
41607
  if (result.success && resolvedWorkspace) {
41101
41608
  try {
41102
41609
  const next = appendRecentActivity(loadState(), {
@@ -41124,8 +41631,9 @@ Run 'adhdev doctor' for detailed diagnostics.`
41124
41631
  }
41125
41632
  // ─── Detect IDEs ───
41126
41633
  case "detect_ides": {
41127
- const results = await detectIDEs();
41634
+ const results = await detectIDEs(this.deps.providerLoader);
41128
41635
  this.deps.detectedIdes.value = results;
41636
+ this.deps.providerLoader.setIdeDetectionResults(results, true);
41129
41637
  return { success: true, detectedInfo: results };
41130
41638
  }
41131
41639
  // ─── Set User Name ───
@@ -41537,6 +42045,51 @@ Run 'adhdev doctor' for detailed diagnostics.`
41537
42045
  isTransportError(reason) {
41538
42046
  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);
41539
42047
  }
42048
+ titlesMatch(actual, expected) {
42049
+ const lhs = actual.trim().toLowerCase();
42050
+ const rhs = expected.trim().toLowerCase();
42051
+ if (!lhs || !rhs) return false;
42052
+ return lhs === rhs || lhs.includes(rhs) || rhs.includes(lhs);
42053
+ }
42054
+ messageCount(state) {
42055
+ return Array.isArray(state?.messages) ? state.messages.length : 0;
42056
+ }
42057
+ lastMessageSignature(state) {
42058
+ const messages = Array.isArray(state?.messages) ? state.messages : [];
42059
+ const last = messages[messages.length - 1];
42060
+ if (!last) return "";
42061
+ return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
42062
+ }
42063
+ async verifySendOutcome(evaluate, before) {
42064
+ const beforeCount = this.messageCount(before);
42065
+ const beforeSignature = this.lastMessageSignature(before);
42066
+ for (let attempt = 0; attempt < 12; attempt += 1) {
42067
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
42068
+ let state;
42069
+ try {
42070
+ state = await this.readChat(evaluate);
42071
+ } catch {
42072
+ continue;
42073
+ }
42074
+ if (state.status === "waiting_approval") {
42075
+ return true;
42076
+ }
42077
+ const afterCount = this.messageCount(state);
42078
+ const afterSignature = this.lastMessageSignature(state);
42079
+ if (afterCount > beforeCount) return true;
42080
+ if (afterSignature && afterSignature !== beforeSignature) return true;
42081
+ }
42082
+ return false;
42083
+ }
42084
+ async readStableBaselineState(evaluate) {
42085
+ const first = await this.readChat(evaluate);
42086
+ if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
42087
+ return first;
42088
+ }
42089
+ await new Promise((resolve12) => setTimeout(resolve12, 150));
42090
+ const second = await this.readChat(evaluate);
42091
+ return this.messageCount(second) >= this.messageCount(first) ? second : first;
42092
+ }
41540
42093
  async readChat(evaluate) {
41541
42094
  const script = this.callScript("readChat");
41542
42095
  if (!script) return this.errorState("readChat script not available");
@@ -41562,6 +42115,9 @@ Run 'adhdev doctor' for detailed diagnostics.`
41562
42115
  mode: data.mode,
41563
42116
  activeModal: data.activeModal
41564
42117
  };
42118
+ if (typeof data.title === "string" && data.title.trim()) {
42119
+ state.title = data.title.trim();
42120
+ }
41565
42121
  const controlValues = extractProviderControlValues(this.provider.controls, data);
41566
42122
  if (controlValues) state.controlValues = controlValues;
41567
42123
  const effects = normalizeProviderEffects(data);
@@ -41585,6 +42141,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
41585
42141
  }
41586
42142
  }
41587
42143
  async sendMessage(evaluate, text) {
42144
+ let beforeState = null;
42145
+ try {
42146
+ beforeState = await this.readStableBaselineState(evaluate);
42147
+ } catch {
42148
+ beforeState = null;
42149
+ }
41588
42150
  const params = { message: text };
41589
42151
  const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
41590
42152
  if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
@@ -41602,7 +42164,9 @@ Run 'adhdev doctor' for detailed diagnostics.`
41602
42164
  }
41603
42165
  if (parsed && typeof parsed === "object") {
41604
42166
  if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
41605
- return;
42167
+ const verified = await this.verifySendOutcome(evaluate, beforeState);
42168
+ if (verified) return;
42169
+ throw new Error(`[${this.agentName}] sendMessage was not observed in chat state`);
41606
42170
  }
41607
42171
  if (typeof parsed.error === "string" && parsed.error.trim()) {
41608
42172
  throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
@@ -41613,7 +42177,15 @@ Run 'adhdev doctor' for detailed diagnostics.`
41613
42177
  async resolveAction(evaluate, action, button) {
41614
42178
  const script = this.callScript("resolveAction", { action, button });
41615
42179
  if (!script) return false;
41616
- return await evaluate(script) === true;
42180
+ const result = await evaluate(script);
42181
+ const parsed = this.parseMaybeJson(result);
42182
+ if (parsed === true) return true;
42183
+ if (typeof parsed === "string") {
42184
+ const normalized = parsed.trim().toLowerCase();
42185
+ return normalized === "ok" || normalized === "success" || normalized === "true" || normalized === "resolved" || normalized === "approved" || normalized === "rejected";
42186
+ }
42187
+ if (!parsed || typeof parsed !== "object") return false;
42188
+ return parsed.resolved === true || parsed.success === true || parsed.ok === true || parsed.found === true;
41617
42189
  }
41618
42190
  async newSession(evaluate) {
41619
42191
  const script = this.callScript("newSession");
@@ -41631,7 +42203,10 @@ Run 'adhdev doctor' for detailed diagnostics.`
41631
42203
  const raw = await evaluate(script, 1e4);
41632
42204
  const data = typeof raw === "string" ? JSON.parse(raw) : raw;
41633
42205
  if (data?.error) return [];
41634
- return Array.isArray(data) ? data : [];
42206
+ if (Array.isArray(data)) return data;
42207
+ if (Array.isArray(data?.sessions)) return data.sessions;
42208
+ if (Array.isArray(data?.chats)) return data.chats;
42209
+ return [];
41635
42210
  } catch {
41636
42211
  return [];
41637
42212
  }
@@ -41639,7 +42214,24 @@ Run 'adhdev doctor' for detailed diagnostics.`
41639
42214
  async switchSession(evaluate, sessionId) {
41640
42215
  const script = this.callScript("switchSession", sessionId);
41641
42216
  if (!script) return false;
41642
- return await evaluate(script, 1e4) === true;
42217
+ const raw = await evaluate(script, 1e4);
42218
+ const data = this.parseMaybeJson(raw);
42219
+ if (data === true) return true;
42220
+ if (typeof data === "string") {
42221
+ const normalized = data.trim().toLowerCase();
42222
+ return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
42223
+ }
42224
+ if (data && typeof data === "object") {
42225
+ if (data.switched === true || data.success === true || data.ok === true) return true;
42226
+ if (typeof data.error === "string" && data.error.trim()) return false;
42227
+ }
42228
+ for (let attempt = 0; attempt < 6; attempt += 1) {
42229
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
42230
+ const state = await this.readChat(evaluate);
42231
+ const title = typeof state.title === "string" ? state.title : "";
42232
+ if (this.titlesMatch(title, sessionId)) return true;
42233
+ }
42234
+ return false;
41643
42235
  }
41644
42236
  async focusEditor(evaluate) {
41645
42237
  const script = this.callScript("focusEditor");
@@ -41843,7 +42435,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
41843
42435
  return false;
41844
42436
  }
41845
42437
  }
41846
- async resolveSessionAction(cdp, sessionId, action) {
42438
+ async resolveSessionAction(cdp, sessionId, action, button) {
41847
42439
  await this.ensureSessionPanelOpen(sessionId);
41848
42440
  const target = this.getSessionTarget(sessionId);
41849
42441
  if (!target?.parentSessionId) return false;
@@ -41853,7 +42445,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
41853
42445
  if (!agent) return false;
41854
42446
  try {
41855
42447
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
41856
- return await agent.adapter.resolveAction(evaluate, action);
42448
+ return await agent.adapter.resolveAction(evaluate, action, button);
41857
42449
  } catch (e) {
41858
42450
  this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
41859
42451
  return false;
@@ -42319,11 +42911,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
42319
42911
  }
42320
42912
  };
42321
42913
  var fs10 = __toESM2(require("fs"));
42322
- var path14 = __toESM2(require("path"));
42914
+ var path16 = __toESM2(require("path"));
42323
42915
  var os16 = __toESM2(require("os"));
42324
42916
  var import_child_process9 = require("child_process");
42325
42917
  var import_os3 = require("os");
42326
- var ARCHIVE_PATH = path14.join(os16.homedir(), ".adhdev", "version-history.json");
42918
+ var ARCHIVE_PATH = path16.join(os16.homedir(), ".adhdev", "version-history.json");
42327
42919
  var MAX_ENTRIES_PER_PROVIDER = 20;
42328
42920
  var VersionArchive = class {
42329
42921
  history = {};
@@ -42370,7 +42962,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42370
42962
  }
42371
42963
  save() {
42372
42964
  try {
42373
- fs10.mkdirSync(path14.dirname(ARCHIVE_PATH), { recursive: true });
42965
+ fs10.mkdirSync(path16.dirname(ARCHIVE_PATH), { recursive: true });
42374
42966
  fs10.writeFileSync(ARCHIVE_PATH, JSON.stringify(this.history, null, 2));
42375
42967
  } catch {
42376
42968
  }
@@ -42411,7 +43003,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42411
43003
  for (const p of paths) {
42412
43004
  if (p.includes("*")) {
42413
43005
  const home = os16.homedir();
42414
- const resolved = p.replace(/\*/g, home.split(path14.sep).pop() || "");
43006
+ const resolved = p.replace(/\*/g, home.split(path16.sep).pop() || "");
42415
43007
  if (fs10.existsSync(resolved)) return resolved;
42416
43008
  } else {
42417
43009
  if (fs10.existsSync(p)) return p;
@@ -42421,7 +43013,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42421
43013
  }
42422
43014
  function getMacAppVersion(appPath) {
42423
43015
  if ((0, import_os3.platform)() !== "darwin" || !appPath.endsWith(".app")) return null;
42424
- const plistPath = path14.join(appPath, "Contents", "Info.plist");
43016
+ const plistPath = path16.join(appPath, "Contents", "Info.plist");
42425
43017
  if (!fs10.existsSync(plistPath)) return null;
42426
43018
  const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
42427
43019
  return raw || null;
@@ -42448,7 +43040,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42448
43040
  const cliBin = provider.cli ? findBinary2(provider.cli) : null;
42449
43041
  let resolvedBin = cliBin;
42450
43042
  if (!resolvedBin && appPath && currentOs === "darwin") {
42451
- const bundled = path14.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
43043
+ const bundled = path16.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
42452
43044
  if (provider.cli && fs10.existsSync(bundled)) resolvedBin = bundled;
42453
43045
  }
42454
43046
  info.installed = !!(appPath || resolvedBin);
@@ -42487,7 +43079,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
42487
43079
  }
42488
43080
  var http2 = __toESM2(require("http"));
42489
43081
  var fs14 = __toESM2(require("fs"));
42490
- var path18 = __toESM2(require("path"));
43082
+ var path20 = __toESM2(require("path"));
42491
43083
  function generateFiles(type, name, category, opts = {}) {
42492
43084
  const { cdpPorts, cli, processName, installPath, binary, extensionId, version: version2 = "0.1" } = opts;
42493
43085
  if (category === "cli" || category === "acp") {
@@ -42817,7 +43409,7 @@ async (params) => {
42817
43409
  }
42818
43410
  init_logger();
42819
43411
  var fs11 = __toESM2(require("fs"));
42820
- var path15 = __toESM2(require("path"));
43412
+ var path17 = __toESM2(require("path"));
42821
43413
  init_logger();
42822
43414
  async function handleCdpEvaluate(ctx, req, res) {
42823
43415
  const body = await ctx.readBody(req);
@@ -42996,17 +43588,17 @@ async (params) => {
42996
43588
  return;
42997
43589
  }
42998
43590
  let scriptsPath = "";
42999
- const directScripts = path15.join(dir, "scripts.js");
43591
+ const directScripts = path17.join(dir, "scripts.js");
43000
43592
  if (fs11.existsSync(directScripts)) {
43001
43593
  scriptsPath = directScripts;
43002
43594
  } else {
43003
- const scriptsDir = path15.join(dir, "scripts");
43595
+ const scriptsDir = path17.join(dir, "scripts");
43004
43596
  if (fs11.existsSync(scriptsDir)) {
43005
43597
  const versions = fs11.readdirSync(scriptsDir).filter((d) => {
43006
- return fs11.statSync(path15.join(scriptsDir, d)).isDirectory();
43598
+ return fs11.statSync(path17.join(scriptsDir, d)).isDirectory();
43007
43599
  }).sort().reverse();
43008
43600
  for (const ver of versions) {
43009
- const p = path15.join(scriptsDir, ver, "scripts.js");
43601
+ const p = path17.join(scriptsDir, ver, "scripts.js");
43010
43602
  if (fs11.existsSync(p)) {
43011
43603
  scriptsPath = p;
43012
43604
  break;
@@ -43823,7 +44415,7 @@ async (params) => {
43823
44415
  }
43824
44416
  }
43825
44417
  var fs12 = __toESM2(require("fs"));
43826
- var path16 = __toESM2(require("path"));
44418
+ var path18 = __toESM2(require("path"));
43827
44419
  function slugifyFixtureName(value) {
43828
44420
  const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
43829
44421
  return normalized || `fixture-${Date.now()}`;
@@ -43833,11 +44425,11 @@ async (params) => {
43833
44425
  if (!providerDir) {
43834
44426
  throw new Error(`Provider directory not found for '${type}'`);
43835
44427
  }
43836
- return path16.join(providerDir, "fixtures");
44428
+ return path18.join(providerDir, "fixtures");
43837
44429
  }
43838
44430
  function readCliFixture(ctx, type, name) {
43839
44431
  const fixtureDir = getCliFixtureDir(ctx, type);
43840
- const filePath = path16.join(fixtureDir, `${name}.json`);
44432
+ const filePath = path18.join(fixtureDir, `${name}.json`);
43841
44433
  if (!fs12.existsSync(filePath)) {
43842
44434
  throw new Error(`Fixture not found: ${filePath}`);
43843
44435
  }
@@ -43997,7 +44589,7 @@ async (params) => {
43997
44589
  return { target, instance, adapter };
43998
44590
  }
43999
44591
  function sleep(ms2) {
44000
- return new Promise((resolve9) => setTimeout(resolve9, ms2));
44592
+ return new Promise((resolve12) => setTimeout(resolve12, ms2));
44001
44593
  }
44002
44594
  async function waitForCliReady(ctx, type, instanceId, timeoutMs) {
44003
44595
  const startedAt = Date.now();
@@ -44596,7 +45188,7 @@ async (params) => {
44596
45188
  },
44597
45189
  notes: typeof body?.notes === "string" ? body.notes : void 0
44598
45190
  };
44599
- const filePath = path16.join(fixtureDir, `${name}.json`);
45191
+ const filePath = path18.join(fixtureDir, `${name}.json`);
44600
45192
  fs12.writeFileSync(filePath, JSON.stringify(fixture, null, 2));
44601
45193
  ctx.json(res, 200, {
44602
45194
  saved: true,
@@ -44620,7 +45212,7 @@ async (params) => {
44620
45212
  return;
44621
45213
  }
44622
45214
  const fixtures = fs12.readdirSync(fixtureDir).filter((file2) => file2.endsWith(".json")).sort((a, b2) => b2.localeCompare(a, void 0, { numeric: true, sensitivity: "base" })).map((file2) => {
44623
- const fullPath = path16.join(fixtureDir, file2);
45215
+ const fullPath = path18.join(fixtureDir, file2);
44624
45216
  try {
44625
45217
  const raw = JSON.parse(fs12.readFileSync(fullPath, "utf-8"));
44626
45218
  return {
@@ -44754,7 +45346,7 @@ async (params) => {
44754
45346
  }
44755
45347
  }
44756
45348
  var fs13 = __toESM2(require("fs"));
44757
- var path17 = __toESM2(require("path"));
45349
+ var path19 = __toESM2(require("path"));
44758
45350
  var os17 = __toESM2(require("os"));
44759
45351
  function getAutoImplPid(ctx) {
44760
45352
  const proc = ctx.autoImplProcess;
@@ -44794,22 +45386,22 @@ async (params) => {
44794
45386
  if (!fs13.existsSync(scriptsDir)) return null;
44795
45387
  const versions = fs13.readdirSync(scriptsDir).filter((d) => {
44796
45388
  try {
44797
- return fs13.statSync(path17.join(scriptsDir, d)).isDirectory();
45389
+ return fs13.statSync(path19.join(scriptsDir, d)).isDirectory();
44798
45390
  } catch {
44799
45391
  return false;
44800
45392
  }
44801
45393
  }).sort((a, b2) => b2.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
44802
45394
  if (versions.length === 0) return null;
44803
- return path17.join(scriptsDir, versions[0]);
45395
+ return path19.join(scriptsDir, versions[0]);
44804
45396
  }
44805
45397
  function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
44806
- const canonicalUserDir = path17.resolve(ctx.providerLoader.getUserProviderDir(category, type));
44807
- const desiredDir = requestedDir ? path17.resolve(requestedDir) : canonicalUserDir;
44808
- const upstreamRoot = path17.resolve(ctx.providerLoader.getUpstreamDir());
44809
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path17.sep}`)) {
45398
+ const canonicalUserDir = path19.resolve(ctx.providerLoader.getUserProviderDir(category, type));
45399
+ const desiredDir = requestedDir ? path19.resolve(requestedDir) : canonicalUserDir;
45400
+ const upstreamRoot = path19.resolve(ctx.providerLoader.getUpstreamDir());
45401
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path19.sep}`)) {
44810
45402
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
44811
45403
  }
44812
- if (path17.basename(desiredDir) !== type) {
45404
+ if (path19.basename(desiredDir) !== type) {
44813
45405
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
44814
45406
  }
44815
45407
  const sourceDir = ctx.findProviderDir(type);
@@ -44817,11 +45409,11 @@ async (params) => {
44817
45409
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
44818
45410
  }
44819
45411
  if (!fs13.existsSync(desiredDir)) {
44820
- fs13.mkdirSync(path17.dirname(desiredDir), { recursive: true });
45412
+ fs13.mkdirSync(path19.dirname(desiredDir), { recursive: true });
44821
45413
  fs13.cpSync(sourceDir, desiredDir, { recursive: true });
44822
45414
  ctx.log(`Auto-implement writable copy created: ${desiredDir}`);
44823
45415
  }
44824
- const providerJson = path17.join(desiredDir, "provider.json");
45416
+ const providerJson = path19.join(desiredDir, "provider.json");
44825
45417
  if (!fs13.existsSync(providerJson)) {
44826
45418
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
44827
45419
  }
@@ -44844,13 +45436,13 @@ async (params) => {
44844
45436
  const refDir = ctx.findProviderDir(referenceType);
44845
45437
  if (!refDir || !fs13.existsSync(refDir)) return {};
44846
45438
  const referenceScripts = {};
44847
- const scriptsDir = path17.join(refDir, "scripts");
45439
+ const scriptsDir = path19.join(refDir, "scripts");
44848
45440
  const latestDir = getLatestScriptVersionDir(scriptsDir);
44849
45441
  if (!latestDir) return referenceScripts;
44850
45442
  for (const file2 of fs13.readdirSync(latestDir)) {
44851
45443
  if (!file2.endsWith(".js")) continue;
44852
45444
  try {
44853
- referenceScripts[file2] = fs13.readFileSync(path17.join(latestDir, file2), "utf-8");
45445
+ referenceScripts[file2] = fs13.readFileSync(path19.join(latestDir, file2), "utf-8");
44854
45446
  } catch {
44855
45447
  }
44856
45448
  }
@@ -44958,9 +45550,9 @@ async (params) => {
44958
45550
  });
44959
45551
  const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
44960
45552
  const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
44961
- const tmpDir = path17.join(os17.tmpdir(), "adhdev-autoimpl");
45553
+ const tmpDir = path19.join(os17.tmpdir(), "adhdev-autoimpl");
44962
45554
  if (!fs13.existsSync(tmpDir)) fs13.mkdirSync(tmpDir, { recursive: true });
44963
- const promptFile = path17.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
45555
+ const promptFile = path19.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
44964
45556
  fs13.writeFileSync(promptFile, prompt, "utf-8");
44965
45557
  ctx.log(`Auto-implement prompt written to ${promptFile} (${prompt.length} chars)`);
44966
45558
  const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
@@ -45387,7 +45979,7 @@ async (params) => {
45387
45979
  setMode: "set_mode.js"
45388
45980
  };
45389
45981
  const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
45390
- const scriptsDir = path17.join(providerDir, "scripts");
45982
+ const scriptsDir = path19.join(providerDir, "scripts");
45391
45983
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
45392
45984
  if (latestScriptsDir) {
45393
45985
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -45398,7 +45990,7 @@ async (params) => {
45398
45990
  for (const file2 of fs13.readdirSync(latestScriptsDir)) {
45399
45991
  if (file2.endsWith(".js") && targetFileNames.has(file2)) {
45400
45992
  try {
45401
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file2), "utf-8");
45993
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file2), "utf-8");
45402
45994
  lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
45403
45995
  lines.push("```javascript");
45404
45996
  lines.push(content);
@@ -45415,7 +46007,7 @@ async (params) => {
45415
46007
  lines.push("");
45416
46008
  for (const file2 of refFiles) {
45417
46009
  try {
45418
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file2), "utf-8");
46010
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file2), "utf-8");
45419
46011
  lines.push(`### \`${file2}\` \u{1F512}`);
45420
46012
  lines.push("```javascript");
45421
46013
  lines.push(content);
@@ -45456,10 +46048,10 @@ async (params) => {
45456
46048
  lines.push("");
45457
46049
  }
45458
46050
  }
45459
- const docsDir = path17.join(providerDir, "../../docs");
46051
+ const docsDir = path19.join(providerDir, "../../docs");
45460
46052
  const loadGuide = (name) => {
45461
46053
  try {
45462
- const p = path17.join(docsDir, name);
46054
+ const p = path19.join(docsDir, name);
45463
46055
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
45464
46056
  } catch {
45465
46057
  }
@@ -45694,7 +46286,7 @@ async (params) => {
45694
46286
  parseApproval: "parse_approval.js"
45695
46287
  };
45696
46288
  const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
45697
- const scriptsDir = path17.join(providerDir, "scripts");
46289
+ const scriptsDir = path19.join(providerDir, "scripts");
45698
46290
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
45699
46291
  if (latestScriptsDir) {
45700
46292
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -45706,7 +46298,7 @@ async (params) => {
45706
46298
  if (!file2.endsWith(".js")) continue;
45707
46299
  if (!targetFileNames.has(file2)) continue;
45708
46300
  try {
45709
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file2), "utf-8");
46301
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file2), "utf-8");
45710
46302
  lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
45711
46303
  lines.push("```javascript");
45712
46304
  lines.push(content);
@@ -45722,7 +46314,7 @@ async (params) => {
45722
46314
  lines.push("");
45723
46315
  for (const file2 of refFiles) {
45724
46316
  try {
45725
- const content = fs13.readFileSync(path17.join(latestScriptsDir, file2), "utf-8");
46317
+ const content = fs13.readFileSync(path19.join(latestScriptsDir, file2), "utf-8");
45726
46318
  lines.push(`### \`${file2}\` \u{1F512}`);
45727
46319
  lines.push("```javascript");
45728
46320
  lines.push(content);
@@ -45755,10 +46347,10 @@ async (params) => {
45755
46347
  lines.push("");
45756
46348
  }
45757
46349
  }
45758
- const docsDir = path17.join(providerDir, "../../docs");
46350
+ const docsDir = path19.join(providerDir, "../../docs");
45759
46351
  const loadGuide = (name) => {
45760
46352
  try {
45761
- const p = path17.join(docsDir, name);
46353
+ const p = path19.join(docsDir, name);
45762
46354
  if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
45763
46355
  } catch {
45764
46356
  }
@@ -46167,8 +46759,8 @@ data: ${JSON.stringify(msg.data)}
46167
46759
  }
46168
46760
  getEndpointList() {
46169
46761
  return this.routes.map((r) => {
46170
- const path19 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
46171
- return `${r.method.padEnd(5)} ${path19}`;
46762
+ const path21 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
46763
+ return `${r.method.padEnd(5)} ${path21}`;
46172
46764
  });
46173
46765
  }
46174
46766
  async start(port = DEV_SERVER_PORT) {
@@ -46199,15 +46791,15 @@ data: ${JSON.stringify(msg.data)}
46199
46791
  this.json(res, 500, { error: e.message });
46200
46792
  }
46201
46793
  });
46202
- return new Promise((resolve9, reject) => {
46794
+ return new Promise((resolve12, reject) => {
46203
46795
  this.server.listen(port, "127.0.0.1", () => {
46204
46796
  this.log(`Dev server listening on http://127.0.0.1:${port}`);
46205
- resolve9();
46797
+ resolve12();
46206
46798
  });
46207
46799
  this.server.on("error", (e) => {
46208
46800
  if (e.code === "EADDRINUSE") {
46209
46801
  this.log(`Port ${port} in use, skipping dev server`);
46210
- resolve9();
46802
+ resolve12();
46211
46803
  } else {
46212
46804
  reject(e);
46213
46805
  }
@@ -46290,20 +46882,20 @@ data: ${JSON.stringify(msg.data)}
46290
46882
  child.stderr?.on("data", (d) => {
46291
46883
  stderr += d.toString().slice(0, 2e3);
46292
46884
  });
46293
- await new Promise((resolve9) => {
46885
+ await new Promise((resolve12) => {
46294
46886
  const timer = setTimeout(() => {
46295
46887
  child.kill();
46296
- resolve9();
46888
+ resolve12();
46297
46889
  }, 3e3);
46298
46890
  child.on("exit", () => {
46299
46891
  clearTimeout(timer);
46300
- resolve9();
46892
+ resolve12();
46301
46893
  });
46302
46894
  child.stdout?.once("data", () => {
46303
46895
  setTimeout(() => {
46304
46896
  child.kill();
46305
46897
  clearTimeout(timer);
46306
- resolve9();
46898
+ resolve12();
46307
46899
  }, 500);
46308
46900
  });
46309
46901
  });
@@ -46450,12 +47042,12 @@ data: ${JSON.stringify(msg.data)}
46450
47042
  // ─── DevConsole SPA ───
46451
47043
  getConsoleDistDir() {
46452
47044
  const candidates = [
46453
- path18.resolve(__dirname, "../../web-devconsole/dist"),
46454
- path18.resolve(__dirname, "../../../web-devconsole/dist"),
46455
- path18.join(process.cwd(), "packages/web-devconsole/dist")
47045
+ path20.resolve(__dirname, "../../web-devconsole/dist"),
47046
+ path20.resolve(__dirname, "../../../web-devconsole/dist"),
47047
+ path20.join(process.cwd(), "packages/web-devconsole/dist")
46456
47048
  ];
46457
47049
  for (const dir of candidates) {
46458
- if (fs14.existsSync(path18.join(dir, "index.html"))) return dir;
47050
+ if (fs14.existsSync(path20.join(dir, "index.html"))) return dir;
46459
47051
  }
46460
47052
  return null;
46461
47053
  }
@@ -46465,7 +47057,7 @@ data: ${JSON.stringify(msg.data)}
46465
47057
  this.json(res, 500, { error: "DevConsole not found. Run: npm run build -w packages/web-devconsole" });
46466
47058
  return;
46467
47059
  }
46468
- const htmlPath = path18.join(distDir, "index.html");
47060
+ const htmlPath = path20.join(distDir, "index.html");
46469
47061
  try {
46470
47062
  const html = fs14.readFileSync(htmlPath, "utf-8");
46471
47063
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
@@ -46490,15 +47082,15 @@ data: ${JSON.stringify(msg.data)}
46490
47082
  this.json(res, 404, { error: "Not found" });
46491
47083
  return;
46492
47084
  }
46493
- const safePath = path18.normalize(pathname).replace(/^\.\.\//, "");
46494
- const filePath = path18.join(distDir, safePath);
47085
+ const safePath = path20.normalize(pathname).replace(/^\.\.\//, "");
47086
+ const filePath = path20.join(distDir, safePath);
46495
47087
  if (!filePath.startsWith(distDir)) {
46496
47088
  this.json(res, 403, { error: "Forbidden" });
46497
47089
  return;
46498
47090
  }
46499
47091
  try {
46500
47092
  const content = fs14.readFileSync(filePath);
46501
- const ext = path18.extname(filePath);
47093
+ const ext = path20.extname(filePath);
46502
47094
  const contentType = _DevServer.MIME_MAP[ext] || "application/octet-stream";
46503
47095
  res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=31536000, immutable" });
46504
47096
  res.end(content);
@@ -46611,9 +47203,9 @@ data: ${JSON.stringify(msg.data)}
46611
47203
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
46612
47204
  if (entry.isDirectory()) {
46613
47205
  files.push({ path: rel, size: 0, type: "dir" });
46614
- scan(path18.join(d, entry.name), rel);
47206
+ scan(path20.join(d, entry.name), rel);
46615
47207
  } else {
46616
- const stat4 = fs14.statSync(path18.join(d, entry.name));
47208
+ const stat4 = fs14.statSync(path20.join(d, entry.name));
46617
47209
  files.push({ path: rel, size: stat4.size, type: "file" });
46618
47210
  }
46619
47211
  }
@@ -46636,7 +47228,7 @@ data: ${JSON.stringify(msg.data)}
46636
47228
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
46637
47229
  return;
46638
47230
  }
46639
- const fullPath = path18.resolve(dir, path18.normalize(filePath));
47231
+ const fullPath = path20.resolve(dir, path20.normalize(filePath));
46640
47232
  if (!fullPath.startsWith(dir)) {
46641
47233
  this.json(res, 403, { error: "Forbidden" });
46642
47234
  return;
@@ -46661,14 +47253,14 @@ data: ${JSON.stringify(msg.data)}
46661
47253
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
46662
47254
  return;
46663
47255
  }
46664
- const fullPath = path18.resolve(dir, path18.normalize(filePath));
47256
+ const fullPath = path20.resolve(dir, path20.normalize(filePath));
46665
47257
  if (!fullPath.startsWith(dir)) {
46666
47258
  this.json(res, 403, { error: "Forbidden" });
46667
47259
  return;
46668
47260
  }
46669
47261
  try {
46670
47262
  if (fs14.existsSync(fullPath)) fs14.copyFileSync(fullPath, fullPath + ".bak");
46671
- fs14.mkdirSync(path18.dirname(fullPath), { recursive: true });
47263
+ fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
46672
47264
  fs14.writeFileSync(fullPath, content, "utf-8");
46673
47265
  this.log(`File saved: ${fullPath} (${content.length} chars)`);
46674
47266
  this.providerLoader.reload();
@@ -46685,7 +47277,7 @@ data: ${JSON.stringify(msg.data)}
46685
47277
  return;
46686
47278
  }
46687
47279
  for (const name of ["scripts.js", "provider.json"]) {
46688
- const p = path18.join(dir, name);
47280
+ const p = path20.join(dir, name);
46689
47281
  if (fs14.existsSync(p)) {
46690
47282
  const source = fs14.readFileSync(p, "utf-8");
46691
47283
  this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
@@ -46706,8 +47298,8 @@ data: ${JSON.stringify(msg.data)}
46706
47298
  this.json(res, 404, { error: `Provider not found: ${type}` });
46707
47299
  return;
46708
47300
  }
46709
- const target = fs14.existsSync(path18.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
46710
- const targetPath = path18.join(dir, target);
47301
+ const target = fs14.existsSync(path20.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
47302
+ const targetPath = path20.join(dir, target);
46711
47303
  try {
46712
47304
  if (fs14.existsSync(targetPath)) fs14.copyFileSync(targetPath, targetPath + ".bak");
46713
47305
  fs14.writeFileSync(targetPath, source, "utf-8");
@@ -46812,14 +47404,14 @@ data: ${JSON.stringify(msg.data)}
46812
47404
  child.stderr?.on("data", (d) => {
46813
47405
  stderr += d.toString();
46814
47406
  });
46815
- await new Promise((resolve9) => {
47407
+ await new Promise((resolve12) => {
46816
47408
  const timer = setTimeout(() => {
46817
47409
  child.kill();
46818
- resolve9();
47410
+ resolve12();
46819
47411
  }, timeout);
46820
47412
  child.on("exit", () => {
46821
47413
  clearTimeout(timer);
46822
- resolve9();
47414
+ resolve12();
46823
47415
  });
46824
47416
  });
46825
47417
  const elapsed = Date.now() - start;
@@ -46867,7 +47459,7 @@ data: ${JSON.stringify(msg.data)}
46867
47459
  }
46868
47460
  let targetDir;
46869
47461
  targetDir = this.providerLoader.getUserProviderDir(category, type);
46870
- const jsonPath = path18.join(targetDir, "provider.json");
47462
+ const jsonPath = path20.join(targetDir, "provider.json");
46871
47463
  if (fs14.existsSync(jsonPath)) {
46872
47464
  this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
46873
47465
  return;
@@ -46879,8 +47471,8 @@ data: ${JSON.stringify(msg.data)}
46879
47471
  const createdFiles = ["provider.json"];
46880
47472
  if (result.files) {
46881
47473
  for (const [relPath, content] of Object.entries(result.files)) {
46882
- const fullPath = path18.join(targetDir, relPath);
46883
- fs14.mkdirSync(path18.dirname(fullPath), { recursive: true });
47474
+ const fullPath = path20.join(targetDir, relPath);
47475
+ fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
46884
47476
  fs14.writeFileSync(fullPath, content, "utf-8");
46885
47477
  createdFiles.push(relPath);
46886
47478
  }
@@ -46933,22 +47525,22 @@ data: ${JSON.stringify(msg.data)}
46933
47525
  if (!fs14.existsSync(scriptsDir)) return null;
46934
47526
  const versions = fs14.readdirSync(scriptsDir).filter((d) => {
46935
47527
  try {
46936
- return fs14.statSync(path18.join(scriptsDir, d)).isDirectory();
47528
+ return fs14.statSync(path20.join(scriptsDir, d)).isDirectory();
46937
47529
  } catch {
46938
47530
  return false;
46939
47531
  }
46940
47532
  }).sort((a, b2) => b2.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
46941
47533
  if (versions.length === 0) return null;
46942
- return path18.join(scriptsDir, versions[0]);
47534
+ return path20.join(scriptsDir, versions[0]);
46943
47535
  }
46944
47536
  resolveAutoImplWritableProviderDir(category, type, requestedDir) {
46945
- const canonicalUserDir = path18.resolve(this.providerLoader.getUserProviderDir(category, type));
46946
- const desiredDir = requestedDir ? path18.resolve(requestedDir) : canonicalUserDir;
46947
- const upstreamRoot = path18.resolve(this.providerLoader.getUpstreamDir());
46948
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path18.sep}`)) {
47537
+ const canonicalUserDir = path20.resolve(this.providerLoader.getUserProviderDir(category, type));
47538
+ const desiredDir = requestedDir ? path20.resolve(requestedDir) : canonicalUserDir;
47539
+ const upstreamRoot = path20.resolve(this.providerLoader.getUpstreamDir());
47540
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path20.sep}`)) {
46949
47541
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
46950
47542
  }
46951
- if (path18.basename(desiredDir) !== type) {
47543
+ if (path20.basename(desiredDir) !== type) {
46952
47544
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
46953
47545
  }
46954
47546
  const sourceDir = this.findProviderDir(type);
@@ -46956,11 +47548,11 @@ data: ${JSON.stringify(msg.data)}
46956
47548
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
46957
47549
  }
46958
47550
  if (!fs14.existsSync(desiredDir)) {
46959
- fs14.mkdirSync(path18.dirname(desiredDir), { recursive: true });
47551
+ fs14.mkdirSync(path20.dirname(desiredDir), { recursive: true });
46960
47552
  fs14.cpSync(sourceDir, desiredDir, { recursive: true });
46961
47553
  this.log(`Auto-implement writable copy created: ${desiredDir}`);
46962
47554
  }
46963
- const providerJson = path18.join(desiredDir, "provider.json");
47555
+ const providerJson = path20.join(desiredDir, "provider.json");
46964
47556
  if (!fs14.existsSync(providerJson)) {
46965
47557
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
46966
47558
  }
@@ -47008,7 +47600,7 @@ data: ${JSON.stringify(msg.data)}
47008
47600
  setMode: "set_mode.js"
47009
47601
  };
47010
47602
  const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
47011
- const scriptsDir = path18.join(providerDir, "scripts");
47603
+ const scriptsDir = path20.join(providerDir, "scripts");
47012
47604
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
47013
47605
  if (latestScriptsDir) {
47014
47606
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -47019,7 +47611,7 @@ data: ${JSON.stringify(msg.data)}
47019
47611
  for (const file2 of fs14.readdirSync(latestScriptsDir)) {
47020
47612
  if (file2.endsWith(".js") && targetFileNames.has(file2)) {
47021
47613
  try {
47022
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file2), "utf-8");
47614
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file2), "utf-8");
47023
47615
  lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
47024
47616
  lines.push("```javascript");
47025
47617
  lines.push(content);
@@ -47036,7 +47628,7 @@ data: ${JSON.stringify(msg.data)}
47036
47628
  lines.push("");
47037
47629
  for (const file2 of refFiles) {
47038
47630
  try {
47039
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file2), "utf-8");
47631
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file2), "utf-8");
47040
47632
  lines.push(`### \`${file2}\` \u{1F512}`);
47041
47633
  lines.push("```javascript");
47042
47634
  lines.push(content);
@@ -47077,10 +47669,10 @@ data: ${JSON.stringify(msg.data)}
47077
47669
  lines.push("");
47078
47670
  }
47079
47671
  }
47080
- const docsDir = path18.join(providerDir, "../../docs");
47672
+ const docsDir = path20.join(providerDir, "../../docs");
47081
47673
  const loadGuide = (name) => {
47082
47674
  try {
47083
- const p = path18.join(docsDir, name);
47675
+ const p = path20.join(docsDir, name);
47084
47676
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
47085
47677
  } catch {
47086
47678
  }
@@ -47254,7 +47846,7 @@ data: ${JSON.stringify(msg.data)}
47254
47846
  parseApproval: "parse_approval.js"
47255
47847
  };
47256
47848
  const targetFileNames = new Set(functions.map((fn2) => funcToFile[fn2]).filter(Boolean));
47257
- const scriptsDir = path18.join(providerDir, "scripts");
47849
+ const scriptsDir = path20.join(providerDir, "scripts");
47258
47850
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
47259
47851
  if (latestScriptsDir) {
47260
47852
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -47266,7 +47858,7 @@ data: ${JSON.stringify(msg.data)}
47266
47858
  if (!file2.endsWith(".js")) continue;
47267
47859
  if (!targetFileNames.has(file2)) continue;
47268
47860
  try {
47269
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file2), "utf-8");
47861
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file2), "utf-8");
47270
47862
  lines.push(`### \`${file2}\` \u270F\uFE0F EDIT`);
47271
47863
  lines.push("```javascript");
47272
47864
  lines.push(content);
@@ -47282,7 +47874,7 @@ data: ${JSON.stringify(msg.data)}
47282
47874
  lines.push("");
47283
47875
  for (const file2 of refFiles) {
47284
47876
  try {
47285
- const content = fs14.readFileSync(path18.join(latestScriptsDir, file2), "utf-8");
47877
+ const content = fs14.readFileSync(path20.join(latestScriptsDir, file2), "utf-8");
47286
47878
  lines.push(`### \`${file2}\` \u{1F512}`);
47287
47879
  lines.push("```javascript");
47288
47880
  lines.push(content);
@@ -47315,10 +47907,10 @@ data: ${JSON.stringify(msg.data)}
47315
47907
  lines.push("");
47316
47908
  }
47317
47909
  }
47318
- const docsDir = path18.join(providerDir, "../../docs");
47910
+ const docsDir = path20.join(providerDir, "../../docs");
47319
47911
  const loadGuide = (name) => {
47320
47912
  try {
47321
- const p = path18.join(docsDir, name);
47913
+ const p = path20.join(docsDir, name);
47322
47914
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
47323
47915
  } catch {
47324
47916
  }
@@ -47494,14 +48086,14 @@ data: ${JSON.stringify(msg.data)}
47494
48086
  res.end(JSON.stringify(data, null, 2));
47495
48087
  }
47496
48088
  async readBody(req) {
47497
- return new Promise((resolve9) => {
48089
+ return new Promise((resolve12) => {
47498
48090
  let body = "";
47499
48091
  req.on("data", (chunk) => body += chunk);
47500
48092
  req.on("end", () => {
47501
48093
  try {
47502
- resolve9(JSON.parse(body));
48094
+ resolve12(JSON.parse(body));
47503
48095
  } catch {
47504
- resolve9({});
48096
+ resolve12({});
47505
48097
  }
47506
48098
  });
47507
48099
  });
@@ -47963,7 +48555,7 @@ data: ${JSON.stringify(msg.data)}
47963
48555
  const deadline = Date.now() + timeoutMs;
47964
48556
  while (Date.now() < deadline) {
47965
48557
  if (await canConnect(endpoint)) return;
47966
- await new Promise((resolve9) => setTimeout(resolve9, STARTUP_POLL_MS));
48558
+ await new Promise((resolve12) => setTimeout(resolve12, STARTUP_POLL_MS));
47967
48559
  }
47968
48560
  throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
47969
48561
  }
@@ -48117,10 +48709,10 @@ data: ${JSON.stringify(msg.data)}
48117
48709
  const buffer = Buffer.from(await res.arrayBuffer());
48118
48710
  const fs15 = await import("fs");
48119
48711
  fs15.writeFileSync(vsixPath, buffer);
48120
- return new Promise((resolve9) => {
48712
+ return new Promise((resolve12) => {
48121
48713
  const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
48122
48714
  (0, import_child_process10.exec)(cmd, { timeout: 6e4 }, (error48, _stdout, stderr) => {
48123
- resolve9({
48715
+ resolve12({
48124
48716
  extensionId: extension.id,
48125
48717
  marketplaceId: extension.marketplaceId,
48126
48718
  success: !error48,
@@ -48133,11 +48725,11 @@ data: ${JSON.stringify(msg.data)}
48133
48725
  } catch (e) {
48134
48726
  }
48135
48727
  }
48136
- return new Promise((resolve9) => {
48728
+ return new Promise((resolve12) => {
48137
48729
  const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
48138
48730
  (0, import_child_process10.exec)(cmd, { timeout: 6e4 }, (error48, stdout, stderr) => {
48139
48731
  if (error48) {
48140
- resolve9({
48732
+ resolve12({
48141
48733
  extensionId: extension.id,
48142
48734
  marketplaceId: extension.marketplaceId,
48143
48735
  success: false,
@@ -48145,7 +48737,7 @@ data: ${JSON.stringify(msg.data)}
48145
48737
  error: stderr || error48.message
48146
48738
  });
48147
48739
  } else {
48148
- resolve9({
48740
+ resolve12({
48149
48741
  extensionId: extension.id,
48150
48742
  marketplaceId: extension.marketplaceId,
48151
48743
  success: true,
@@ -48277,13 +48869,32 @@ data: ${JSON.stringify(msg.data)}
48277
48869
  const detectedIdesRef = { value: [] };
48278
48870
  let agentStreamManager = null;
48279
48871
  let poller = null;
48872
+ const refreshProviderAvailability = async (providerType) => {
48873
+ const targetProvider = providerType ? providerLoader.getMeta(providerLoader.resolveAlias(providerType)) : null;
48874
+ const targetCategory = targetProvider?.category;
48875
+ if (!providerType || targetCategory === "cli" || targetCategory === "acp") {
48876
+ if (providerType && targetProvider) {
48877
+ const detected = await detectCLI(targetProvider.type, providerLoader, { includeVersion: false });
48878
+ providerLoader.setProviderAvailability(targetProvider.type, {
48879
+ installed: !!detected,
48880
+ detectedPath: detected?.path || null
48881
+ });
48882
+ } else {
48883
+ providerLoader.setCliDetectionResults(await detectCLIs(providerLoader, { includeVersion: false }), true);
48884
+ }
48885
+ }
48886
+ if (!providerType || targetCategory === "ide") {
48887
+ detectedIdesRef.value = await detectIDEs(providerLoader);
48888
+ providerLoader.setIdeDetectionResults(detectedIdesRef.value, true);
48889
+ }
48890
+ };
48280
48891
  const cliManager = new DaemonCliManager({
48281
48892
  ...config2.cliManagerDeps,
48282
48893
  getInstanceManager: () => instanceManager,
48283
48894
  getSessionRegistry: () => sessionRegistry
48284
48895
  }, providerLoader);
48285
48896
  LOG2.info("Init", "Detecting IDEs...");
48286
- detectedIdesRef.value = await detectIDEs();
48897
+ await refreshProviderAvailability();
48287
48898
  const installed = detectedIdesRef.value.filter((i) => i.installed);
48288
48899
  LOG2.info("Init", `Found ${installed.length} IDE(s): ${installed.map((i) => i.id).join(", ") || "none"}`);
48289
48900
  const cdpSetupContext = {
@@ -48323,7 +48934,11 @@ data: ${JSON.stringify(msg.data)}
48323
48934
  adapters: cliManager.adapters,
48324
48935
  providerLoader,
48325
48936
  instanceManager,
48326
- sessionRegistry
48937
+ sessionRegistry,
48938
+ onProviderSettingChanged: async (providerType) => {
48939
+ await refreshProviderAvailability(providerType);
48940
+ config2.onStatusChange?.();
48941
+ }
48327
48942
  });
48328
48943
  agentStreamManager = new DaemonAgentStreamManager(
48329
48944
  LOG2.forComponent("AgentStream").asLogFn(),
@@ -48747,6 +49362,9 @@ var StandaloneSessionHostControlPlane = class {
48747
49362
  async forceDetachClient(sessionId, clientId) {
48748
49363
  return this.request("force_detach_client", { sessionId, clientId });
48749
49364
  }
49365
+ async pruneDuplicateSessions(payload = {}) {
49366
+ return this.request("prune_duplicate_sessions", payload);
49367
+ }
48750
49368
  async acquireWrite(payload) {
48751
49369
  return this.request("acquire_write", payload);
48752
49370
  }