@adhdev/daemon-standalone 1.0.18-rc.2 → 1.0.18-rc.3

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
@@ -2872,6 +2872,7 @@ var require_dist = __commonJS({
2872
2872
  applyTerminalColorEnv: () => applyTerminalColorEnv,
2873
2873
  buildRuntimeDisplayName: () => buildRuntimeDisplayName,
2874
2874
  buildRuntimeKey: () => buildRuntimeKey,
2875
+ classifyTermination: () => classifyTermination,
2875
2876
  createLineParser: () => createLineParser2,
2876
2877
  createResponseEnvelope: () => createResponseEnvelope,
2877
2878
  createSessionHostControlPlane: () => createSessionHostControlPlane2,
@@ -2915,6 +2916,37 @@ var require_dist = __commonJS({
2915
2916
  "prune_duplicate_sessions",
2916
2917
  "list_sessions"
2917
2918
  ];
2919
+ function classifyTermination(input) {
2920
+ const { exitCode, terminatedAt } = input;
2921
+ const signal = input.signal ?? null;
2922
+ let reason;
2923
+ let lifecycle;
2924
+ if (signal !== null && signal !== 0) {
2925
+ reason = "signal";
2926
+ lifecycle = "failed";
2927
+ } else if (exitCode === null) {
2928
+ reason = "unknown";
2929
+ lifecycle = "failed";
2930
+ } else if (exitCode === 0) {
2931
+ reason = "exit";
2932
+ lifecycle = "stopped";
2933
+ } else {
2934
+ reason = "failed";
2935
+ lifecycle = "failed";
2936
+ }
2937
+ const termination = {
2938
+ exitCode,
2939
+ signal,
2940
+ reason,
2941
+ lifecycle,
2942
+ terminatedAt
2943
+ };
2944
+ if (typeof input.osPid === "number") termination.osPid = input.osPid;
2945
+ if (input.previousLifecycle) termination.previousLifecycle = input.previousLifecycle;
2946
+ if (typeof input.lastOutputAt === "number") termination.lastOutputAt = input.lastOutputAt;
2947
+ if (input.requestedStop) termination.requestedStop = input.requestedStop;
2948
+ return termination;
2949
+ }
2918
2950
  var DEFAULT_SESSION_RING_BUFFER_MAX_BYTES2 = 4 * 1024 * 1024;
2919
2951
  var SessionRingBuffer = class {
2920
2952
  maxBytes;
@@ -3314,10 +3346,11 @@ var require_dist = __commonJS({
3314
3346
  state.record.lastActivityAt = Date.now();
3315
3347
  return this.cloneRecord(state.record);
3316
3348
  }
3317
- markStopped(sessionId, lifecycle = "stopped") {
3349
+ markStopped(sessionId, lifecycle = "stopped", termination) {
3318
3350
  const state = this.requireSession(sessionId);
3319
3351
  state.record.lifecycle = lifecycle;
3320
3352
  state.record.lastActivityAt = Date.now();
3353
+ if (termination) state.record.termination = termination;
3321
3354
  return this.cloneRecord(state.record);
3322
3355
  }
3323
3356
  setLifecycle(sessionId, lifecycle) {
@@ -30232,10 +30265,10 @@ var require_dist3 = __commonJS({
30232
30265
  }
30233
30266
  function getDaemonBuildInfo() {
30234
30267
  if (cached2) return cached2;
30235
- const commit = readInjected(true ? "dcf5798b77def59c325d533c744c7d57b4fc8eac" : void 0) ?? "unknown";
30236
- const commitShort = readInjected(true ? "dcf5798b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30237
- const version2 = readInjected(true ? "1.0.18-rc.2" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30238
- const builtAt = readInjected(true ? "2026-07-21T07:46:01.928Z" : void 0);
30268
+ const commit = readInjected(true ? "96f3f1365be0d875f5089fc7ce7e4e254ca8e08d" : void 0) ?? "unknown";
30269
+ const commitShort = readInjected(true ? "96f3f136" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30270
+ const version2 = readInjected(true ? "1.0.18-rc.3" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30271
+ const builtAt = readInjected(true ? "2026-07-21T13:00:54.179Z" : void 0);
30239
30272
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
30240
30273
  return cached2;
30241
30274
  }
@@ -55727,6 +55760,22 @@ ${cont}` : cont;
55727
55760
  // ── Approval ─────────────────────────────────────
55728
55761
  lastApprovalResolvedAt = 0;
55729
55762
  lastResolvedModalMessage = "";
55763
+ /**
55764
+ * Normalized approval-context signature captured at resolve time (see
55765
+ * `computeApprovalContentSignature`) — the screen text with blank-line
55766
+ * padding and any manifest-declared chrome (transcriptPty.chromePatterns,
55767
+ * spinner.patterns) stripped out. Used by applyWaitingApproval's
55768
+ * isStaleResolvedRepaint check to tell "the same already-answered modal,
55769
+ * re-parsed from a screen that has not meaningfully changed" apart from
55770
+ * "a genuinely new approval" — content-based, not time-based, because
55771
+ * ordinary TUI chrome (status bar, context meter, blank-line repaint)
55772
+ * keeps producing fresh PTY bytes on an otherwise-unchanged screen and
55773
+ * defeats any output-timestamp discriminator within a few hundred ms.
55774
+ * Cleared whenever a turn starts or the session tears down (see
55775
+ * onTurnStarted / resetActiveTurnState / onPtyExit) so a next-turn
55776
+ * approval is never compared against stale prior-turn state.
55777
+ */
55778
+ lastApprovalResolvedContentSignature = "";
55730
55779
  /**
55731
55780
  * Monotonic counter bumped every time the FSM *enters* waiting_approval
55732
55781
  * with a freshly captured modal (see `applyWaitingApproval`). It is the
@@ -55846,11 +55895,17 @@ ${cont}` : cont;
55846
55895
  this.currentTurnTaskId = typeof turnScope.taskId === "string" && turnScope.taskId.trim() ? turnScope.taskId : null;
55847
55896
  this.currentTurnStartedAt = Date.now();
55848
55897
  this.responseEpoch += 1;
55898
+ this.clearApprovalResolutionMemory();
55899
+ if (this.provider.transcriptAuthority === "provider" && this.currentStatus !== "waiting_approval") {
55900
+ this.setStatus("generating", "turn_started");
55901
+ this.callbacks.onStatusChange();
55902
+ }
55849
55903
  }
55850
55904
  /** Called when PTY exits */
55851
55905
  onPtyExit() {
55852
55906
  this.clearAllTimers();
55853
55907
  this.setStatus("stopped", "pty_exit");
55908
+ this.clearApprovalResolutionMemory();
55854
55909
  }
55855
55910
  /** Called when adapter starts up successfully */
55856
55911
  onSpawnReady() {
@@ -55892,6 +55947,7 @@ ${cont}` : cont;
55892
55947
  this.activeModal = null;
55893
55948
  this.lastApprovalResolvedAt = Date.now();
55894
55949
  this.lastResolvedModalMessage = currentModalMessage;
55950
+ this.lastApprovalResolvedContentSignature = this.computeApprovalContentSignature(snap);
55895
55951
  this.lastResolvedEntrySeq = this.approvalEntrySeq;
55896
55952
  this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
55897
55953
  if (this.approvalExitTimeout) {
@@ -56014,6 +56070,7 @@ ${cont}` : cont;
56014
56070
  this.pendingScriptStatusSince = 0;
56015
56071
  this.approvalResumeDeferSince = 0;
56016
56072
  this.approvalResumeDeferEpoch = -1;
56073
+ this.clearApprovalResolutionMemory();
56017
56074
  }
56018
56075
  clearIdleFinishCandidate(reason) {
56019
56076
  if (!this.idleFinishCandidate) return;
@@ -56172,7 +56229,15 @@ ${cont}` : cont;
56172
56229
  }
56173
56230
  if (!status) return;
56174
56231
  const prevStatus = this.currentStatus;
56175
- const ctx = { now, modal, status, parsedMessages, lastParsedAssistant, parsedStatus: parsedStatus || null, prevStatus };
56232
+ const ctx = {
56233
+ now,
56234
+ modal,
56235
+ status,
56236
+ parsedMessages,
56237
+ lastParsedAssistant,
56238
+ parsedStatus: parsedStatus || null,
56239
+ prevStatus
56240
+ };
56176
56241
  if (!this.applyPendingScriptStatusDebounce(ctx)) return;
56177
56242
  const recentInteractiveActivity = this.hasRecentInteractiveActivity(snap, now);
56178
56243
  LOG2.debug(
@@ -56208,7 +56273,7 @@ ${cont}` : cont;
56208
56273
  return;
56209
56274
  }
56210
56275
  if (status === "waiting_approval") {
56211
- this.applyWaitingApproval(ctx);
56276
+ this.applyWaitingApproval(ctx, snap);
56212
56277
  return;
56213
56278
  }
56214
56279
  if (status === "generating") {
@@ -56268,7 +56333,7 @@ ${cont}` : cont;
56268
56333
  }, this.timeouts.generatingIdle);
56269
56334
  this.callbacks.onStatusChange();
56270
56335
  }
56271
- applyWaitingApproval(ctx) {
56336
+ applyWaitingApproval(ctx, snap) {
56272
56337
  const { modal } = ctx;
56273
56338
  this.clearIdleFinishCandidate("waiting_approval");
56274
56339
  const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
@@ -56315,6 +56380,11 @@ ${cont}` : cont;
56315
56380
  }
56316
56381
  return;
56317
56382
  }
56383
+ const isStaleResolvedRepaint = !this.activeModal && this.isStaleResolvedApproval(modal, snap);
56384
+ if (isStaleResolvedRepaint) {
56385
+ LOG2.debug("CLI", `[${this.provider.type}] ignoring stale re-parsed approval matching the just-resolved modal (approval-context signature unchanged)`);
56386
+ return;
56387
+ }
56318
56388
  this.modalLostAt = 0;
56319
56389
  this.isWaitingForResponse = true;
56320
56390
  this.setStatus("waiting_approval", "script_detect");
@@ -56600,6 +56670,86 @@ ${cont}` : cont;
56600
56670
  this.recordTrace("idle_finish_cancelled", { trigger: reason });
56601
56671
  }
56602
56672
  // ─── Helpers ────────────────────────────────────────────────────────────
56673
+ /**
56674
+ * Derive a stable approval-context signature from the current screen,
56675
+ * with blank-line padding and any manifest-declared chrome stripped out.
56676
+ *
56677
+ * Generic by design — no kimi- or provider-specific hardcoding: it reads
56678
+ * whatever `tui.transcriptPty.chromePatterns` and `tui.spinner.patterns`
56679
+ * the ACTIVE provider's own manifest already declares (present on any
56680
+ * declarative-TUI provider; simply absent/empty for scripted providers,
56681
+ * in which case this degrades to blank-line stripping only — never worse
56682
+ * than comparing the raw screen). Those pattern lists exist precisely to
56683
+ * name "known volatile repaint noise" (status bar, context meter, spinner
56684
+ * ticks, banners) — reusing them here means the SAME declared knowledge
56685
+ * that governs transcript-chrome stripping also governs staleness
56686
+ * detection, instead of re-encoding provider knowledge into daemon-core.
56687
+ *
56688
+ * Deliberately NOT a hash of the whole screen/buffer: an ordinary TUI
56689
+ * repaints its footer/status/context-meter chrome continuously even while
56690
+ * genuinely idle, so a raw whole-screen or whole-buffer fingerprint (or a
56691
+ * mere "did any bytes arrive" timestamp) changes on every repaint tick
56692
+ * regardless of whether anything approval-relevant actually happened.
56693
+ * Stripping the declared chrome first yields a signature that only
56694
+ * changes when the surrounding conversation/tool-output content itself
56695
+ * changes — exactly the discriminator applyWaitingApproval's
56696
+ * isStaleResolvedRepaint check needs.
56697
+ */
56698
+ computeApprovalContentSignature(snap) {
56699
+ const screenText = snap.screenText || snap.accumulatedBuffer || "";
56700
+ if (!screenText) return "";
56701
+ const tui = this.provider.tui;
56702
+ const patternSpecs = [
56703
+ ...Array.isArray(tui?.transcriptPty?.chromePatterns) ? tui.transcriptPty.chromePatterns : [],
56704
+ ...Array.isArray(tui?.spinner?.patterns) ? tui.spinner.patterns : []
56705
+ ];
56706
+ const chromeRegexes = [];
56707
+ for (const spec of patternSpecs) {
56708
+ if (spec && typeof spec.regex === "string") {
56709
+ try {
56710
+ chromeRegexes.push(new RegExp(spec.regex, typeof spec.flags === "string" ? spec.flags : ""));
56711
+ } catch {
56712
+ }
56713
+ }
56714
+ }
56715
+ const kept = [];
56716
+ for (const rawLine of screenText.split("\n")) {
56717
+ const line = rawLine.trim();
56718
+ if (!line) continue;
56719
+ if (chromeRegexes.some((re) => re.test(line))) continue;
56720
+ kept.push(line);
56721
+ }
56722
+ return kept.join("\n");
56723
+ }
56724
+ /**
56725
+ * True when `modal` is a stale re-parse of an already-resolved approval:
56726
+ * same message text, and the chrome-stripped approval-context signature
56727
+ * of `snap` is unchanged from the signature captured at resolve time.
56728
+ *
56729
+ * Public and reused verbatim by BOTH the settled-eval capture path
56730
+ * (applyWaitingApproval, below) and any OUTSIDE re-parse the adapter
56731
+ * performs independently of the settle loop — e.g. provider-cli-adapter's
56732
+ * getStatus()/getDebugState() startup-gate modal detection, which reads
56733
+ * `recentOutputBuffer` directly while `startupParseGate` is open and can
56734
+ * re-surface the same already-resolved modal before the gate closes.
56735
+ * Centralizing the discriminator here means there is exactly ONE
56736
+ * definition of "stale" for the whole session — no divergent duplicate
56737
+ * heuristic re-implemented per call site.
56738
+ */
56739
+ isStaleResolvedApproval(modal, snap) {
56740
+ if (!modal) return false;
56741
+ const normalizedMessage = typeof modal.message === "string" ? modal.message.trim() : "";
56742
+ if (!normalizedMessage) return false;
56743
+ return this.lastApprovalResolvedAt > 0 && normalizedMessage === this.lastResolvedModalMessage && this.computeApprovalContentSignature(snap) === this.lastApprovalResolvedContentSignature;
56744
+ }
56745
+ /** Clear all resolve-time approval bookkeeping (message, timestamp, content
56746
+ * signature) — called at turn/session boundaries so a next-turn or
56747
+ * next-session approval is never compared against stale prior state. */
56748
+ clearApprovalResolutionMemory() {
56749
+ this.lastApprovalResolvedAt = 0;
56750
+ this.lastResolvedModalMessage = "";
56751
+ this.lastApprovalResolvedContentSignature = "";
56752
+ }
56603
56753
  /**
56604
56754
  * Schedule one more settled evaluation while pinned to `waiting_approval`
56605
56755
  * with no actionable modal. The settled FSM normally only re-runs on new
@@ -57425,8 +57575,8 @@ ${lastSnapshot}`;
57425
57575
  }
57426
57576
  }
57427
57577
  });
57428
- this.ptyProcess.onExit(({ exitCode }) => {
57429
- LOG2.info("CLI", `[${this.cliType}] Exit code ${exitCode}`);
57578
+ this.ptyProcess.onExit(({ exitCode, signal }) => {
57579
+ LOG2.info("CLI", `[${this.cliType}] Exit code ${exitCode === null || exitCode === void 0 ? "unknown" : exitCode}${signal ? ` (signal ${signal})` : ""}`);
57430
57580
  this.flushPendingOutputParse();
57431
57581
  this.ptyProcess = null;
57432
57582
  this.engine.onPtyExit();
@@ -57732,7 +57882,10 @@ ${lastSnapshot}`;
57732
57882
  // ─── Public API (CliAdapter) ───────────────────
57733
57883
  getStatus(options = {}) {
57734
57884
  const allowParse = options.allowParse !== false;
57735
- const startupModal = allowParse && this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
57885
+ let startupModal = allowParse && this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
57886
+ if (startupModal && this.engine.isStaleResolvedApproval(startupModal, { screenText: this.terminalScreen.getText(), accumulatedBuffer: this.accumulatedBuffer })) {
57887
+ startupModal = null;
57888
+ }
57736
57889
  const startupDetectedStatus = allowParse && this.startupParseGate && !startupModal ? this.runDetectStatus(this.recentOutputBuffer || this.terminalScreen.getText()) : null;
57737
57890
  if (allowParse && this.engine.currentStatus === "generating" && !this.engine.currentTurnScope && !this.engine.activeModal) {
57738
57891
  const now = Date.now();
@@ -57766,7 +57919,7 @@ ${lastSnapshot}`;
57766
57919
  if (liveDetect === "waiting_approval") {
57767
57920
  const liveModal = this.runParseApproval(this.terminalScreen.getText()) || this.runParseApproval(this.recentOutputBuffer);
57768
57921
  const buttonsOk = liveModal && Array.isArray(liveModal.buttons) && liveModal.buttons.some((b) => typeof b === "string" && b.trim());
57769
- if (liveModal && buttonsOk) {
57922
+ if (liveModal && buttonsOk && !this.engine.isStaleResolvedApproval(liveModal, { screenText: this.terminalScreen.getText(), accumulatedBuffer: this.accumulatedBuffer })) {
57770
57923
  effectiveModal = liveModal;
57771
57924
  if (!this.engine.activeModal) this.engine.activeModal = liveModal;
57772
57925
  }
@@ -58892,7 +59045,10 @@ ${lastSnapshot}`;
58892
59045
  }
58893
59046
  getDebugState() {
58894
59047
  const screenText = sanitizeTerminalText(this.terminalScreen.getText());
58895
- const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
59048
+ let startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
59049
+ if (startupModal && this.engine.isStaleResolvedApproval(startupModal, { screenText: this.terminalScreen.getText(), accumulatedBuffer: this.accumulatedBuffer })) {
59050
+ startupModal = null;
59051
+ }
58896
59052
  const startupDetectedStatus = this.startupParseGate && !startupModal ? this.runDetectStatus(this.recentOutputBuffer || screenText) : null;
58897
59053
  const effectiveReady = this.ready || !!startupModal || startupDetectedStatus === "waiting_approval";
58898
59054
  const parsedDebugState = this.getParsedDebugState();
@@ -72529,18 +72685,196 @@ ${formatManifestValidationIssues2(validation.issues)}`,
72529
72685
  }
72530
72686
  };
72531
72687
  init_config();
72532
- var import_child_process6 = require("child_process");
72533
72688
  var import_child_process7 = require("child_process");
72689
+ var import_child_process8 = require("child_process");
72534
72690
  var fs15 = __toESM2(require("fs"));
72535
72691
  var os13 = __toESM2(require("os"));
72536
72692
  var path21 = __toESM2(require("path"));
72537
- var import_child_process5 = require("child_process");
72693
+ var import_child_process6 = require("child_process");
72538
72694
  var fs14 = __toESM2(require("fs"));
72539
72695
  var http2 = __toESM2(require("http"));
72540
72696
  var path20 = __toESM2(require("path"));
72697
+ var import_child_process5 = require("child_process");
72698
+ function defaultExecFileSync() {
72699
+ return import_child_process5.execFileSync;
72700
+ }
72701
+ function getWindowsProcessCommandLine(pid, exec7) {
72702
+ const pidFilter = `ProcessId=${pid}`;
72703
+ try {
72704
+ const psOut = exec7("powershell.exe", [
72705
+ "-NoProfile",
72706
+ "-NonInteractive",
72707
+ "-ExecutionPolicy",
72708
+ "Bypass",
72709
+ "-Command",
72710
+ `(Get-CimInstance Win32_Process -Filter "${pidFilter}").CommandLine`
72711
+ ], { encoding: "utf8", timeout: 5e3, stdio: ["ignore", "pipe", "ignore"], windowsHide: true });
72712
+ const text = String(psOut).trim();
72713
+ if (text) return text;
72714
+ } catch {
72715
+ }
72716
+ try {
72717
+ const wmicOut = exec7("wmic", [
72718
+ "process",
72719
+ "where",
72720
+ pidFilter,
72721
+ "get",
72722
+ "CommandLine"
72723
+ ], { encoding: "utf8", timeout: 3e3, stdio: ["ignore", "pipe", "ignore"], windowsHide: true });
72724
+ const text = String(wmicOut).trim();
72725
+ if (text) return text;
72726
+ } catch {
72727
+ }
72728
+ return null;
72729
+ }
72730
+ function getProcessCommandLine(pid, options = {}) {
72731
+ if (!Number.isFinite(pid) || pid <= 0) return null;
72732
+ const exec7 = options.execFileSync ?? defaultExecFileSync();
72733
+ const platform10 = options.platform ?? process.platform;
72734
+ if (platform10 === "win32") {
72735
+ return getWindowsProcessCommandLine(pid, exec7);
72736
+ }
72737
+ try {
72738
+ const text = String(exec7("ps", ["-o", "command=", "-p", String(pid)], {
72739
+ encoding: "utf8",
72740
+ timeout: 3e3,
72741
+ stdio: ["ignore", "pipe", "ignore"]
72742
+ })).trim();
72743
+ return text || null;
72744
+ } catch {
72745
+ return null;
72746
+ }
72747
+ }
72748
+ function parseNodeScriptPath(commandLine) {
72749
+ if (!commandLine) return null;
72750
+ let rest = commandLine.trim();
72751
+ if (rest.startsWith('"')) {
72752
+ const end = rest.indexOf('"', 1);
72753
+ if (end === -1) return null;
72754
+ rest = rest.slice(end + 1).trim();
72755
+ } else {
72756
+ const idx2 = rest.search(/\s/);
72757
+ if (idx2 === -1) return null;
72758
+ rest = rest.slice(idx2 + 1).trim();
72759
+ }
72760
+ if (!rest) return null;
72761
+ if (rest.startsWith('"')) {
72762
+ const end = rest.indexOf('"', 1);
72763
+ return end === -1 ? rest.slice(1) : rest.slice(1, end);
72764
+ }
72765
+ const idx = rest.search(/\s/);
72766
+ return idx === -1 ? rest : rest.slice(0, idx);
72767
+ }
72768
+ function normalizeWindowsPath(value) {
72769
+ return value.toLowerCase().replace(/\//g, "\\").replace(/\\+$/, "");
72770
+ }
72771
+ function killProcess(pid, options = {}) {
72772
+ if (!Number.isFinite(pid) || pid <= 0) return false;
72773
+ const exec7 = options.execFileSync ?? defaultExecFileSync();
72774
+ const platform10 = options.platform ?? process.platform;
72775
+ try {
72776
+ if (platform10 === "win32") {
72777
+ exec7("taskkill", ["/PID", String(pid), "/T", "/F"], {
72778
+ stdio: "ignore",
72779
+ windowsHide: true
72780
+ });
72781
+ } else {
72782
+ process.kill(pid, "SIGTERM");
72783
+ }
72784
+ return true;
72785
+ } catch {
72786
+ return false;
72787
+ }
72788
+ }
72789
+ async function waitForPidExit(pid, timeoutMs) {
72790
+ const start = Date.now();
72791
+ while (Date.now() - start < timeoutMs) {
72792
+ try {
72793
+ process.kill(pid, 0);
72794
+ await new Promise((resolve27) => setTimeout(resolve27, 250));
72795
+ } catch {
72796
+ return true;
72797
+ }
72798
+ }
72799
+ return false;
72800
+ }
72801
+ function listOwnedNodeProcesses(options) {
72802
+ const platform10 = options.platform ?? process.platform;
72803
+ if (platform10 !== "win32") return [];
72804
+ const exec7 = options.execFileSync ?? defaultExecFileSync();
72805
+ const prefixes = options.prefixes.map((p) => normalizeWindowsPath(p));
72806
+ const exclude = new Set((options.excludePids ?? []).filter((n) => Number.isFinite(n) && n > 0));
72807
+ const markers = (options.markers ?? []).map((m) => m.toLowerCase().replace(/\//g, "\\"));
72808
+ let pids = [];
72809
+ try {
72810
+ const out = String(exec7("powershell.exe", [
72811
+ "-NoProfile",
72812
+ "-NonInteractive",
72813
+ "-ExecutionPolicy",
72814
+ "Bypass",
72815
+ "-Command",
72816
+ "Get-Process node -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Id | ConvertTo-Json -Compress"
72817
+ ], { encoding: "utf8", timeout: 8e3, stdio: ["ignore", "pipe", "ignore"], windowsHide: true })).trim();
72818
+ if (out) {
72819
+ const parsed = JSON.parse(out);
72820
+ pids = Array.isArray(parsed) ? parsed : [parsed];
72821
+ }
72822
+ } catch {
72823
+ return [];
72824
+ }
72825
+ const results = [];
72826
+ for (const pid of pids) {
72827
+ if (!Number.isFinite(pid) || pid <= 0 || exclude.has(pid)) continue;
72828
+ const commandLine = getProcessCommandLine(pid, { platform: platform10, execFileSync: exec7 });
72829
+ if (!commandLine) continue;
72830
+ const lower = commandLine.toLowerCase();
72831
+ const underPrefix = prefixes.some((prefix) => lower.includes(prefix));
72832
+ if (!underPrefix) continue;
72833
+ if (markers.length > 0 && !markers.some((marker) => lower.includes(marker))) continue;
72834
+ results.push({ pid, commandLine });
72835
+ }
72836
+ return results;
72837
+ }
72838
+ async function stopOwnedProcesses(options) {
72839
+ const waitMs = options.waitMs ?? 15e3;
72840
+ const killed = /* @__PURE__ */ new Set();
72841
+ for (const p of options.processes) {
72842
+ if (killProcess(p.pid, options)) killed.add(p.pid);
72843
+ }
72844
+ const survivors = [];
72845
+ for (const p of options.processes) {
72846
+ if (!killed.has(p.pid)) {
72847
+ survivors.push(p);
72848
+ continue;
72849
+ }
72850
+ const exited = await waitForPidExit(p.pid, waitMs);
72851
+ if (!exited) survivors.push(p);
72852
+ }
72853
+ return { stopped: options.processes.length - survivors.length, survivors };
72854
+ }
72855
+ async function stopOwnedProcessesForPrefixes(options) {
72856
+ const processes = listOwnedNodeProcesses(options);
72857
+ if (processes.length === 0) return { stopped: 0, survivors: [] };
72858
+ options.log?.(`Stopping ${processes.length} owned process(es) under prefixes: ${options.prefixes.join(", ")}`);
72859
+ const result = await stopOwnedProcesses({
72860
+ processes,
72861
+ waitMs: options.waitMs,
72862
+ platform: options.platform,
72863
+ execFileSync: options.execFileSync
72864
+ });
72865
+ if (result.survivors.length > 0) {
72866
+ options.log?.(`Could not stop ${result.survivors.length} owned process(es): ${result.survivors.map((s2) => s2.pid).join(", ")}`);
72867
+ }
72868
+ return result;
72869
+ }
72541
72870
  var POINTER_NAME = ".adhdev-current";
72542
72871
  var STABLE_FILES = [POINTER_NAME, "adhdev.cmd", "adhdev.ps1", "adhdev"];
72543
72872
  var DEFAULT_HEALTH_PORT = 19222;
72873
+ var ADHDEV_OWNED_MARKERS = [
72874
+ "session-host-daemon",
72875
+ "node_modules/adhdev",
72876
+ "node_modules/@adhdev/daemon-standalone"
72877
+ ];
72544
72878
  function normalizeForCompare(value) {
72545
72879
  return path20.resolve(value).replace(/[\\/]+$/, "").toLowerCase();
72546
72880
  }
@@ -72563,7 +72897,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
72563
72897
  }
72564
72898
  function nodeMajor(nodeExecutable) {
72565
72899
  try {
72566
- const version2 = String((0, import_child_process5.execFileSync)(nodeExecutable, ["-p", "process.versions.node"], {
72900
+ const version2 = String((0, import_child_process6.execFileSync)(nodeExecutable, ["-p", "process.versions.node"], {
72567
72901
  encoding: "utf8",
72568
72902
  timeout: 5e3,
72569
72903
  windowsHide: true,
@@ -72625,7 +72959,7 @@ exit $LASTEXITCODE\r
72625
72959
  }
72626
72960
  }
72627
72961
  function validateStagedCli(portableNode, cliEntry, targetVersion) {
72628
- const output = String((0, import_child_process5.execFileSync)(portableNode, [cliEntry, "--version"], {
72962
+ const output = String((0, import_child_process6.execFileSync)(portableNode, [cliEntry, "--version"], {
72629
72963
  encoding: "utf8",
72630
72964
  timeout: 15e3,
72631
72965
  windowsHide: true,
@@ -72654,7 +72988,7 @@ exit $LASTEXITCODE\r
72654
72988
  `} finally { if ([IO.File]::Exists($temporary)) { [IO.File]::Delete($temporary) } }`
72655
72989
  ].join("\n");
72656
72990
  const encoded = Buffer.from(script, "utf16le").toString("base64");
72657
- const result = (0, import_child_process5.spawnSync)("powershell.exe", [
72991
+ const result = (0, import_child_process6.spawnSync)("powershell.exe", [
72658
72992
  "-NoLogo",
72659
72993
  "-NoProfile",
72660
72994
  "-NonInteractive",
@@ -72739,6 +73073,19 @@ exit $LASTEXITCODE\r
72739
73073
  pinStagedShims(stagedPrefix, portableNode, stagedCliEntry);
72740
73074
  validateStagedCli(portableNode, stagedCliEntry, targetVersion);
72741
73075
  hooks.log(`Validated staged CLI and portable Node 22 shims in ${stagedPrefix}`);
73076
+ const excludedPids = new Set([process.pid, ...options.excludePids ?? []].filter((n) => Number.isFinite(n) && n > 0));
73077
+ const preStop = await stopOwnedProcessesForPrefixes({
73078
+ prefixes: [layout.activePrefix, layout.stablePrefix],
73079
+ excludePids: Array.from(excludedPids),
73080
+ markers: Array.from(ADHDEV_OWNED_MARKERS),
73081
+ waitMs: 15e3,
73082
+ log: hooks.log
73083
+ });
73084
+ if (preStop.survivors.length > 0) {
73085
+ throw new Error(
73086
+ `Cannot activate ${versionName}: ${preStop.survivors.length} owned process(es) still running under the current prefix`
73087
+ );
73088
+ }
72742
73089
  activated = true;
72743
73090
  publishStableShimsAndPointer(layout, versionName);
72744
73091
  hooks.log(`Atomically activated ${versionName}`);
@@ -72748,7 +73095,7 @@ exit $LASTEXITCODE\r
72748
73095
  throw new Error("replacement daemon did not pass the health/version gate");
72749
73096
  }
72750
73097
  hooks.log(`Replacement daemon pid ${daemonPid} passed health for ${targetVersion}`);
72751
- hooks.cleanup(layout, stagedPrefix);
73098
+ await hooks.cleanup(layout, stagedPrefix);
72752
73099
  return { stagedPrefix, stagedCliEntry, daemonPid };
72753
73100
  } catch (error48) {
72754
73101
  if (restarted?.pid) hooks.stopProcess(restarted.pid);
@@ -72766,7 +73113,7 @@ exit $LASTEXITCODE\r
72766
73113
  hooks.log("Failed to restart the previous daemon during rollback");
72767
73114
  }
72768
73115
  try {
72769
- hooks.cleanup(layout, layout.activePrefix);
73116
+ await hooks.cleanup(layout, layout.activePrefix);
72770
73117
  } catch {
72771
73118
  }
72772
73119
  throw error48;
@@ -72778,7 +73125,7 @@ exit $LASTEXITCODE\r
72778
73125
  const env2 = { ...options.env, ADHDEV_BOOTSTRAP: "1" };
72779
73126
  const pathKey = Object.keys(env2).find((key2) => key2.toLowerCase() === "path") || "Path";
72780
73127
  env2[pathKey] = `${path20.dirname(portableNode)};${env2[pathKey] || ""}`;
72781
- (0, import_child_process5.execFileSync)(portableNode, [
73128
+ (0, import_child_process6.execFileSync)(portableNode, [
72782
73129
  options.npmCliPath,
72783
73130
  "install",
72784
73131
  "-g",
@@ -72798,7 +73145,7 @@ exit $LASTEXITCODE\r
72798
73145
  restart: (portableNode, stagedCliEntry) => {
72799
73146
  const restartArgv = options.restartArgv.map((arg, index) => index === 0 ? stagedCliEntry : arg);
72800
73147
  if (restartArgv.length === 0) throw new Error("replacement daemon restart arguments are missing");
72801
- const child = (0, import_child_process5.spawn)(portableNode, restartArgv, {
73148
+ const child = (0, import_child_process6.spawn)(portableNode, restartArgv, {
72802
73149
  detached: true,
72803
73150
  stdio: "ignore",
72804
73151
  windowsHide: true,
@@ -72810,7 +73157,7 @@ exit $LASTEXITCODE\r
72810
73157
  },
72811
73158
  restartOld: (portableNode) => {
72812
73159
  if (options.restartArgv.length === 0) return;
72813
- const child = (0, import_child_process5.spawn)(portableNode, options.restartArgv, {
73160
+ const child = (0, import_child_process6.spawn)(portableNode, options.restartArgv, {
72814
73161
  detached: true,
72815
73162
  stdio: "ignore",
72816
73163
  windowsHide: true,
@@ -72848,15 +73195,23 @@ exit $LASTEXITCODE\r
72848
73195
  },
72849
73196
  stopProcess: (pid) => {
72850
73197
  try {
72851
- (0, import_child_process5.execFileSync)("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
73198
+ (0, import_child_process6.execFileSync)("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
72852
73199
  } catch {
72853
73200
  }
72854
73201
  },
72855
- cleanup: (layout, activePrefix) => boundedCleanupInactivePrefixes(layout, activePrefix, options.log),
73202
+ cleanup: async (layout, activePrefix) => cleanupInactivePrefixesWithGuard({
73203
+ layout,
73204
+ activePrefix,
73205
+ excludePids: [process.pid],
73206
+ markers: Array.from(ADHDEV_OWNED_MARKERS),
73207
+ waitMs: 15e3,
73208
+ log: options.log
73209
+ }),
72856
73210
  log: options.log
72857
73211
  };
72858
73212
  }
72859
- function boundedCleanupInactivePrefixes(layout, activePrefix, log) {
73213
+ async function cleanupInactivePrefixesWithGuard(options) {
73214
+ const { layout, activePrefix, log } = options;
72860
73215
  let candidates = [];
72861
73216
  try {
72862
73217
  candidates = fs14.readdirSync(layout.installRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("version-")).map((entry) => path20.join(layout.installRoot, entry.name)).filter((entry) => normalizeForCompare(entry) !== normalizeForCompare(activePrefix)).sort().slice(0, 8);
@@ -72864,19 +73219,41 @@ exit $LASTEXITCODE\r
72864
73219
  return;
72865
73220
  }
72866
73221
  if (candidates.length === 0) return;
72867
- const escaped = candidates.map((candidate) => quotePowerShellLiteral(candidate)).join(",");
72868
- const script = `$ErrorActionPreference='Stop'; @(${escaped}) | ForEach-Object { if (Test-Path -LiteralPath $_) { Remove-Item -LiteralPath $_ -Recurse -Force } }`;
72869
- const encoded = Buffer.from(script, "utf16le").toString("base64");
72870
- const result = (0, import_child_process5.spawnSync)("powershell.exe", [
72871
- "-NoLogo",
72872
- "-NoProfile",
72873
- "-NonInteractive",
72874
- "-ExecutionPolicy",
72875
- "Bypass",
72876
- "-EncodedCommand",
72877
- encoded
72878
- ], { timeout: 5e3, windowsHide: true, stdio: "ignore" });
72879
- if (result.error || result.status !== 0) log("Bounded inactive-prefix cleanup was incomplete; future updates will retry");
73222
+ for (const candidate of candidates) {
73223
+ const stopResult = await stopOwnedProcessesForPrefixes({
73224
+ prefixes: [candidate],
73225
+ excludePids: options.excludePids,
73226
+ markers: options.markers,
73227
+ waitMs: options.waitMs ?? 15e3,
73228
+ log
73229
+ });
73230
+ if (stopResult.survivors.length > 0) {
73231
+ log?.(`Skipping cleanup of ${candidate}: ${stopResult.survivors.length} owned process(es) could not be stopped`);
73232
+ continue;
73233
+ }
73234
+ removeInactivePrefix(candidate, log);
73235
+ }
73236
+ }
73237
+ function removeInactivePrefix(target, log) {
73238
+ try {
73239
+ const escaped = quotePowerShellLiteral(target);
73240
+ const script = `if (Test-Path -LiteralPath ${escaped}) { Remove-Item -LiteralPath ${escaped} -Recurse -Force }`;
73241
+ const encoded = Buffer.from(script, "utf16le").toString("base64");
73242
+ const result = (0, import_child_process6.spawnSync)("powershell.exe", [
73243
+ "-NoLogo",
73244
+ "-NoProfile",
73245
+ "-NonInteractive",
73246
+ "-ExecutionPolicy",
73247
+ "Bypass",
73248
+ "-EncodedCommand",
73249
+ encoded
73250
+ ], { timeout: 5e3, windowsHide: true, stdio: "ignore" });
73251
+ if (result.error || result.status !== 0) {
73252
+ log?.(`Failed to remove inactive prefix ${target}: ${result.error?.message || `exit ${result.status}`}`);
73253
+ }
73254
+ } catch (error48) {
73255
+ log?.(`Failed to remove inactive prefix ${target}: ${error48?.message || String(error48)}`);
73256
+ }
72880
73257
  }
72881
73258
  var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
72882
73259
  function getUpgradeLogPath(home = os13.homedir()) {
@@ -73003,7 +73380,7 @@ exit $LASTEXITCODE\r
73003
73380
  }
73004
73381
  function execNpmCommandSync(args, options = {}, surface) {
73005
73382
  const execOptions = surface?.execOptions || getNpmExecOptions();
73006
- return (0, import_child_process6.execFileSync)(
73383
+ return (0, import_child_process7.execFileSync)(
73007
73384
  surface?.npmExecutable || "npm",
73008
73385
  [...surface?.npmArgsPrefix || [], ...args],
73009
73386
  {
@@ -73013,74 +73390,10 @@ exit $LASTEXITCODE\r
73013
73390
  }
73014
73391
  );
73015
73392
  }
73016
- function killPid(pid) {
73017
- try {
73018
- if (process.platform === "win32") {
73019
- (0, import_child_process6.execFileSync)("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
73020
- } else {
73021
- process.kill(pid, "SIGTERM");
73022
- }
73023
- return true;
73024
- } catch {
73025
- return false;
73026
- }
73027
- }
73028
- function getWindowsProcessCommandLine(pid) {
73029
- const pidFilter = `ProcessId=${pid}`;
73030
- try {
73031
- const psOut = (0, import_child_process6.execFileSync)("powershell.exe", [
73032
- "-NoProfile",
73033
- "-NonInteractive",
73034
- "-ExecutionPolicy",
73035
- "Bypass",
73036
- "-Command",
73037
- `(Get-CimInstance Win32_Process -Filter "${pidFilter}").CommandLine`
73038
- ], { encoding: "utf8", timeout: 5e3, stdio: ["ignore", "pipe", "ignore"], windowsHide: true }).trim();
73039
- if (psOut) return psOut;
73040
- } catch {
73041
- }
73042
- try {
73043
- const wmicOut = (0, import_child_process6.execFileSync)("wmic", [
73044
- "process",
73045
- "where",
73046
- pidFilter,
73047
- "get",
73048
- "CommandLine"
73049
- ], { encoding: "utf8", timeout: 3e3, stdio: ["ignore", "pipe", "ignore"], windowsHide: true }).trim();
73050
- if (wmicOut) return wmicOut;
73051
- } catch {
73052
- }
73053
- return null;
73054
- }
73055
- function getProcessCommandLine(pid) {
73056
- if (!Number.isFinite(pid) || pid <= 0) return null;
73057
- if (process.platform === "win32") return getWindowsProcessCommandLine(pid);
73058
- try {
73059
- const text = (0, import_child_process6.execFileSync)("ps", ["-o", "command=", "-p", String(pid)], {
73060
- encoding: "utf8",
73061
- timeout: 3e3,
73062
- stdio: ["ignore", "pipe", "ignore"]
73063
- }).trim();
73064
- return text || null;
73065
- } catch {
73066
- return null;
73067
- }
73068
- }
73069
73393
  function isManagedSessionHostPid(pid) {
73070
73394
  const commandLine = getProcessCommandLine(pid);
73071
73395
  return !!commandLine && /session-host-daemon/i.test(commandLine);
73072
73396
  }
73073
- async function waitForPidExit(pid, timeoutMs) {
73074
- const start = Date.now();
73075
- while (Date.now() - start < timeoutMs) {
73076
- try {
73077
- process.kill(pid, 0);
73078
- await new Promise((resolve27) => setTimeout(resolve27, 250));
73079
- } catch {
73080
- return;
73081
- }
73082
- }
73083
- }
73084
73397
  async function stopSessionHostProcesses(appName) {
73085
73398
  const pidFile = path21.join(os13.homedir(), ".adhdev", `${appName}-session-host.pid`);
73086
73399
  let killedPid = null;
@@ -73088,7 +73401,7 @@ exit $LASTEXITCODE\r
73088
73401
  if (fs15.existsSync(pidFile)) {
73089
73402
  const pid = Number.parseInt(fs15.readFileSync(pidFile, "utf8").trim(), 10);
73090
73403
  if (Number.isFinite(pid) && pid !== process.pid && isManagedSessionHostPid(pid)) {
73091
- if (killPid(pid)) killedPid = pid;
73404
+ if (killProcess(pid)) killedPid = pid;
73092
73405
  }
73093
73406
  }
73094
73407
  } catch {
@@ -73124,7 +73437,7 @@ exit $LASTEXITCODE\r
73124
73437
  ].join("\n");
73125
73438
  let out = "";
73126
73439
  try {
73127
- out = String((0, import_child_process6.execFileSync)("powershell.exe", [
73440
+ out = String((0, import_child_process7.execFileSync)("powershell.exe", [
73128
73441
  "-NoProfile",
73129
73442
  "-NonInteractive",
73130
73443
  "-ExecutionPolicy",
@@ -73156,7 +73469,7 @@ exit $LASTEXITCODE\r
73156
73469
  appendUpgradeLog(
73157
73470
  `Foreign native-addon holder found: pid ${holder.pid}${holder.commandLine ? ` \u2014 ${holder.commandLine}` : ""}`
73158
73471
  );
73159
- const killed = killPid(holder.pid);
73472
+ const killed = killProcess(holder.pid);
73160
73473
  if (killed) {
73161
73474
  await waitForPidExit(holder.pid, 15e3);
73162
73475
  appendUpgradeLog(`Terminated foreign native-addon holder pid ${holder.pid}`);
@@ -73248,7 +73561,7 @@ ${body}
73248
73561
  }
73249
73562
  function spawnDetachedDaemonUpgradeHelper(payload) {
73250
73563
  const env2 = { ...process.env, [UPGRADE_HELPER_ENV]: JSON.stringify(payload) };
73251
- const child = (0, import_child_process7.spawn)(process.execPath, process.argv.slice(1), {
73564
+ const child = (0, import_child_process8.spawn)(process.execPath, process.argv.slice(1), {
73252
73565
  detached: true,
73253
73566
  stdio: "ignore",
73254
73567
  windowsHide: true,
@@ -73289,11 +73602,25 @@ ${body}
73289
73602
  throw new Error(`portable Node.js 22 npm CLI is missing: ${npmCliPath}`);
73290
73603
  }
73291
73604
  appendUpgradeLog(`Installer-managed pointer layout detected; active prefix will remain untouched: ${windowsInstallerLayout.activePrefix}`);
73605
+ const upgradePids = [process.pid, payload.parentPid].filter((n) => Number.isFinite(n) && n > 0);
73606
+ const preStop = await stopOwnedProcessesForPrefixes({
73607
+ prefixes: [windowsInstallerLayout.activePrefix, windowsInstallerLayout.stablePrefix],
73608
+ excludePids: upgradePids,
73609
+ markers: Array.from(ADHDEV_OWNED_MARKERS),
73610
+ waitMs: 15e3,
73611
+ log: appendUpgradeLog
73612
+ });
73613
+ if (preStop.survivors.length > 0) {
73614
+ throw new Error(
73615
+ `Cannot upgrade: owned processes still running under current prefix: ${preStop.survivors.map((s2) => s2.pid).join(", ")}`
73616
+ );
73617
+ }
73292
73618
  await performWindowsAtomicUpgrade({
73293
73619
  layout: windowsInstallerLayout,
73294
73620
  packageName: payload.packageName,
73295
73621
  targetVersion: payload.targetVersion,
73296
73622
  portableNode,
73623
+ excludePids: upgradePids,
73297
73624
  hooks: createDefaultWindowsAtomicHooks({
73298
73625
  packageName: payload.packageName,
73299
73626
  targetVersion: payload.targetVersion,
@@ -73319,7 +73646,7 @@ ${body}
73319
73646
  let installOutput = "";
73320
73647
  for (let attempt = 1; attempt <= maxInstallAttempts; attempt++) {
73321
73648
  try {
73322
- installOutput = String((0, import_child_process6.execFileSync)(
73649
+ installOutput = String((0, import_child_process7.execFileSync)(
73323
73650
  installCommand.command,
73324
73651
  installCommand.args,
73325
73652
  {
@@ -73372,7 +73699,7 @@ ${body}
73372
73699
  const env2 = { ...process.env };
73373
73700
  delete env2[UPGRADE_HELPER_ENV];
73374
73701
  appendUpgradeLog(`Restarting daemon with args: ${restartArgv.join(" ")}`);
73375
- const child = (0, import_child_process7.spawn)(process.execPath, restartArgv, {
73702
+ const child = (0, import_child_process8.spawn)(process.execPath, restartArgv, {
73376
73703
  detached: true,
73377
73704
  stdio: "ignore",
73378
73705
  windowsHide: true,
@@ -73936,7 +74263,7 @@ ${body}
73936
74263
  var path30 = __toESM2(require("path"));
73937
74264
  var crypto6 = __toESM2(require("crypto"));
73938
74265
  var import_fs16 = require("fs");
73939
- var import_child_process9 = require("child_process");
74266
+ var import_child_process10 = require("child_process");
73940
74267
  var import_chalk = __toESM2((init_source(), __toCommonJS(source_exports)));
73941
74268
  init_provider_cli_adapter();
73942
74269
  init_cli_detector();
@@ -80984,7 +81311,7 @@ ${buttons.join("\n")}`;
80984
81311
  };
80985
81312
  var path29 = __toESM2(require("path"));
80986
81313
  var import_stream = require("stream");
80987
- var import_child_process8 = require("child_process");
81314
+ var import_child_process9 = require("child_process");
80988
81315
  var import_sdk = (init_acp(), __toCommonJS(acp_exports));
80989
81316
  init_contracts2();
80990
81317
  init_provider_input_support();
@@ -81508,7 +81835,7 @@ ${buttons.join("\n")}`;
81508
81835
  this.errorMessage = null;
81509
81836
  this.errorReason = null;
81510
81837
  this.stderrBuffer = [];
81511
- this.process = (0, import_child_process8.spawn)(command, args, {
81838
+ this.process = (0, import_child_process9.spawn)(command, args, {
81512
81839
  cwd: this.workingDir,
81513
81840
  env: env2,
81514
81841
  stdio: ["pipe", "pipe", "pipe"],
@@ -82252,7 +82579,7 @@ ${rawInput}` : rawInput;
82252
82579
  return (0, import_fs16.existsSync)(expandExecutable(trimmed));
82253
82580
  }
82254
82581
  try {
82255
- (0, import_child_process9.execFileSync)(process.platform === "win32" ? "where" : "which", [trimmed], {
82582
+ (0, import_child_process10.execFileSync)(process.platform === "win32" ? "where" : "which", [trimmed], {
82256
82583
  stdio: "ignore",
82257
82584
  ...process.platform === "win32" ? { windowsHide: true } : {}
82258
82585
  });
@@ -83728,7 +84055,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
83728
84055
  return ctx.deps.cliManager.handleCliCommand("restart_session", args);
83729
84056
  }
83730
84057
  };
83731
- var import_child_process10 = require("child_process");
84058
+ var import_child_process11 = require("child_process");
83732
84059
  var net3 = __toESM2(require("net"));
83733
84060
  var os27 = __toESM2(require("os"));
83734
84061
  var path38 = __toESM2(require("path"));
@@ -87626,7 +87953,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
87626
87953
  }
87627
87954
  async function execQuiet(command, options = {}) {
87628
87955
  return new Promise((resolve27) => {
87629
- (0, import_child_process10.exec)(command, options, (error48, stdout) => {
87956
+ (0, import_child_process11.exec)(command, options, (error48, stdout) => {
87630
87957
  if (error48) return resolve27("");
87631
87958
  resolve27(stdout.toString());
87632
87959
  });
@@ -88026,10 +88353,10 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
88026
88353
  const canUseAppLauncher = !!appName;
88027
88354
  const useAppLauncher = preferredMethod === "app" ? canUseAppLauncher : preferredMethod === "cli" ? false : !canUseCli && canUseAppLauncher;
88028
88355
  if (!useAppLauncher && ide.cliCommand) {
88029
- (0, import_child_process10.spawn)(ide.cliCommand, args, { detached: true, stdio: "ignore", windowsHide: true }).unref();
88356
+ (0, import_child_process11.spawn)(ide.cliCommand, args, { detached: true, stdio: "ignore", windowsHide: true }).unref();
88030
88357
  } else if (appName) {
88031
88358
  const openArgs = ["-a", appName, "--args", ...args];
88032
- (0, import_child_process10.spawn)("open", openArgs, { detached: true, stdio: "ignore" }).unref();
88359
+ (0, import_child_process11.spawn)("open", openArgs, { detached: true, stdio: "ignore" }).unref();
88033
88360
  } else {
88034
88361
  throw new Error(`No app identifier or CLI for ${ide.displayName}`);
88035
88362
  }
@@ -88055,7 +88382,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
88055
88382
  const args = ["--remote-debugging-port=" + port];
88056
88383
  if (newWindow) args.push("--new-window");
88057
88384
  if (workspace) args.push(workspace);
88058
- (0, import_child_process10.spawn)(cli, args, { detached: true, stdio: "ignore", windowsHide: true }).unref();
88385
+ (0, import_child_process11.spawn)(cli, args, { detached: true, stdio: "ignore", windowsHide: true }).unref();
88059
88386
  }
88060
88387
  function getAvailableIdeIds() {
88061
88388
  return getProviderLoader().getAvailableIdeTypes();
@@ -88133,15 +88460,15 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
88133
88460
  stop_ide: async (ctx, args) => {
88134
88461
  const ideType = args?.ideType;
88135
88462
  if (!ideType) throw new Error("ideType required");
88136
- const killProcess = args?.killProcess !== false;
88137
- await ctx.stopIde(ideType, killProcess);
88463
+ const killProcess2 = args?.killProcess !== false;
88464
+ await ctx.stopIde(ideType, killProcess2);
88138
88465
  try {
88139
88466
  const results = await detectIDEs(ctx.deps.providerLoader);
88140
88467
  ctx.deps.detectedIdes.value = results;
88141
88468
  ctx.deps.providerLoader.setIdeDetectionResults(results, true);
88142
88469
  } catch {
88143
88470
  }
88144
- return { success: true, ideType, stopped: true, processKilled: killProcess };
88471
+ return { success: true, ideType, stopped: true, processKilled: killProcess2 };
88145
88472
  },
88146
88473
  // ─── IDE restart ───
88147
88474
  restart_ide: async (ctx, args) => {
@@ -91380,18 +91707,18 @@ ${ptyResult.output.slice(-2e3)}`);
91380
91707
  return { enabled: false };
91381
91708
  }
91382
91709
  async function computeGitPatchId(cwd, fromRef, toRef, excludePaths = []) {
91383
- const { execFileSync: execFileSync11 } = await import("child_process");
91710
+ const { execFileSync: execFileSync12 } = await import("child_process");
91384
91711
  const diffArgs = ["diff", "--patch", "--full-index", fromRef, toRef];
91385
91712
  if (excludePaths.length > 0) {
91386
91713
  diffArgs.push("--", ".", ...excludePaths.map((path46) => `:(exclude)${path46}`));
91387
91714
  }
91388
- const diff = execFileSync11(GIT2, diffArgs, {
91715
+ const diff = execFileSync12(GIT2, diffArgs, {
91389
91716
  cwd,
91390
91717
  encoding: "utf8",
91391
91718
  maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
91392
91719
  });
91393
91720
  if (!diff.trim()) return "";
91394
- const patchId = execFileSync11(GIT2, ["patch-id", "--stable"], {
91721
+ const patchId = execFileSync12(GIT2, ["patch-id", "--stable"], {
91395
91722
  cwd,
91396
91723
  input: diff,
91397
91724
  encoding: "utf8",
@@ -91402,8 +91729,8 @@ ${ptyResult.output.slice(-2e3)}`);
91402
91729
  async function runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead) {
91403
91730
  const startedAt = Date.now();
91404
91731
  try {
91405
- const { execFileSync: execFileSync11 } = await import("child_process");
91406
- const git = (args) => execFileSync11(GIT2, args, {
91732
+ const { execFileSync: execFileSync12 } = await import("child_process");
91733
+ const git = (args) => execFileSync12(GIT2, args, {
91407
91734
  cwd: repoRoot,
91408
91735
  encoding: "utf8",
91409
91736
  maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
@@ -91638,8 +91965,8 @@ ${e?.stderr || ""}`
91638
91965
  async function checkWorktreeChangesPatchEquivalentInRef(repoRoot, ref, worktreeHead) {
91639
91966
  const startedAt = Date.now();
91640
91967
  try {
91641
- const { execFileSync: execFileSync11 } = await import("child_process");
91642
- const git = (gitArgs) => execFileSync11(GIT2, gitArgs, {
91968
+ const { execFileSync: execFileSync12 } = await import("child_process");
91969
+ const git = (gitArgs) => execFileSync12(GIT2, gitArgs, {
91643
91970
  cwd: repoRoot,
91644
91971
  encoding: "utf8",
91645
91972
  maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
@@ -91702,8 +92029,8 @@ ${mergeTreeErr?.stderr || ""}`;
91702
92029
  async function runMeshRefineEffectiveDiffGate(repoRoot, baseHead, branchHead) {
91703
92030
  const startedAt = Date.now();
91704
92031
  try {
91705
- const { execFileSync: execFileSync11 } = await import("child_process");
91706
- const git = (args, opts) => execFileSync11(GIT2, args, {
92032
+ const { execFileSync: execFileSync12 } = await import("child_process");
92033
+ const git = (args, opts) => execFileSync12(GIT2, args, {
91707
92034
  cwd: opts?.cwd || repoRoot,
91708
92035
  encoding: "utf8",
91709
92036
  maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
@@ -95798,7 +96125,7 @@ ${e?.stderr || ""}`;
95798
96125
  /**
95799
96126
  * IDE stop: CDP disconnect + InstanceManager cleanup + optionally kill OS process
95800
96127
  */
95801
- async stopIde(ideType, killProcess = false) {
96128
+ async stopIde(ideType, killProcess2 = false) {
95802
96129
  const cdpKeysToRemove = [];
95803
96130
  for (const key2 of this.deps.cdpManagers.keys()) {
95804
96131
  if (key2 === ideType || key2.startsWith(`${ideType}_`)) {
@@ -95836,7 +96163,7 @@ ${e?.stderr || ""}`;
95836
96163
  LOG2.info("StopIDE", `Instance removed: ${instanceKey}`);
95837
96164
  }
95838
96165
  }
95839
- if (killProcess) {
96166
+ if (killProcess2) {
95840
96167
  const running = await isIdeRunning(ideType);
95841
96168
  if (running) {
95842
96169
  LOG2.info("StopIDE", `Killing IDE process: ${ideType}`);
@@ -95851,7 +96178,7 @@ ${e?.stderr || ""}`;
95851
96178
  }
95852
96179
  }
95853
96180
  this.deps.onStatusChange?.();
95854
- LOG2.info("StopIDE", `IDE stopped: ${ideType} (processKill=${killProcess})`);
96181
+ LOG2.info("StopIDE", `IDE stopped: ${ideType} (processKill=${killProcess2})`);
95855
96182
  }
95856
96183
  };
95857
96184
  init_logger();
@@ -97535,7 +97862,7 @@ ${e?.stderr || ""}`;
97535
97862
  var path40 = __toESM2(require("path"));
97536
97863
  var os29 = __toESM2(require("os"));
97537
97864
  var import_os5 = require("os");
97538
- var import_child_process11 = require("child_process");
97865
+ var import_child_process12 = require("child_process");
97539
97866
  var ARCHIVE_PATH = path40.join(os29.homedir(), ".adhdev", "version-history.json");
97540
97867
  var MAX_ENTRIES_PER_PROVIDER = 20;
97541
97868
  var VersionArchive = class {
@@ -97591,7 +97918,7 @@ ${e?.stderr || ""}`;
97591
97918
  };
97592
97919
  async function runCommand(cmd, timeout = 1e4) {
97593
97920
  return new Promise((resolve27) => {
97594
- (0, import_child_process11.exec)(cmd, {
97921
+ (0, import_child_process12.exec)(cmd, {
97595
97922
  encoding: "utf-8",
97596
97923
  timeout
97597
97924
  }, (error48, stdout) => {
@@ -102742,8 +103069,10 @@ data: ${JSON.stringify(msg.data)}
102742
103069
  return;
102743
103070
  }
102744
103071
  if (event.type === "session_exit") {
103072
+ const exitCode = typeof event.exitCode === "number" ? event.exitCode : null;
103073
+ const signal = typeof event.signal === "number" ? event.signal : null;
102745
103074
  for (const callback of this.exitCallbacks) {
102746
- callback({ exitCode: event.exitCode ?? 0 });
103075
+ callback({ exitCode, signal });
102747
103076
  }
102748
103077
  void this.closeClient(false);
102749
103078
  }
@@ -103122,11 +103451,12 @@ data: ${JSON.stringify(msg.data)}
103122
103451
  });
103123
103452
  }
103124
103453
  }
103125
- var import_child_process12 = require("child_process");
103454
+ var import_child_process13 = require("child_process");
103126
103455
  var fs43 = __toESM2(require("fs"));
103127
103456
  var os31 = __toESM2(require("os"));
103128
103457
  var path45 = __toESM2(require("path"));
103129
103458
  var import_session_host_core13 = require_dist();
103459
+ init_logger();
103130
103460
  init_runtime_defaults();
103131
103461
  function createManagedSessionHost2(options) {
103132
103462
  const appName = options.appName;
@@ -103150,6 +103480,14 @@ data: ${JSON.stringify(msg.data)}
103150
103480
  }
103151
103481
  return require.resolve("@adhdev/session-host-daemon");
103152
103482
  }
103483
+ function pathsEquivalent(left, right) {
103484
+ return path45.resolve(left).toLowerCase() === path45.resolve(right).toLowerCase();
103485
+ }
103486
+ function getRunningSessionHostScriptPath(pid) {
103487
+ const commandLine = getProcessCommandLine(pid);
103488
+ if (!commandLine || !/session-host-daemon/i.test(commandLine)) return null;
103489
+ return parseNodeScriptPath(commandLine);
103490
+ }
103153
103491
  function getPidFile() {
103154
103492
  return path45.join(os31.homedir(), ".adhdev", `${appName}-session-host.pid`);
103155
103493
  }
@@ -103163,12 +103501,12 @@ data: ${JSON.stringify(msg.data)}
103163
103501
  return null;
103164
103502
  }
103165
103503
  }
103166
- function killPid2(pid) {
103504
+ function killPid(pid) {
103167
103505
  try {
103168
103506
  if (process.platform === "win32") {
103169
103507
  const spawnOpts = { stdio: "ignore" };
103170
103508
  if (options.killWindowsHide) spawnOpts.windowsHide = true;
103171
- (0, import_child_process12.execFileSync)("taskkill", ["/PID", String(pid), "/T", "/F"], spawnOpts);
103509
+ (0, import_child_process13.execFileSync)("taskkill", ["/PID", String(pid), "/T", "/F"], spawnOpts);
103172
103510
  } else {
103173
103511
  process.kill(pid, "SIGTERM");
103174
103512
  }
@@ -103187,7 +103525,7 @@ data: ${JSON.stringify(msg.data)}
103187
103525
  logFd = fs43.openSync(path45.join(logDir, "session-host.log"), "a");
103188
103526
  stdio = ["ignore", logFd, logFd];
103189
103527
  }
103190
- const child = (0, import_child_process12.spawn)(process.execPath, [entry], {
103528
+ const child = (0, import_child_process13.spawn)(process.execPath, [entry], {
103191
103529
  detached: true,
103192
103530
  stdio,
103193
103531
  windowsHide: true,
@@ -103208,7 +103546,7 @@ data: ${JSON.stringify(msg.data)}
103208
103546
  if (fs43.existsSync(pidFile)) {
103209
103547
  const pid = Number.parseInt(fs43.readFileSync(pidFile, "utf8").trim(), 10);
103210
103548
  if (Number.isFinite(pid) && pid !== process.pid && isManagedPid(pid)) {
103211
- stopped = killPid2(pid) || stopped;
103549
+ stopped = killPid(pid) || stopped;
103212
103550
  }
103213
103551
  }
103214
103552
  } catch {
@@ -103225,6 +103563,20 @@ data: ${JSON.stringify(msg.data)}
103225
103563
  }
103226
103564
  async function ensureReady() {
103227
103565
  options.beforeEnsureReady?.();
103566
+ if (process.platform === "win32") {
103567
+ const existingPid = getPid();
103568
+ if (existingPid !== null) {
103569
+ const runningPath = getRunningSessionHostScriptPath(existingPid);
103570
+ const currentEntry = resolveEntry();
103571
+ if (runningPath && !pathsEquivalent(runningPath, currentEntry)) {
103572
+ LOG2.warn(
103573
+ "SessionHost",
103574
+ `Detected stale host pid ${existingPid} running from ${runningPath}; restarting from ${currentEntry}`
103575
+ );
103576
+ stopManagedSessionHostProcess();
103577
+ }
103578
+ }
103579
+ }
103228
103580
  try {
103229
103581
  return await ensureSessionHostReady2({
103230
103582
  appName,
@@ -103253,7 +103605,7 @@ data: ${JSON.stringify(msg.data)}
103253
103605
  getPid,
103254
103606
  buildEnv,
103255
103607
  resolveEntry,
103256
- killPid: killPid2,
103608
+ killPid,
103257
103609
  spawnHost,
103258
103610
  stopManagedSessionHostProcess,
103259
103611
  ensureReady,
@@ -103269,7 +103621,7 @@ data: ${JSON.stringify(msg.data)}
103269
103621
  if (raw === "0" || raw === "false" || raw === "no") return false;
103270
103622
  return raw === "1" || raw === "true" || raw === "yes";
103271
103623
  }
103272
- var import_child_process13 = require("child_process");
103624
+ var import_child_process14 = require("child_process");
103273
103625
  var import_util32 = require("util");
103274
103626
  var EXTENSION_CATALOG = [
103275
103627
  // AI Agent extensions
@@ -103357,7 +103709,7 @@ data: ${JSON.stringify(msg.data)}
103357
103709
  apiKeyName: "OpenAI/Anthropic API key"
103358
103710
  }
103359
103711
  ];
103360
- var execAsync4 = (0, import_util32.promisify)(import_child_process13.exec);
103712
+ var execAsync4 = (0, import_util32.promisify)(import_child_process14.exec);
103361
103713
  async function isExtensionInstalled(ide, marketplaceId) {
103362
103714
  if (!ide.cliCommand) return false;
103363
103715
  try {
@@ -103401,7 +103753,7 @@ data: ${JSON.stringify(msg.data)}
103401
103753
  fs44.writeFileSync(vsixPath, buffer);
103402
103754
  return new Promise((resolve27) => {
103403
103755
  const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
103404
- (0, import_child_process13.exec)(cmd, { timeout: 6e4 }, (error48, _stdout, stderr) => {
103756
+ (0, import_child_process14.exec)(cmd, { timeout: 6e4 }, (error48, _stdout, stderr) => {
103405
103757
  resolve27({
103406
103758
  extensionId: extension.id,
103407
103759
  marketplaceId: extension.marketplaceId,
@@ -103417,7 +103769,7 @@ data: ${JSON.stringify(msg.data)}
103417
103769
  }
103418
103770
  return new Promise((resolve27) => {
103419
103771
  const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
103420
- (0, import_child_process13.exec)(cmd, { timeout: 6e4 }, (error48, stdout, stderr) => {
103772
+ (0, import_child_process14.exec)(cmd, { timeout: 6e4 }, (error48, stdout, stderr) => {
103421
103773
  if (error48) {
103422
103774
  resolve27({
103423
103775
  extensionId: extension.id,
@@ -103454,7 +103806,7 @@ data: ${JSON.stringify(msg.data)}
103454
103806
  if (!ide.cliCommand) return false;
103455
103807
  try {
103456
103808
  const args = workspacePath ? `"${workspacePath}"` : "";
103457
- (0, import_child_process13.exec)(`"${ide.cliCommand}" ${args}`, { timeout: 1e4 });
103809
+ (0, import_child_process14.exec)(`"${ide.cliCommand}" ${args}`, { timeout: 1e4 });
103458
103810
  return true;
103459
103811
  } catch {
103460
103812
  return false;