@adhdev/daemon-core 1.0.18-rc.20 → 1.0.18-rc.4

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
@@ -8,53 +8,6 @@ const POINTER_NAME = '.adhdev-current';
8
8
  const STABLE_FILES = [POINTER_NAME, 'adhdev.cmd', 'adhdev.ps1', 'adhdev'] as const;
9
9
  const DEFAULT_HEALTH_PORT = 19222;
10
10
 
11
- // How long to wait for the replacement daemon to report the target version
12
- // before deterministically rolling back. status.version only appears AFTER the
13
- // daemon fully boots its components — a separate session-host process, node-pty/
14
- // conpty, CDP, and providers — which on a Windows cold self-upgrade routinely
15
- // exceeds the old 30s ceiling. Live Windows logs showed every rollback clustering
16
- // at 33–35s (30s timeout + poll overhead), so the gate was tripping on slow-boot,
17
- // not on genuine failure. 120s reflects real Windows self-upgrade cold-start time.
18
- export const DEFAULT_HEALTH_TIMEOUT_MS = 120_000;
19
-
20
- function fetchLocalJson(port: number, pathname: string): Promise<{ ok: boolean; body: string }> {
21
- return new Promise((resolve) => {
22
- const req = http.get(`http://127.0.0.1:${port}${pathname}`, { timeout: 1500 }, (res) => {
23
- let body = '';
24
- res.setEncoding('utf8');
25
- res.on('data', (chunk) => { body += chunk; });
26
- res.on('end', () => resolve({ ok: res.statusCode === 200, body }));
27
- });
28
- req.on('timeout', () => req.destroy());
29
- req.on('error', () => resolve({ ok: false, body: '' }));
30
- });
31
- }
32
-
33
- // Liveness + pid identity: GET /health returns {ok, pid, wsPath, port}.
34
- async function fetchLocalHealth(port: number): Promise<{ ok: boolean; pid?: number }> {
35
- const { ok, body } = await fetchLocalJson(port, '/health');
36
- if (!ok) return { ok: false };
37
- try {
38
- const pid = Number(JSON.parse(body)?.pid);
39
- return { ok: true, pid: Number.isFinite(pid) ? pid : undefined };
40
- } catch {
41
- return { ok: false };
42
- }
43
- }
44
-
45
- // Running version: GET /api/v1/status exposes it at payload.status.version.
46
- // /health carries no version, so the upgrade version gate must read this.
47
- async function fetchLocalStatusVersion(port: number): Promise<string | undefined> {
48
- const { ok, body } = await fetchLocalJson(port, '/api/v1/status');
49
- if (!ok) return undefined;
50
- try {
51
- const version = (JSON.parse(body) as { status?: { version?: unknown } })?.status?.version;
52
- return typeof version === 'string' ? version : undefined;
53
- } catch {
54
- return undefined;
55
- }
56
- }
57
-
58
11
  // Markers that identify a Node process as ADHDev-owned when its command line
59
12
  // also lives under a versioned install prefix. This deliberately excludes
60
13
  // arbitrary user scripts that happen to be located under ~/.adhdev.
@@ -161,28 +114,6 @@ function packageRootForPrefix(prefix: string, packageName: string): string {
161
114
  return path.join(prefix, 'node_modules', ...packageName.split('/'));
162
115
  }
163
116
 
164
- // node-pty ships a Windows x64 prebuild. If npm rebuilds from source (because a
165
- // .npmrc sets build-from-source=true), the install script deletes the prebuild
166
- // and may leave no conpty.node on machines without build tools. Verify it
167
- // survived before we ever activate the staged prefix.
168
- const CONPTY_PREBUILD_RELATIVE_PATH = path.join(
169
- 'node_modules', 'adhdev', 'node_modules', 'node-pty', 'prebuilds', 'win32-x64', 'conpty.node'
170
- );
171
-
172
- function resolveStagedConptyPrebuildPath(stagedPrefix: string): string {
173
- return path.join(stagedPrefix, CONPTY_PREBUILD_RELATIVE_PATH);
174
- }
175
-
176
- function verifyStagedConptyPrebuild(stagedPrefix: string): void {
177
- const conptyPath = resolveStagedConptyPrebuildPath(stagedPrefix);
178
- if (!fs.existsSync(conptyPath)) {
179
- throw new Error(
180
- `Staged install is missing required native addon: ${conptyPath}. ` +
181
- 'Aborting activation to prevent a daemon boot crash.'
182
- );
183
- }
184
- }
185
-
186
117
  function readPackageCliEntry(prefix: string, packageName: string, targetVersion: string): string {
187
118
  const packageRoot = packageRootForPrefix(prefix, packageName);
188
119
  const packageJsonPath = path.join(packageRoot, 'package.json');
@@ -209,31 +140,13 @@ function quotePowerShellLiteral(value: string): string {
209
140
  function pinStagedShims(prefix: string, portableNode: string, cliEntry: string): void {
210
141
  const cmdPath = path.join(prefix, 'adhdev.cmd');
211
142
  const ps1Path = path.join(prefix, 'adhdev.ps1');
212
- // The no-extension `adhdev` shim is what `where.exe adhdev` can resolve first
213
- // and what git-bash / MSYS / WSL and `spawnSync('adhdev')` invoke. npm generates
214
- // it as a POSIX `sh` shim whose ELSE branch falls back to the FIRST `node` on
215
- // PATH (`else exec node ...`). On a box where system PATH node is v24, that
216
- // fallback trips adhdev's own Node-24 guard and `adhdev doctor` reports the
217
- // runtime surface broken. Rewrite it too so it hard-codes the portable Node 22
218
- // absolute path with NO system-node fallback — mirroring the .cmd/.ps1 pins.
219
- const noExtPath = path.join(prefix, 'adhdev');
220
143
  const cmd = `@echo off\r\n"${portableNode}" "${cliEntry}" %*\r\n`;
221
144
  const ps1 = `#!/usr/bin/env pwsh\r\n& ${quotePowerShellLiteral(portableNode)} ${quotePowerShellLiteral(cliEntry)} @args\r\nexit $LASTEXITCODE\r\n`;
222
- // sh shim: single unconditional exec of the pinned node — no `if -x node` /
223
- // `else exec node` branch, so PATH ordering can never shadow the runtime.
224
- const noExt = `#!/bin/sh\nexec "${portableNode}" "${cliEntry}" "$@"\n`;
225
145
  fs.writeFileSync(cmdPath, cmd, 'ascii');
226
146
  fs.writeFileSync(ps1Path, ps1, 'utf8');
227
- fs.writeFileSync(noExtPath, noExt, 'ascii');
228
147
  const cmdReadback = fs.readFileSync(cmdPath, 'utf8');
229
148
  const ps1Readback = fs.readFileSync(ps1Path, 'utf8');
230
- const noExtReadback = fs.readFileSync(noExtPath, 'utf8');
231
- if (
232
- !cmdReadback.includes(portableNode)
233
- || !ps1Readback.includes(portableNode)
234
- || !noExtReadback.includes(portableNode)
235
- || /(^|\s)exec\s+node(\s|$)/m.test(noExtReadback)
236
- ) {
149
+ if (!cmdReadback.includes(portableNode) || !ps1Readback.includes(portableNode)) {
237
150
  throw new Error('portable Node 22 pin validation failed');
238
151
  }
239
152
  }
@@ -319,39 +232,10 @@ function snapshotStableFiles(stablePrefix: string): Map<string, FileSnapshot> {
319
232
  return snapshots;
320
233
  }
321
234
 
322
- // Rollback must never leave PATH `adhdev` broken. Two failure modes made the old
323
- // "restore-or-delete" logic dangerous:
324
- // 1. If a stable shim (adhdev.cmd/.ps1/no-ext) did not exist at snapshot time —
325
- // a first/partial install, or a stable tree that never had the launcher —
326
- // deleting it leaves `where.exe adhdev` / `spawnSync('adhdev')` resolving to
327
- // nothing (ENOENT). `adhdev doctor` then reports the runtime surface broken.
328
- // 2. If the pointer (.adhdev-current) was absent at snapshot time, deleting it
329
- // strands the re-published shims with no version to redirect to.
330
- // So rollback (re)guarantees a valid launcher surface: existing snapshots restore
331
- // their original bytes atomically; missing shims are re-issued from the canonical
332
- // pointer-redirect launcher contents; and the pointer, when it has no snapshot,
333
- // is re-written to the last-known-good active version instead of removed.
334
- function restoreStableFiles(snapshots: Map<string, FileSnapshot>, layout: WindowsInstallerLayout): void {
335
- const shims = stableShimContents();
235
+ function restoreStableFiles(snapshots: Map<string, FileSnapshot>): void {
336
236
  for (const [target, snapshot] of snapshots) {
337
- if (snapshot.exists && snapshot.data) {
338
- atomicWrite(target, snapshot.data.toString('binary'), 'binary');
339
- continue;
340
- }
341
- const name = path.basename(target);
342
- if (name === 'adhdev.cmd' || name === 'adhdev.ps1' || name === 'adhdev') {
343
- // Re-issue a valid pointer-redirect launcher rather than deleting it, so
344
- // PATH `adhdev` always resolves after a rollback.
345
- atomicWrite(target, shims[name].content, shims[name].encoding);
346
- } else if (name === POINTER_NAME) {
347
- // Preserve the last-successful (currently active) version so the redirect
348
- // launchers still reach a real prefix. Only fall back to deleting when we
349
- // have no active version to point at.
350
- if (layout.activeVersionName) atomicWrite(target, layout.activeVersionName, 'ascii');
351
- else try { fs.unlinkSync(target); } catch { /* noop */ }
352
- } else {
353
- try { fs.unlinkSync(target); } catch { /* noop */ }
354
- }
237
+ if (snapshot.exists && snapshot.data) atomicWrite(target, snapshot.data.toString('binary'), 'binary');
238
+ else try { fs.unlinkSync(target); } catch { /* noop */ }
355
239
  }
356
240
  }
357
241
 
@@ -375,7 +259,6 @@ export async function performWindowsAtomicUpgrade(options: WindowsAtomicUpgradeO
375
259
  try {
376
260
  hooks.log(`Installing ${packageName}@${targetVersion} into inactive prefix ${stagedPrefix}`);
377
261
  await hooks.install(stagedPrefix, portableNode);
378
- verifyStagedConptyPrebuild(stagedPrefix);
379
262
  const stagedCliEntry = readPackageCliEntry(stagedPrefix, packageName, targetVersion);
380
263
  pinStagedShims(stagedPrefix, portableNode, stagedCliEntry);
381
264
  validateStagedCli(portableNode, stagedCliEntry, targetVersion);
@@ -415,7 +298,7 @@ export async function performWindowsAtomicUpgrade(options: WindowsAtomicUpgradeO
415
298
  if (restarted?.pid) hooks.stopProcess(restarted.pid);
416
299
  if (activated) {
417
300
  try {
418
- restoreStableFiles(snapshots, layout);
301
+ restoreStableFiles(snapshots);
419
302
  hooks.log(`Rolled back activation to ${layout.activeVersionName}`);
420
303
  } catch (rollbackError: any) {
421
304
  hooks.log(`Stable-file rollback failed: ${rollbackError?.message || String(rollbackError)}`);
@@ -435,24 +318,13 @@ export function createDefaultWindowsAtomicHooks(options: {
435
318
  cwd: string;
436
319
  env: NodeJS.ProcessEnv;
437
320
  log: (message: string) => void;
438
- /** Loopback IPC port to probe for health/version. Defaults to the daemon's local IPC port. */
439
- healthPort?: number;
440
- /** How long to poll for the replacement daemon to report the target version. */
441
- healthTimeoutMs?: number;
442
321
  }): WindowsAtomicUpgradeHooks {
443
- const healthPort = options.healthPort ?? DEFAULT_HEALTH_PORT;
444
- const healthTimeoutMs = options.healthTimeoutMs ?? DEFAULT_HEALTH_TIMEOUT_MS;
445
322
  return {
446
323
  install: (stagedPrefix, portableNode) => {
447
- const env: NodeJS.ProcessEnv = {
448
- ...options.env,
449
- ADHDEV_BOOTSTRAP: '1',
450
- npm_config_build_from_source: 'false',
451
- 'npm_config_build-from-source': 'false',
452
- };
324
+ const env: NodeJS.ProcessEnv = { ...options.env, ADHDEV_BOOTSTRAP: '1' };
453
325
  const pathKey = Object.keys(env).find((key) => key.toLowerCase() === 'path') || 'Path';
454
326
  env[pathKey] = `${path.dirname(portableNode)};${env[pathKey] || ''}`;
455
- const installOutput = String(execFileSync(portableNode, [
327
+ execFileSync(portableNode, [
456
328
  options.npmCliPath,
457
329
  'install', '-g', `${options.packageName}@${options.targetVersion}`, '--force', '--prefer-online', '--prefix', stagedPrefix,
458
330
  ], {
@@ -461,11 +333,7 @@ export function createDefaultWindowsAtomicHooks(options: {
461
333
  maxBuffer: 20 * 1024 * 1024,
462
334
  windowsHide: true,
463
335
  env,
464
- }));
465
- // On failure execFileSync throws with stdout attached to the Error; on
466
- // success it was previously discarded. Surface the npm output either way so
467
- // a silently-succeeding-then-rolled-back upgrade leaves a diagnosable trail.
468
- if (installOutput.trim()) options.log(installOutput.trim());
336
+ });
469
337
  },
470
338
  restart: (portableNode, stagedCliEntry) => {
471
339
  const restartArgv = options.restartArgv.map((arg, index) => index === 0 ? stagedCliEntry : arg);
@@ -492,42 +360,25 @@ export function createDefaultWindowsAtomicHooks(options: {
492
360
  child.unref();
493
361
  },
494
362
  waitForHealth: async (pid, targetVersion) => {
495
- const startedAt = Date.now();
496
- const deadline = startedAt + healthTimeoutMs;
497
- let attempt = 0;
498
- // Log the transition milestones exactly once so the daemon log shows how far
499
- // the replacement got before the gate resolved: not-yet-alive → alive but
500
- // version-not-yet-ready (components still booting) → version matched. This is
501
- // what pins down which boot stage the full-boot budget is being spent in.
502
- let loggedAlive = false;
503
- let loggedVersionPending = false;
363
+ const deadline = Date.now() + 30000;
504
364
  while (Date.now() < deadline) {
505
- attempt += 1;
506
- // Liveness + pid identity come from GET /health, whose body is
507
- // {ok, pid, wsPath, port} — it carries NO version. The running version
508
- // lives only in GET /api/v1/status → payload.status.version, so the
509
- // version gate must fetch that endpoint separately. Requiring the raw
510
- // /health body to include targetVersion is unsatisfiable and silently
511
- // rolls every upgrade back.
512
- const liveness = await fetchLocalHealth(healthPort);
513
- const alive = liveness.ok && liveness.pid === pid;
514
- const version = alive ? await fetchLocalStatusVersion(healthPort) : undefined;
515
- const elapsedMs = Date.now() - startedAt;
516
- if (alive && version === targetVersion) {
517
- options.log(`Health gate passed after ${elapsedMs}ms (${attempt} probe(s)): pid ${pid} reports ${targetVersion}`);
518
- return true;
519
- }
520
- if (alive && !loggedAlive) {
521
- loggedAlive = true;
522
- options.log(`Health gate: replacement pid ${pid} is alive at ${elapsedMs}ms; awaiting status.version (components still booting)`);
523
- }
524
- if (alive && version && version !== targetVersion && !loggedVersionPending) {
525
- loggedVersionPending = true;
526
- options.log(`Health gate: replacement reports version ${version} (want ${targetVersion}) at ${elapsedMs}ms`);
527
- }
365
+ const result = await new Promise<{ ok: boolean; pid?: number; body?: string }>((resolve) => {
366
+ const req = http.get(`http://127.0.0.1:${DEFAULT_HEALTH_PORT}/health`, { timeout: 1500 }, (res) => {
367
+ let body = '';
368
+ res.setEncoding('utf8');
369
+ res.on('data', (chunk) => { body += chunk; });
370
+ res.on('end', () => {
371
+ let responsePid: number | undefined;
372
+ try { responsePid = Number(JSON.parse(body)?.pid); } catch { /* noop */ }
373
+ resolve({ ok: res.statusCode === 200, pid: responsePid, body });
374
+ });
375
+ });
376
+ req.on('timeout', () => req.destroy());
377
+ req.on('error', () => resolve({ ok: false }));
378
+ });
379
+ if (result.ok && result.pid === pid && result.body?.includes(targetVersion)) return true;
528
380
  await new Promise((resolve) => setTimeout(resolve, 500));
529
381
  }
530
- options.log(`Health gate timed out after ${Date.now() - startedAt}ms (${attempt} probe(s), budget ${healthTimeoutMs}ms) waiting for pid ${pid} to report ${targetVersion}`);
531
382
  return false;
532
383
  },
533
384
  stopProcess: (pid) => {
@@ -562,19 +413,13 @@ export function boundedCleanupInactivePrefixes(
562
413
  return;
563
414
  }
564
415
  if (candidates.length === 0) return;
565
- // Delete in-process (see removeInactivePrefix) rather than via a powershell.exe
566
- // Remove-Item batch under a 5000ms spawnSync timeout, which ETIMEDOUT'd on
567
- // Windows and left orphan version-* prefixes behind. best-effort: a failure
568
- // here never blocks the upgrade success/failure signal.
569
- let incomplete = false;
570
- for (const candidate of candidates) {
571
- try {
572
- fs.rmSync(candidate, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
573
- } catch {
574
- incomplete = true;
575
- }
576
- }
577
- if (incomplete) log('Bounded inactive-prefix cleanup was incomplete; future updates will retry');
416
+ const escaped = candidates.map((candidate) => quotePowerShellLiteral(candidate)).join(',');
417
+ const script = `$ErrorActionPreference='Stop'; @(${escaped}) | ForEach-Object { if (Test-Path -LiteralPath $_) { Remove-Item -LiteralPath $_ -Recurse -Force } }`;
418
+ const encoded = Buffer.from(script, 'utf16le').toString('base64');
419
+ const result = spawnSync('powershell.exe', [
420
+ '-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', encoded,
421
+ ], { timeout: 5000, windowsHide: true, stdio: 'ignore' });
422
+ if (result.error || result.status !== 0) log('Bounded inactive-prefix cleanup was incomplete; future updates will retry');
578
423
  }
579
424
 
580
425
  export async function cleanupInactivePrefixesWithGuard(options: {
@@ -616,15 +461,16 @@ export async function cleanupInactivePrefixesWithGuard(options: {
616
461
  }
617
462
 
618
463
  function removeInactivePrefix(target: string, log?: (message: string) => void): void {
619
- // Delete in-process with fs.rmSync instead of shelling out to powershell.exe.
620
- // A version prefix holds thousands of small files (node_modules, node-pty
621
- // prebuilds); Remove-Item -Recurse -Force over that, plus PowerShell 5.1's
622
- // cold-start, routinely blew past the old 5000ms spawnSync timeout on Windows
623
- // — every candidate then failed with ETIMEDOUT and orphan version-* dirs
624
- // accumulated. fs.rmSync has no process spawn, no timeout, and retries the
625
- // transient EBUSY/EPERM/ENOTEMPTY that a just-stopped process can leave behind.
626
464
  try {
627
- fs.rmSync(target, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
465
+ const escaped = quotePowerShellLiteral(target);
466
+ const script = `if (Test-Path -LiteralPath ${escaped}) { Remove-Item -LiteralPath ${escaped} -Recurse -Force }`;
467
+ const encoded = Buffer.from(script, 'utf16le').toString('base64');
468
+ const result = spawnSync('powershell.exe', [
469
+ '-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', encoded,
470
+ ], { timeout: 5000, windowsHide: true, stdio: 'ignore' });
471
+ if (result.error || result.status !== 0) {
472
+ log?.(`Failed to remove inactive prefix ${target}: ${result.error?.message || `exit ${result.status}`}`);
473
+ }
628
474
  } catch (error: any) {
629
475
  log?.(`Failed to remove inactive prefix ${target}: ${error?.message || String(error)}`);
630
476
  }
@@ -19,18 +19,6 @@
19
19
  * machine-local policy directly. The repo file shapes the coordinator prompt and
20
20
  * operating notes, nothing else.
21
21
  *
22
- * `providerDefaults` IS NOT POLICY EITHER. It is a repo-shared DECLARATIVE
23
- * "requested auto-approve mode" per providerType — the answer to "WHEN a delegated
24
- * worker of type X is auto-approved, WHICH mode should it use by default". It has
25
- * NO say over WHETHER auto-approve is enabled: the ENABLE decision (and the
26
- * dangerous-mode opt-in) remains 100% machine-local (meshes.json /
27
- * RepoMeshPolicy). A repo can declare `providerDefaults` freely; a machine that
28
- * has delegatedWorkerAutoApprove=false still gets no auto-approve, and a dangerous
29
- * requested mode is still downgraded to PTY parsing unless the machine opts in.
30
- * The requested mode ID is validated at runtime against the provider spec — an
31
- * unknown mode ID is IGNORED (fall back to the provider's own default), never
32
- * silently coerced into a dangerous mode.
33
- *
34
22
  * The coordinator/operatingNotes merge is **in-memory only**: the on-disk
35
23
  * machine-local `meshes.json` is never mutated by this module.
36
24
  *
@@ -72,19 +60,6 @@ export interface RepoMeshDeclarativeLimits {
72
60
  maxNotes?: number;
73
61
  }
74
62
 
75
- /**
76
- * Repo-shared declarative per-provider defaults. NOT policy — see the file header.
77
- * `autoApproveModes` maps a providerType (e.g. "claude-cli") to the auto-approve
78
- * mode ID that a delegated worker of that type should REQUEST when it is
79
- * auto-approved. The map only influences WHICH mode is used, never WHETHER
80
- * auto-approve is enabled (that stays machine-local). Mode IDs are validated
81
- * against the live provider spec at resolve time; an unknown ID is ignored.
82
- */
83
- export interface RepoMeshDeclarativeProviderDefaults {
84
- /** providerType → requested auto-approve mode ID. */
85
- autoApproveModes?: Record<string, string>;
86
- }
87
-
88
63
  /**
89
64
  * Parsed + normalized `.adhdev/mesh.json` shape. Every field is optional except
90
65
  * version so a repo can declare only the zone(s) it cares about. Policy is NOT a
@@ -95,8 +70,6 @@ export interface RepoMeshDeclarativeConfig {
95
70
  coordinator?: RepoMeshDeclarativeCoordinatorConfig;
96
71
  operatingNotes?: CoordinatorOperatingNote[];
97
72
  limits?: RepoMeshDeclarativeLimits;
98
- /** Repo-shared per-provider defaults (requested auto-approve mode). NOT policy. */
99
- providerDefaults?: RepoMeshDeclarativeProviderDefaults;
100
73
  }
101
74
 
102
75
  export interface RepoMeshJsonConfigLoadResult {
@@ -159,20 +132,6 @@ export const MESH_JSON_CONFIG_SCHEMA = {
159
132
  maxNotes: { type: 'number', minimum: 1 },
160
133
  },
161
134
  },
162
- providerDefaults: {
163
- type: 'object',
164
- additionalProperties: false,
165
- properties: {
166
- // providerType → requested auto-approve mode ID. Mode IDs are
167
- // validated against the live provider spec at resolve time; an
168
- // unknown ID is ignored (provider default is used), so the schema
169
- // only constrains the shape (string→string), not the ID values.
170
- autoApproveModes: {
171
- type: 'object',
172
- additionalProperties: { type: 'string' },
173
- },
174
- },
175
- },
176
135
  },
177
136
  } as const;
178
137
 
@@ -201,14 +160,6 @@ function normalizeOperatingNote(value: unknown): CoordinatorOperatingNote | null
201
160
  ...(category ? { category } : {}),
202
161
  ...(typeof value.createdAt === 'string' ? { createdAt: value.createdAt } : {}),
203
162
  ...(typeof value.sourceCoordinator === 'string' ? { sourceCoordinator: value.sourceCoordinator } : {}),
204
- // Operating-notes lifecycle: a repo-declared note may pin itself or set an
205
- // explicit expiry, same as a runtime-recorded one. Phase 2 (b)/(c): it may
206
- // also declare supersedes/subjectKey to retire an earlier note or group
207
- // same-subject notes for folding.
208
- ...(value.pinned === true ? { pinned: true } : {}),
209
- ...(typeof value.expiresAt === 'string' ? { expiresAt: value.expiresAt } : {}),
210
- ...(typeof value.supersedes === 'string' ? { supersedes: value.supersedes } : {}),
211
- ...(typeof value.subjectKey === 'string' ? { subjectKey: value.subjectKey } : {}),
212
163
  };
213
164
  }
214
165
 
@@ -267,38 +218,6 @@ export function normalizeRepoMeshDeclarativeConfig(parsed: unknown): {
267
218
  }
268
219
  }
269
220
 
270
- // providerDefaults: shape-only normalization. We keep every string→string
271
- // (providerType → requested mode ID) entry after trimming; we deliberately do
272
- // NOT validate the mode IDs against any provider spec here — the normalizer has
273
- // no provider registry, and (crucially) validity depends on the LIVE provider
274
- // at resolve time. The runtime resolver (resolveDelegatedWorkerAutoApprove)
275
- // checks the requested ID against provider.autoApproveModes.modes and falls
276
- // back to the provider's own default when the ID is unknown. This keeps the
277
- // config fail-closed: a stale/typo'd mode ID never coerces into a dangerous
278
- // mode, it just means "no repo override → provider default".
279
- if (parsed.providerDefaults !== undefined) {
280
- if (isRecord(parsed.providerDefaults)) {
281
- const pd: RepoMeshDeclarativeProviderDefaults = {};
282
- const rawModes = (parsed.providerDefaults as Record<string, unknown>).autoApproveModes;
283
- if (rawModes !== undefined) {
284
- if (isRecord(rawModes)) {
285
- const modes: Record<string, string> = {};
286
- for (const [providerType, modeId] of Object.entries(rawModes)) {
287
- const type = typeof providerType === 'string' ? providerType.trim() : '';
288
- const id = typeof modeId === 'string' ? modeId.trim() : '';
289
- if (type && id) modes[type] = id;
290
- }
291
- if (Object.keys(modes).length) pd.autoApproveModes = modes;
292
- } else {
293
- errors.push('providerDefaults.autoApproveModes must be an object when provided');
294
- }
295
- }
296
- if (Object.keys(pd).length) config.providerDefaults = pd;
297
- } else {
298
- errors.push('providerDefaults must be an object when provided');
299
- }
300
- }
301
-
302
221
  return { valid: true, config, errors };
303
222
  }
304
223
 
@@ -437,21 +356,6 @@ export function applyRepoMeshConfig<T extends Pick<LocalMeshEntry, 'coordinator'
437
356
  * exported — it is machine-local only and has no place in mesh.json. Operating
438
357
  * notes are intentionally NOT exported either: those are runtime ledger lessons,
439
358
  * and a repo should declare baseline notes deliberately.
440
- *
441
- * `providerDefaults` is NOT auto-exported from the machine-local mesh (there is no
442
- * machine-local source for it — it is a repo-authored declaration). To help an
443
- * operator hand-author it, the scaffold carries a commented example shape:
444
- *
445
- * "providerDefaults": {
446
- * "autoApproveModes": {
447
- * "claude-cli": "accept-edits", // requested mode WHEN auto-approve is on
448
- * "codex-cli": "auto" // (enable/dangerous opt-in stays machine-local)
449
- * }
450
- * }
451
- *
452
- * The example is surfaced via the scaffold's `_providerDefaultsExample` hint (JSON
453
- * has no comments) rather than a live `providerDefaults` value, so serializing the
454
- * scaffold never writes an unwanted requested-mode map into the repo file.
455
359
  */
456
360
  export function buildMeshJsonConfigScaffold(
457
361
  mesh: Pick<LocalMeshEntry, 'coordinator'>,
@@ -466,21 +370,6 @@ export function buildMeshJsonConfigScaffold(
466
370
  return scaffold;
467
371
  }
468
372
 
469
- /**
470
- * A copy-paste example of the `providerDefaults` zone for operators hand-authoring
471
- * a repo `.adhdev/mesh.json`. Returned by the export/write commands as a separate
472
- * hint field (never merged into the serialized scaffold) so the repo file stays
473
- * clean. The mode IDs shown are illustrative — a repo should use IDs that exist in
474
- * its own providers' specs; an unknown ID is ignored at resolve time and the
475
- * provider's own default is used.
476
- */
477
- export const MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE: RepoMeshDeclarativeProviderDefaults = {
478
- autoApproveModes: {
479
- 'claude-cli': 'accept-edits',
480
- 'codex-cli': 'auto',
481
- },
482
- };
483
-
484
373
  /** Serialize a scaffold to the canonical 2-space JSON draft text. */
485
374
  export function serializeMeshJsonConfigScaffold(config: RepoMeshDeclarativeConfig): string {
486
375
  return JSON.stringify(config, null, 2);
package/src/index.ts CHANGED
@@ -143,8 +143,6 @@ export type {
143
143
  export {
144
144
  DEFAULT_MESH_POLICY,
145
145
  resolveDelegatedWorkerAutoApprove,
146
- delegatedWorkerAutoApproveSettings,
147
- resolveDelegatedWorkerDangerousModeAllow,
148
146
  resolveAllowSendKeysDestructive,
149
147
  resolveMagiSessionCleanupMode,
150
148
  magiAutoLaunchedSessionCleanupDecision,
@@ -163,24 +161,6 @@ export {
163
161
  resolveAutoConvergeCodeChange,
164
162
  } from './repo-mesh-types.js';
165
163
 
166
- // ── Repo-shared declarative mesh config (.adhdev/mesh.json) ──
167
- export {
168
- loadRepoMeshJsonConfig,
169
- normalizeRepoMeshDeclarativeConfig,
170
- buildMeshJsonConfigScaffold,
171
- serializeMeshJsonConfigScaffold,
172
- MESH_JSON_CONFIG_LOCATIONS,
173
- MESH_JSON_CONFIG_SCHEMA,
174
- MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE,
175
- } from './config/mesh-json-config.js';
176
- export type {
177
- RepoMeshDeclarativeConfig,
178
- RepoMeshDeclarativeCoordinatorConfig,
179
- RepoMeshDeclarativeLimits,
180
- RepoMeshDeclarativeProviderDefaults,
181
- RepoMeshJsonConfigLoadResult,
182
- } from './config/mesh-json-config.js';
183
-
184
164
  // ── Git Surface ──
185
165
  export * from './git/index.js';
186
166
 
@@ -509,7 +489,7 @@ export { ProviderInstanceManager } from './providers/provider-instance-manager.j
509
489
  export { IdeProviderInstance } from './providers/ide-provider-instance.js';
510
490
  export { CliProviderInstance } from './providers/cli-provider-instance.js';
511
491
  export { AcpProviderInstance } from './providers/acp-provider-instance.js';
512
- export type { ProviderModule, AutoApproveMode, AutoApproveModesConfig, AutoApproveModeRisk, AutoApproveModeStrategy, CdpTargetFilter, ProviderResumeCapability, InputEnvelope, InputPart, MessagePart, ReadChatTurnStatus, ControlListResult, ControlSetResult, ControlInvokeResult } from './providers/contracts.js';
492
+ export type { ProviderModule, CdpTargetFilter, ProviderResumeCapability, InputEnvelope, InputPart, MessagePart, ReadChatTurnStatus, ControlListResult, ControlSetResult, ControlInvokeResult } from './providers/contracts.js';
513
493
  export type { ProviderSourceConfigSnapshot, ProviderSourceConfigUpdate } from './config/provider-source-config.js';
514
494
  export { parseProviderSourceConfigUpdate } from './config/provider-source-config.js';
515
495
  export { normalizeInputEnvelope, normalizeMessageParts, flattenMessageParts } from './providers/io-contracts.js';