@adhdev/daemon-core 1.0.18-rc.20 → 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.
Files changed (74) hide show
  1. package/dist/cli-adapters/provider-cli-shared.d.ts +0 -33
  2. package/dist/commands/cli-manager.d.ts +0 -9
  3. package/dist/commands/upgrade-helper.d.ts +0 -1
  4. package/dist/commands/windows-atomic-upgrade.d.ts +0 -5
  5. package/dist/config/mesh-json-config.d.ts +0 -62
  6. package/dist/index.d.ts +2 -4
  7. package/dist/index.js +3998 -6112
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +3855 -5966
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/mesh/coordinator-prompt.d.ts +0 -76
  12. package/dist/mesh/mesh-active-work.d.ts +1 -1
  13. package/dist/mesh/mesh-ledger.d.ts +1 -28
  14. package/dist/mesh/mesh-node-identity.d.ts +0 -23
  15. package/dist/mesh/mesh-refine-gates.d.ts +0 -89
  16. package/dist/mesh/mesh-remote-event-pull.d.ts +0 -3
  17. package/dist/mesh/mesh-runtime-store.d.ts +0 -11
  18. package/dist/mesh/mesh-work-queue.d.ts +1 -24
  19. package/dist/providers/chat-message-normalization.d.ts +0 -22
  20. package/dist/providers/cli-provider-instance-types.d.ts +0 -4
  21. package/dist/providers/cli-provider-instance.d.ts +0 -78
  22. package/dist/providers/contracts.d.ts +0 -17
  23. package/dist/providers/provider-schema.d.ts +0 -1
  24. package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +0 -14
  25. package/dist/providers/types/interactive-prompt.d.ts +0 -18
  26. package/dist/repo-mesh-types.d.ts +6 -38
  27. package/dist/shared-types.d.ts +2 -4
  28. package/package.json +3 -3
  29. package/src/boot/daemon-lifecycle.ts +0 -10
  30. package/src/cli-adapters/cli-state-engine.ts +1 -17
  31. package/src/cli-adapters/provider-cli-adapter.ts +6 -2
  32. package/src/cli-adapters/provider-cli-shared.ts +0 -48
  33. package/src/commands/cli-manager.ts +8 -72
  34. package/src/commands/high-family/mesh-coordinator-launch.ts +2 -17
  35. package/src/commands/high-family/mesh-events.ts +2 -13
  36. package/src/commands/med-family/mesh-crud.ts +0 -155
  37. package/src/commands/med-family/mesh-queue.ts +1 -74
  38. package/src/commands/router-refine.ts +4 -71
  39. package/src/commands/router.ts +0 -8
  40. package/src/commands/upgrade-helper.ts +15 -78
  41. package/src/commands/windows-atomic-upgrade.ts +40 -194
  42. package/src/config/mesh-json-config.ts +0 -111
  43. package/src/index.ts +1 -21
  44. package/src/mesh/coordinator-prompt.ts +11 -258
  45. package/src/mesh/mesh-active-work.ts +1 -11
  46. package/src/mesh/mesh-completion-synthesis.ts +1 -12
  47. package/src/mesh/mesh-event-classify.ts +0 -19
  48. package/src/mesh/mesh-event-forwarding.ts +6 -64
  49. package/src/mesh/mesh-events-utils.ts +0 -42
  50. package/src/mesh/mesh-ledger.ts +0 -77
  51. package/src/mesh/mesh-node-identity.ts +0 -49
  52. package/src/mesh/mesh-queue-assignment.ts +11 -210
  53. package/src/mesh/mesh-reconcile-loop.ts +3 -306
  54. package/src/mesh/mesh-refine-gates.ts +0 -300
  55. package/src/mesh/mesh-remote-event-pull.ts +47 -68
  56. package/src/mesh/mesh-runtime-store.ts +0 -21
  57. package/src/mesh/mesh-work-queue.ts +2 -85
  58. package/src/providers/chat-message-normalization.ts +0 -53
  59. package/src/providers/cli-provider-instance-types.ts +0 -53
  60. package/src/providers/cli-provider-instance.ts +15 -497
  61. package/src/providers/contracts.ts +1 -25
  62. package/src/providers/provider-schema.ts +0 -83
  63. package/src/providers/sdk/v1/builders/cli/detect-status.ts +0 -23
  64. package/src/providers/sdk/v1/builders/cli/parse-approval.ts +0 -20
  65. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +0 -44
  66. package/src/providers/types/interactive-prompt.ts +0 -77
  67. package/src/repo-mesh-types.ts +12 -112
  68. package/src/shared-types.ts +1 -9
  69. package/src/status/reporter.ts +0 -1
  70. package/src/status/snapshot.ts +0 -2
  71. package/dist/mesh/mesh-disk-retention.d.ts +0 -105
  72. package/dist/providers/auto-approve-modes.d.ts +0 -14
  73. package/src/mesh/mesh-disk-retention.ts +0 -370
  74. package/src/providers/auto-approve-modes.ts +0 -103
@@ -9,67 +9,6 @@
9
9
  */
10
10
  import { readStringValue } from '../router.js';
11
11
  import type { MedFamilyContext, MedFamilyHandler } from './types.js';
12
- import { LOG } from '../../logging/logger.js';
13
- import type { CancelledTaskAssignment } from '../../mesh/mesh-work-queue.js';
14
-
15
- /**
16
- * CANCEL-STICKY-TERMINAL (authoritative cancel): stop the worker a just-cancelled task was
17
- * bound to. Mirrors the transport-aware stop mesh-event-forwarding's stopStaleMeshWorker
18
- * performs for reclaimed workers, but runs from the command layer using ctx.deps (which the
19
- * pure queue-store module lacks): local adapter → stop_cli directly; otherwise resolve the
20
- * worker node's daemon id from mesh config and dispatch stop_cli over P2P/relay. Fully
21
- * best-effort — a failed stop only loses the belt-and-suspenders; the cancelled row is already
22
- * terminal and cannot be resurrected (updateTaskStatus terminal guard).
23
- */
24
- async function stopCancelledTaskWorker(
25
- ctx: MedFamilyContext,
26
- meshId: string,
27
- prior: CancelledTaskAssignment,
28
- ): Promise<void> {
29
- const { sessionId, nodeId, providerType } = prior;
30
- const stopArgs: Record<string, unknown> = {
31
- targetSessionId: sessionId,
32
- ...(providerType ? { cliType: providerType } : {}),
33
- mode: 'hard',
34
- reason: 'mesh_task_cancelled',
35
- };
36
- try {
37
- const cliManager = ctx.deps.cliManager as any;
38
- const isLocal = cliManager?.adapters?.has?.(sessionId) === true;
39
- if (isLocal) {
40
- if (!stopArgs.cliType) {
41
- const localType = cliManager?.adapters?.get?.(sessionId)?.cliType;
42
- if (localType) stopArgs.cliType = localType;
43
- }
44
- await Promise.resolve(cliManager?.handleCliCommand?.('stop_cli', stopArgs))
45
- .catch((e: any) => LOG.warn('MeshQueue', `Local stop of cancelled worker ${sessionId} failed: ${e?.message || e}`));
46
- return;
47
- }
48
- // Remote: resolve the worker node's daemon id from mesh config, then dispatch stop_cli.
49
- const dispatch = ctx.deps.dispatchMeshCommand;
50
- let daemonId: string | undefined;
51
- if (nodeId) {
52
- try {
53
- const [{ getMesh }, { meshNodeIdMatches }, { readMeshNodeDaemonId }] = await Promise.all([
54
- import('../../config/mesh-config.js'),
55
- import('@adhdev/mesh-shared'),
56
- import('../../mesh/mesh-node-identity.js'),
57
- ]);
58
- const mesh = getMesh(meshId) as { nodes?: any[] } | undefined;
59
- const node = mesh?.nodes?.find((n: any) => meshNodeIdMatches(n, nodeId));
60
- daemonId = node ? readMeshNodeDaemonId(node) || undefined : undefined;
61
- } catch { /* best-effort resolution */ }
62
- }
63
- if (daemonId && dispatch) {
64
- await Promise.resolve(dispatch(daemonId, 'stop_cli', stopArgs))
65
- .catch((e: any) => LOG.warn('MeshQueue', `Remote stop of cancelled worker ${sessionId} on daemon ${daemonId} failed: ${e?.message || e}`));
66
- } else {
67
- LOG.warn('MeshQueue', `Cannot stop cancelled worker ${sessionId}: no local adapter and no resolvable remote daemon id (node ${nodeId ?? '?'}). Row is already terminal; if the worker completes it strand-fails harmlessly.`);
68
- }
69
- } catch (e: any) {
70
- LOG.warn('MeshQueue', `stopCancelledTaskWorker error for ${sessionId}: ${e?.message || e}`);
71
- }
72
- }
73
12
 
74
13
  export const meshQueueHandlers: Record<string, MedFamilyHandler> = {
75
14
  get_mesh_queue: async (_ctx: MedFamilyContext, args: any) => {
@@ -111,22 +50,10 @@ export const meshQueueHandlers: Record<string, MedFamilyHandler> = {
111
50
  const ownerFailure = await ctx.requireMeshHostMutationOwner(meshId, args?.inlineMesh, 'queue cancellation');
112
51
  if (ownerFailure) return ownerFailure;
113
52
  try {
114
- const { cancelTask, takeCancelledTaskAssignment } = await import('../../mesh/mesh-work-queue.js');
53
+ const { cancelTask } = await import('../../mesh/mesh-work-queue.js');
115
54
  const reason = typeof args?.reason === 'string' ? args.reason : undefined;
116
55
  const task = cancelTask(meshId, taskId, { reason });
117
56
  if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
118
- // CANCEL-STICKY-TERMINAL (authoritative cancel): stop the worker that was bound to the
119
- // now-cancelled task. cancelTask already cleared the queue's assignment fields (so the
120
- // reclaim watchdog no longer sees it as a live assignment), but a still-running worker
121
- // keeps emitting delivery/turn signals that re-ignite reclaim. cancelTask runs in the
122
- // pure queue-store module (no DaemonComponents), so the transport-aware stop is done
123
- // here where ctx.deps holds cliManager / dispatchMeshCommand. Best-effort: a failed
124
- // stop only loses the belt-and-suspenders — the row is already terminal and un-revivable
125
- // thanks to the updateTaskStatus terminal guard.
126
- const prior = takeCancelledTaskAssignment(meshId, taskId);
127
- if (prior?.sessionId) {
128
- void stopCancelledTaskWorker(ctx, meshId, prior);
129
- }
130
57
  return { success: true, task };
131
58
  } catch (e: any) {
132
59
  return { success: false, error: e.message };
@@ -36,10 +36,8 @@ import {
36
36
  RefineExecFileAsync,
37
37
  RefineStageOutcome,
38
38
  classifyPatchEquivalenceFailure,
39
- convergeDivergedSubmoduleGitlinks,
40
39
  recordMeshRefineStage,
41
40
  resolveRefineryAutoPublishSubmoduleMainCommits,
42
- rootRebaseResolvingGitlinks,
43
41
  runMeshRefineEffectiveDiffGate,
44
42
  runMeshRefinePatchEquivalenceGate,
45
43
  runMeshRefineSubmoduleReachabilityGate,
@@ -541,9 +539,6 @@ async function computeBranchBaseDivergence(
541
539
  export async function refineSyncBaseStage(self: DaemonCommandRouter, ctx: RefineContext): Promise<RefineStageOutcome> {
542
540
  const { repoRoot, baseHead, node, branch, baseBranch, refineStages, execFileAsync } = ctx;
543
541
  let branchHead = ctx.branchHead;
544
- // Converged submodule gitlink resolutions (path → rebased commit) from STEP 1;
545
- // consumed by the gitlink-aware root rebase (STEP 2) to resolve gitlink conflicts.
546
- let gitlinkResolutions: Array<{ path: string; rebasedCommit: string }> = [];
547
542
  const syncStarted = Date.now();
548
543
  const divergence = await computeBranchBaseDivergence(execFileAsync, node.workspace, baseHead, branchHead);
549
544
 
@@ -576,87 +571,25 @@ export async function refineSyncBaseStage(self: DaemonCommandRouter, ctx: Refine
576
571
  const preRebasePe = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
577
572
  const alreadyMerged = !preRebasePe.actualPatchId && !!preRebasePe.expectedPatchId;
578
573
  const submoduleConflict = preRebasePe.actionableHint?.kind === 'submodule_conflict';
579
- if (alreadyMerged) {
574
+ if (alreadyMerged || submoduleConflict) {
580
575
  recordMeshRefineStage(refineStages, 'sync_base', 'passed', syncStarted, {
581
576
  ahead: divergence.ahead,
582
577
  behind: divergence.behind,
583
578
  rebased: false,
584
- reason: 'already_merged_via_other_path_skip_rebase',
579
+ reason: alreadyMerged ? 'already_merged_via_other_path_skip_rebase' : 'submodule_conflict_defer_to_patch_equivalence',
585
580
  });
586
581
  return { kind: 'continue', ctx };
587
582
  }
588
- if (submoduleConflict) {
589
- // DS3: a "submodule conflict" here means base and branch advanced the
590
- // SAME submodule to DIVERGED sibling commits (neither an ancestor of the
591
- // other), so the gitlink stays in the diff and patch-equivalence fails.
592
- // Attempt to auto-converge it (STEP 1): rebase the branch-side submodule
593
- // commit onto the base-side commit INSIDE the worktree submodule, so the
594
- // base-side commit becomes a strict ancestor of the rebased tip. The root
595
- // rebase below (STEP 2) then resolves the gitlink conflict to that rebased
596
- // commit. Together this automates the documented manual strict-ff bypass
597
- // and keeps the landed oss history linear. On any real submodule content
598
- // conflict it backs out cleanly → we FALL BACK to the historical
599
- // defer→patch_equivalence path below.
600
- const converge = convergeDivergedSubmoduleGitlinks(node.workspace, repoRoot, baseHead, branchHead);
601
- if (converge.converged) {
602
- gitlinkResolutions = converge.resolutions;
603
- recordMeshRefineStage(refineStages, 'submodule_gitlink_converge', 'passed', syncStarted, {
604
- reason: 'submodule_diverged_auto_rebased',
605
- gitlinks: converge.gitlinks,
606
- });
607
- LOG.info('Mesh', `[Refinery] Auto-converged diverged submodule gitlink(s) onto base for node ${node.id}: `
608
- + converge.resolutions.map(r => `${r.path}→${r.rebasedCommit.slice(0, 12)}`).join(', '));
609
- // Fall through (do NOT return) → the gitlink-aware root rebase below runs.
610
- } else {
611
- // Fail-safe: convergence declined (conflict / unreachable / not a real
612
- // divergence) → preserve the historical defer→blocked_review behavior.
613
- recordMeshRefineStage(refineStages, 'submodule_gitlink_converge', 'skipped', syncStarted, {
614
- reason: 'submodule_conflict_defer_to_patch_equivalence',
615
- convergeReason: converge.reason,
616
- gitlinks: converge.gitlinks,
617
- });
618
- recordMeshRefineStage(refineStages, 'sync_base', 'passed', syncStarted, {
619
- ahead: divergence.ahead,
620
- behind: divergence.behind,
621
- rebased: false,
622
- reason: 'submodule_conflict_defer_to_patch_equivalence',
623
- });
624
- return { kind: 'continue', ctx };
625
- }
626
- }
627
583
  } catch { /* fail-open: on gate error, fall through to the rebase */ }
628
584
 
629
585
  // behind>0: strictly-behind OR diverged. Rebase the branch onto the pinned
630
586
  // baseHead. A conflict aborts and terminates blocked_review (retryable=false —
631
587
  // a real content conflict needs human resolution, not a base-movement retry).
632
- //
633
- // When STEP 1 converged a diverged submodule gitlink, use the gitlink-aware
634
- // root rebase (STEP 2): git's recursive merge refuses to auto-merge the still-
635
- // diverged intermediate gitlink, so we drive the rebase and resolve each
636
- // submodule-gitlink conflict to the converged commit. A non-gitlink conflict
637
- // aborts and falls through to the same blocked_review handling as a plain
638
- // rebase conflict below (via the thrown gitlinkRebaseError).
639
588
  const rebaseStarted = Date.now();
640
589
  try {
641
- if (gitlinkResolutions.length > 0) {
642
- const gitlinkRebase = rootRebaseResolvingGitlinks(node.workspace, baseHead, gitlinkResolutions);
643
- if (!gitlinkRebase.ok) {
644
- // Surface as a rebase failure so the shared blocked_review handling
645
- // (submodule-hint recovery included) runs — nothing was left mid-rebase
646
- // (rootRebaseResolvingGitlinks aborts on failure).
647
- const err: any = new Error(`gitlink-aware rebase aborted: ${gitlinkRebase.reason || 'unknown'}`);
648
- err.gitlinkRebaseReason = gitlinkRebase.reason;
649
- err.gitlinkRebaseConflicts = gitlinkRebase.conflictPaths;
650
- err.alreadyAborted = true;
651
- throw err;
652
- }
653
- } else {
654
- execFileSync('git', ['rebase', baseHead], { cwd: node.workspace, stdio: ['ignore', 'pipe', 'pipe'] });
655
- }
590
+ execFileSync('git', ['rebase', baseHead], { cwd: node.workspace, stdio: ['ignore', 'pipe', 'pipe'] });
656
591
  } catch (rebaseErr: any) {
657
- if (!rebaseErr?.alreadyAborted) {
658
- try { execFileSync('git', ['rebase', '--abort'], { cwd: node.workspace, stdio: 'ignore' }); } catch { /* ignore */ }
659
- }
592
+ try { execFileSync('git', ['rebase', '--abort'], { cwd: node.workspace, stdio: 'ignore' }); } catch { /* ignore */ }
660
593
  // A rebase conflict on a submodule/gitlink divergence is a SPECIAL case the
661
594
  // patch-equivalence gate describes with a rich actionable hint (which
662
595
  // submodule, base vs branch commit, how to resolve). Run that gate against
@@ -227,14 +227,6 @@ const MESH_FORWARDABLE_SESSION_COMMANDS = new Set([
227
227
  // MUTATES the worker PTY, so forwarding to the real owner (not a wrong local session) is
228
228
  // doubly important. The daemon re-enforces the destructive-key confirm gate after the forward.
229
229
  'send_keys',
230
- // interactive_prompt_response (mesh_answer_question, mission f1d25e11): the coordinator
231
- // answers a REMOTE worker's AskUserQuestion (waiting_choice). The answer must reach the
232
- // OWNING worker session's live instance — its activeInteractivePrompt (the authoritative
233
- // prompt the labels/indexes resolve against) and its adapter.setInteractivePromptResponse
234
- // live only there. Without forwarding, the coordinator's local high-family handler returns
235
- // 'No running instance for session …' and the question is never answered — the exact
236
- // remote-worker forwarding gap of mission 6938892f, now closed for questions too.
237
- 'interactive_prompt_response',
238
230
  ]);
239
231
 
240
232
  function normalizeCommandSource(source: string): CommandLogEntry['source'] {
@@ -144,68 +144,19 @@ function resolveInstallPrefixFromPackageRoot(packageRoot: string, packageName: s
144
144
  return maybeLibDir;
145
145
  }
146
146
 
147
- // True when `prefix` is the bin dir of a portable Node 22 the installer manages
148
- // under ~/.adhdev/tools/node22/<node-vX>/. A `npm i -g adhdev` run while that
149
- // portable node is the active `node` installs adhdev into node's own default
150
- // global prefix (= that dir) — a "legacy node22-prefix" install that lives at
151
- // the FRONT of PATH (Enable-NodePath prepends node22) and shadows the canonical
152
- // dispatcher shims in ~/.adhdev/npm-global. Because self-upgrade reuses the
153
- // running prefix, that install then re-installs into the same node22 dir forever
154
- // and never converts to the dispatcher. Detecting it lets us force convergence.
155
- function isPortableNode22Prefix(prefix: string | null, homeDir: string): boolean {
156
- if (!prefix) return false;
157
- const portableRoot = path.join(homeDir, '.adhdev', 'tools', 'node22');
158
- const normalizedPrefix = path.resolve(prefix).replace(/[\\/]+$/, '').toLowerCase();
159
- const normalizedRoot = path.resolve(portableRoot).replace(/[\\/]+$/, '').toLowerCase();
160
- return normalizedPrefix === normalizedRoot || normalizedPrefix.startsWith(`${normalizedRoot}${path.sep.toLowerCase()}`);
161
- }
162
-
163
- // Redirect a legacy node22-prefix install to the canonical dispatcher install
164
- // root so resolveWindowsInstallerLayout accepts it and the atomic-upgrade
165
- // publishes the ~/.adhdev/npm-global pointer + shims. Prefer the version the
166
- // dispatcher pointer already names (so we sit on the real active dispatcher
167
- // prefix); otherwise synthesize a stable migration sentinel under npm-installs.
168
- // resolveWindowsInstallerLayout only requires a `npm-installs/version-*` path —
169
- // performWindowsAtomicUpgrade stages a fresh version- prefix of its own and uses
170
- // this only as the "old prefix" to stop/clean, so a non-existent path is a no-op.
171
- function canonicalDispatcherInstallPrefix(homeDir: string): string {
172
- const installRoot = path.join(homeDir, '.adhdev', 'npm-installs');
173
- const pointerPath = path.join(homeDir, '.adhdev', 'npm-global', '.adhdev-current');
174
- try {
175
- const activeVersion = fs.readFileSync(pointerPath, 'utf8').trim();
176
- if (activeVersion.startsWith('version-')) return path.join(installRoot, activeVersion);
177
- } catch {
178
- // No dispatcher pointer yet (first migration off the legacy layout).
179
- }
180
- return path.join(installRoot, 'version-legacy-migrate');
181
- }
182
-
183
147
  export function resolveCurrentGlobalInstallSurface(options: {
184
148
  packageName: string;
185
149
  currentCliPath?: string;
186
150
  nodeExecutable?: string;
187
151
  platform?: NodeJS.Platform;
188
- homeDir?: string;
189
152
  }): CurrentGlobalInstallSurface {
190
153
  const packageRoot = findCurrentPackageRoot(options.currentCliPath || process.argv[1], options.packageName);
191
154
  const npmInvocation = resolveSiblingNpmInvocation(options.nodeExecutable || process.execPath, options.platform);
192
- const platform = options.platform || process.platform;
193
- const homeDir = options.homeDir || os.homedir();
194
- let installPrefix = packageRoot ? resolveInstallPrefixFromPackageRoot(packageRoot, options.packageName) : null;
195
- // FIX C: on Windows, never let a self-upgrade perpetuate the legacy
196
- // node22-prefix install. If the running adhdev lives under ~/.adhdev/tools/
197
- // node22, force the install onto the canonical dispatcher prefix so the update
198
- // converges to the ~/.adhdev/npm-global pointer + shims. Scoped to win32 AND a
199
- // tools/node22 prefix so npm-linked dev / standalone / real dispatcher installs
200
- // are untouched.
201
- if (platform === 'win32' && isPortableNode22Prefix(installPrefix, homeDir)) {
202
- installPrefix = canonicalDispatcherInstallPrefix(homeDir);
203
- }
204
155
  return {
205
156
  npmExecutable: npmInvocation.executable,
206
157
  npmArgsPrefix: npmInvocation.argsPrefix,
207
158
  packageRoot,
208
- installPrefix,
159
+ installPrefix: packageRoot ? resolveInstallPrefixFromPackageRoot(packageRoot, options.packageName) : null,
209
160
  execOptions: npmInvocation.execOptions,
210
161
  };
211
162
  }
@@ -609,36 +560,22 @@ async function runDaemonUpgradeHelper(payload: DaemonUpgradeHelperPayload): Prom
609
560
  );
610
561
  }
611
562
 
612
- try {
613
- await performWindowsAtomicUpgrade({
614
- layout: windowsInstallerLayout,
563
+ await performWindowsAtomicUpgrade({
564
+ layout: windowsInstallerLayout,
565
+ packageName: payload.packageName,
566
+ targetVersion: payload.targetVersion,
567
+ portableNode,
568
+ excludePids: upgradePids,
569
+ hooks: createDefaultWindowsAtomicHooks({
615
570
  packageName: payload.packageName,
616
571
  targetVersion: payload.targetVersion,
617
- portableNode,
618
- excludePids: upgradePids,
619
- hooks: createDefaultWindowsAtomicHooks({
620
- packageName: payload.packageName,
621
- targetVersion: payload.targetVersion,
622
- npmCliPath,
623
- restartArgv,
624
- cwd: payload.cwd || process.cwd(),
625
- env: Object.fromEntries(Object.entries(process.env).filter(([key]) => key !== UPGRADE_HELPER_ENV)),
626
- log: appendUpgradeLog,
627
- }),
628
- });
629
- } catch (error: any) {
630
- // performWindowsAtomicUpgrade already rolled the pointer/shims back to the
631
- // prior version before rethrowing. Without a durable notice that rollback
632
- // was silent — the daemon simply kept running the old version (the rc.6
633
- // stuck-upgrade defect). Leave an actionable last-error file naming the
634
- // target that failed its health/version gate.
635
- emitUpgradeFailureNotice([
636
- `adhdev ${payload.packageName}@${payload.targetVersion} upgrade failed and was rolled back: ${error?.message || String(error)}`,
637
- `Previous version preserved (active prefix: ${windowsInstallerLayout.activePrefix}).`,
638
- 'See daemon-upgrade.log for the full install/health trace. The next daemon start will retry.',
639
- ]);
640
- throw error;
641
- }
572
+ npmCliPath,
573
+ restartArgv,
574
+ cwd: payload.cwd || process.cwd(),
575
+ env: Object.fromEntries(Object.entries(process.env).filter(([key]) => key !== UPGRADE_HELPER_ENV)),
576
+ log: appendUpgradeLog,
577
+ }),
578
+ });
642
579
  try { fs.unlinkSync(getUpgradeFailureNoticePath()); } catch { /* no previous failure notice */ }
643
580
  appendUpgradeLog('Installer-managed Windows atomic upgrade completed');
644
581
  return;