@phnx-labs/agents-cli 1.22.57 → 1.22.58

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 (101) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/bootstrap.js +8 -1
  3. package/dist/commands/accounts.js +7 -3
  4. package/dist/commands/apply.js +10 -2
  5. package/dist/commands/fork.d.ts +23 -10
  6. package/dist/commands/fork.js +115 -58
  7. package/dist/commands/monitors.js +11 -0
  8. package/dist/commands/prune.js +5 -3
  9. package/dist/commands/routines.d.ts +8 -0
  10. package/dist/commands/routines.js +57 -3
  11. package/dist/commands/sessions-picker.d.ts +11 -0
  12. package/dist/commands/sessions-picker.js +16 -0
  13. package/dist/commands/sessions.js +1 -0
  14. package/dist/commands/share.d.ts +14 -0
  15. package/dist/commands/share.js +43 -2
  16. package/dist/commands/status.js +1 -1
  17. package/dist/commands/sync.js +83 -7
  18. package/dist/commands/traces.js +7 -0
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.js +6 -1
  21. package/dist/lib/account-registry.d.ts +5 -1
  22. package/dist/lib/account-registry.js +47 -14
  23. package/dist/lib/accounting/capacity.d.ts +18 -7
  24. package/dist/lib/accounting/capacity.js +19 -8
  25. package/dist/lib/accounting/usage-sync.d.ts +29 -1
  26. package/dist/lib/accounting/usage-sync.js +76 -2
  27. package/dist/lib/accounting/usage.js +7 -1
  28. package/dist/lib/auth-mint.d.ts +11 -1
  29. package/dist/lib/auth-mint.js +21 -6
  30. package/dist/lib/browser/ipc.d.ts +8 -0
  31. package/dist/lib/browser/ipc.js +87 -0
  32. package/dist/lib/browser/service.d.ts +19 -0
  33. package/dist/lib/browser/service.js +96 -11
  34. package/dist/lib/browser/sessions-list.js +10 -1
  35. package/dist/lib/daemon/runner.d.ts +3 -0
  36. package/dist/lib/daemon/runner.js +86 -45
  37. package/dist/lib/daemon/usage-sync-service.d.ts +3 -3
  38. package/dist/lib/daemon/usage-sync-service.js +14 -8
  39. package/dist/lib/daemon-services.js +1 -1
  40. package/dist/lib/devices/connect.d.ts +17 -8
  41. package/dist/lib/devices/connect.js +31 -14
  42. package/dist/lib/doctor-diff.js +77 -7
  43. package/dist/lib/fleet/manifest.d.ts +17 -0
  44. package/dist/lib/fleet/manifest.js +26 -0
  45. package/dist/lib/hooks/install.d.ts +27 -11
  46. package/dist/lib/hooks/install.js +42 -17
  47. package/dist/lib/hosts/reconnect.d.ts +52 -203
  48. package/dist/lib/hosts/reconnect.js +64 -284
  49. package/dist/lib/installations/migrate.d.ts +6 -120
  50. package/dist/lib/installations/migrate.js +27 -259
  51. package/dist/lib/installations/shims.d.ts +13 -95
  52. package/dist/lib/installations/shims.js +22 -139
  53. package/dist/lib/installations/store.js +1 -1
  54. package/dist/lib/installations/versions.d.ts +26 -133
  55. package/dist/lib/installations/versions.js +41 -204
  56. package/dist/lib/plugins/skills.d.ts +8 -1
  57. package/dist/lib/plugins/skills.js +18 -2
  58. package/dist/lib/refresh.d.ts +9 -0
  59. package/dist/lib/refresh.js +3 -1
  60. package/dist/lib/routine-readiness.d.ts +15 -1
  61. package/dist/lib/routine-readiness.js +41 -0
  62. package/dist/lib/sandbox.d.ts +4 -1
  63. package/dist/lib/sandbox.js +30 -1
  64. package/dist/lib/secrets/agent.d.ts +80 -225
  65. package/dist/lib/secrets/agent.js +139 -401
  66. package/dist/lib/secrets/bundles.d.ts +73 -222
  67. package/dist/lib/secrets/bundles.js +168 -467
  68. package/dist/lib/secrets/reaper.d.ts +28 -70
  69. package/dist/lib/secrets/reaper.js +30 -85
  70. package/dist/lib/secrets/remote.d.ts +42 -129
  71. package/dist/lib/secrets/remote.js +55 -173
  72. package/dist/lib/self-heal/checks/install-staging.d.ts +4 -0
  73. package/dist/lib/self-heal/checks/install-staging.js +96 -0
  74. package/dist/lib/self-heal/registry.js +2 -0
  75. package/dist/lib/self-heal/types.d.ts +1 -1
  76. package/dist/lib/self-update.d.ts +23 -0
  77. package/dist/lib/self-update.js +50 -0
  78. package/dist/lib/session/active.d.ts +13 -1
  79. package/dist/lib/session/active.js +2 -0
  80. package/dist/lib/session/db.d.ts +20 -1
  81. package/dist/lib/session/db.js +139 -9
  82. package/dist/lib/session/fork.d.ts +45 -26
  83. package/dist/lib/session/fork.js +32 -95
  84. package/dist/lib/session/tool-calls.d.ts +43 -1
  85. package/dist/lib/session/tool-calls.js +74 -44
  86. package/dist/lib/session/tool-store.d.ts +33 -2
  87. package/dist/lib/session/tool-store.js +56 -3
  88. package/dist/lib/staleness/writers/sources.d.ts +5 -0
  89. package/dist/lib/staleness/writers/sources.js +2 -1
  90. package/dist/lib/sync-status.d.ts +22 -0
  91. package/dist/lib/sync-status.js +27 -0
  92. package/dist/lib/sync-umbrella.d.ts +9 -0
  93. package/dist/lib/sync-umbrella.js +21 -2
  94. package/dist/lib/traces/insights.d.ts +47 -14
  95. package/dist/lib/traces/insights.js +92 -21
  96. package/dist/lib/traces/phenotype.d.ts +23 -3
  97. package/dist/lib/traces/phenotype.js +72 -24
  98. package/dist/lib/traces/sync.d.ts +15 -0
  99. package/dist/lib/traces/sync.js +104 -19
  100. package/dist/lib/traces/worker-template.js +154 -1
  101. package/package.json +1 -1
@@ -1,30 +1,13 @@
1
1
  import type { AgentId } from '../types.js';
2
2
  import { getShimsDir } from '../state.js';
3
3
  export { getShimsDir };
4
- /**
5
- * Strategy for handling file conflicts during config migration.
6
- */
7
4
  export type ConflictStrategy = 'keep-dest' | 'overwrite' | 'ask-per-file';
8
- /**
9
- * Information about conflicts found during config migration.
10
- */
11
5
  export interface ConflictInfo {
12
6
  agent: AgentId;
13
7
  version: string;
14
8
  conflicts: string[];
15
9
  }
16
- /**
17
- * Generate the shim script content for an agent.
18
- *
19
- * The shim resolves the version in order:
20
- * 1. agents.yaml in project root (walk up from $PWD, skip ~/.agents/agents.yaml)
21
- * 2. ~/.agents/agents.yaml default
22
- *
23
- * If version is specified but not installed, auto-installs it.
24
- *
25
- * Config isolation is handled via symlinks:
26
- * ~/.{agent} -> ~/.agents/versions/{agent}/{version}/home/.{agent}/
27
- */
10
+ /** Generate the shim script content for an agent. Resolves project/default version, auto-installs if missing, and execs the binary. */
28
11
  /**
29
12
  * Current shim schema version. Bump whenever `generateShimScript` changes
30
13
  * in a way that requires existing on-disk shims to be regenerated (new
@@ -95,9 +78,7 @@ export declare function shimTargetsFor(platform: NodeJS.Platform): {
95
78
  bash: boolean;
96
79
  cmd: boolean;
97
80
  };
98
- /**
99
- * Create a shim for an agent.
100
- */
81
+ /** Create the shim(s) for an agent. */
101
82
  export declare function createShim(agent: AgentId): string;
102
83
  /** The POSIX pass-through shim for a brand. */
103
84
  export declare function generateBrandShim(name: string): string;
@@ -107,9 +88,7 @@ export declare function createBrandShim(name: string): string;
107
88
  export declare function isBrandShim(filePath: string): boolean;
108
89
  /** Remove a brand's shim companions. Returns true if anything was removed. */
109
90
  export declare function removeBrandShim(name: string): boolean;
110
- /**
111
- * Remove the shim for an agent.
112
- */
91
+ /** Remove the shim(s) for an agent. */
113
92
  export declare function removeShim(agent: AgentId): boolean;
114
93
  /**
115
94
  * Current versioned-alias schema. Bump whenever `generateVersionedAliasScript`
@@ -252,10 +231,7 @@ export declare function removeVersionedAlias(agent: AgentId, version: string): b
252
231
  * Check if a versioned alias exists (the on-disk artifact for this platform).
253
232
  */
254
233
  export declare function versionedAliasExists(agent: AgentId, version: string): boolean;
255
- /**
256
- * Get the path to the agent's config directory in HOME.
257
- * e.g., ~/.claude for claude, ~/.codex for codex
258
- */
234
+ /** Get the agent's config directory path in HOME (e.g. ~/.claude). */
259
235
  export declare function getAgentConfigPath(agent: AgentId): string;
260
236
  /**
261
237
  * Read the user's configured Codex model from their active `~/.codex/config.toml`.
@@ -274,41 +250,11 @@ export declare function getAgentConfigPath(agent: AgentId): string;
274
250
  * a missing/unreadable/unset config yields `undefined` (caller keeps prior behaviour).
275
251
  */
276
252
  export declare function readCodexConfiguredModel(): string | undefined;
277
- /**
278
- * Switch the agent's config symlink to point to a specific version.
279
- * e.g., ~/.claude -> ~/.agents/versions/claude/2.0.65/home/.claude/
280
- *
281
- * If a real directory exists at the config path, it will be backed up
282
- * to ~/.agents/backups/{agent}/{timestamp}/ and replaced with a symlink.
283
- *
284
- * @param agent - The agent ID
285
- * @param version - The version to switch to
286
- *
287
- * Returns: { success: boolean, backupPath?: string, error?: string }
288
- */
289
- /**
290
- * Seed a version's config home with the account credential so switching versions
291
- * doesn't log the CLI out. Droid/antigravity/kimi (registry `authFiles`) store
292
- * login as files inside the per-version config dir; sign-in is account-global,
293
- * so we copy the FRESHEST existing copy (by mtime, across all installed version
294
- * homes) into `toConfigDir` when its copy is missing or older. mtime is
295
- * preserved so the "freshest" comparison stays stable and switches don't
296
- * ping-pong. Best-effort: a failed copy just means the user re-logs in.
297
- */
298
- /**
299
- * Best-effort account identity for the credential *directory* of a file-auth
300
- * agent (droid / kimi / antigravity), or null when the directory holds no
301
- * decodable account claim. Delegates to readAuthAccountIdentity, which decrypts
302
- * / decodes each agent's REAL on-disk format (droid AES-256-GCM + WorkOS JWT,
303
- * kimi access-token JWT, antigravity refresh-token) — the earlier plaintext
304
- * top-level-key scan matched NO real credential file, so the guard below never
305
- * engaged. Two dirs for the SAME account compare equal; DIFFERENT accounts
306
- * compare distinct. Used by carryForwardAuthFiles to refuse overwriting one
307
- * account's login with a credential that belongs to a DIFFERENT account
308
- * (RUSH-1764).
309
- */
253
+ /** Best-effort account identity for a file-auth agent's credential directory; null when no decodable account claim exists. */
310
254
  export declare function readAuthFileIdentity(agent: AgentId, configDir: string): string | null;
255
+ /** Carry the freshest existing account credential into `toConfigDir` so version switches don't log out file-auth agents. */
311
256
  export declare function carryForwardAuthFiles(agent: AgentId, toConfigDir: string): void;
257
+ /** Switch the agent's config symlink to point at a specific version, backing up any real directory first. */
312
258
  export declare function switchConfigSymlink(agent: AgentId, version: string): Promise<{
313
259
  success: boolean;
314
260
  backupPath?: string;
@@ -350,23 +296,11 @@ export declare function ensureClaudeInsideSymlink(version: string): void;
350
296
  * Get the current config symlink target version, if any.
351
297
  */
352
298
  export declare function getConfigSymlinkVersion(agent: AgentId): string | null;
353
- /**
354
- * Check if shim exists for an agent.
355
- */
356
- /**
357
- * The on-disk shim FILENAME for a platform — derived from `shimTargetsFor` (the
358
- * write-side source of truth) so the exists/remove/version checks can never
359
- * drift from what `createShim` actually writes: `<cmd>.cmd` on Windows (the only
360
- * file written there), the bare `<cmd>` script on POSIX. Pure — testable on any
361
- * host.
362
- */
299
+ /** The on-disk shim filename for a platform: `<cmd>.cmd` on Windows, bare `<cmd>` on POSIX. */
363
300
  export declare function onDiskShimFile(cliCommand: string, platform: NodeJS.Platform): string;
301
+ /** Check whether the on-disk shim exists for an agent. */
364
302
  export declare function shimExists(agent: AgentId): boolean;
365
- /**
366
- * True if the on-disk shim's schema version matches `SHIM_SCHEMA_VERSION`.
367
- * False means either the shim is missing, is pre-v2 (no marker), or is an
368
- * older version that needs regeneration.
369
- */
303
+ /** True when the on-disk shim's schema version matches the current schema. */
370
304
  export declare function isShimCurrent(agent: AgentId): boolean;
371
305
  /**
372
306
  * True when the agent shim's baked `AGENTS_BIN` is fine to keep: it either already
@@ -390,27 +324,11 @@ export declare function listShimFileNames(): string[];
390
324
  * Returns true if removed.
391
325
  */
392
326
  export declare function pruneOrphanedCommandShim(fileName: string): boolean;
393
- /**
394
- * Regenerate the shim if it's missing or outdated. Returns a status describing
395
- * what happened — callers can surface a one-line notice to the user ("Updated
396
- * shim for codex") when appropriate.
397
- */
327
+ /** Regenerate the shim if missing or older than the current schema; never downgrade a newer on-disk shim. */
398
328
  export declare function ensureShimCurrent(agent: AgentId): 'created' | 'updated' | 'current';
399
- /**
400
- * Get the path to the shim for an agent.
401
- */
329
+ /** Get the logical (extensionless) shim path for an agent. */
402
330
  export declare function getShimPath(agent: AgentId): string;
403
- /**
404
- * Return the first executable path that would be launched for this agent when
405
- * resolving against PATH, excluding the managed shim itself.
406
- *
407
- * Legacy ~/.agents/shims/<cli> (from the pre-split single-root layout) is NOT
408
- * treated as a shadow when a current managed shim exists at getShimPath() —
409
- * that file is dead weight from the old layout and the repair flow removes it
410
- * separately. Treating it as "shadowing" caused an infinite repair-prompt
411
- * loop because addShimsToPath() only edits the rc file, never the legacy
412
- * shim file itself.
413
- */
331
+ /** Return the first executable on PATH that would shadow the managed shim, excluding the shim itself and legacy pre-split files. */
414
332
  export declare function getPathShadowingExecutable(agent: AgentId, overrides?: {
415
333
  pathDirs?: string[];
416
334
  shimPath?: string;
@@ -1,12 +1,5 @@
1
1
  /**
2
- * Shim generation and config symlink management for agent version switching.
3
- *
4
- * Shims are small shell scripts placed in ~/.agents/shims/ that resolve the
5
- * active agent version (project-level or user-default), then exec the real
6
- * binary. Config isolation is achieved by symlinking ~/.{agent} into the
7
- * per-version home directory. This module also handles versioned aliases
8
- * (e.g., claude@2.0.65), PATH setup, conflict detection during migration,
9
- * and resource diffing between versions.
2
+ * Shim generation, config symlink management, and versioned aliases for agent version switching.
10
3
  */
11
4
  import * as fs from 'fs';
12
5
  import * as path from 'path';
@@ -19,10 +12,7 @@ export { getShimsDir };
19
12
  import { AGENTS, agentConfigDirName, readAuthAccountIdentity } from '../agents.js';
20
13
  import { codexHomeShimBash } from '../codex-home.js';
21
14
  import { resolveHarnessAdapter } from '../harness/index.js';
22
- /**
23
- * Files and directories to always skip during conflict detection and migration.
24
- * These are never user config that should be migrated.
25
- */
15
+ /** Files and directories to always skip during conflict detection and migration. */
26
16
  const MIGRATION_IGNORE_LIST = new Set([
27
17
  'node_modules',
28
18
  '.git',
@@ -34,9 +24,6 @@ const MIGRATION_IGNORE_LIST = new Set([
34
24
  '.DS_Store',
35
25
  'Thumbs.db',
36
26
  ]);
37
- /**
38
- * Check if a file/directory should be ignored during migration.
39
- */
40
27
  function shouldIgnore(name) {
41
28
  if (MIGRATION_IGNORE_LIST.has(name))
42
29
  return true;
@@ -44,10 +31,7 @@ function shouldIgnore(name) {
44
31
  return true;
45
32
  return false;
46
33
  }
47
- /**
48
- * Detect conflicting files between source and destination directories.
49
- * Returns list of filenames that exist in both locations (excluding symlinks in dest).
50
- */
34
+ /** Detect filenames that exist in both `src` and `dest`, excluding symlinks in `dest`. */
51
35
  function detectConflicts(src, dest, prefix = '') {
52
36
  const conflicts = [];
53
37
  if (!fs.existsSync(src) || !fs.existsSync(dest)) {
@@ -94,9 +78,6 @@ function detectConflicts(src, dest, prefix = '') {
94
78
  }
95
79
  return conflicts;
96
80
  }
97
- /**
98
- * Prompt user for conflict resolution strategy.
99
- */
100
81
  async function promptConflictStrategy(conflictInfos) {
101
82
  const totalConflicts = conflictInfos.reduce((sum, info) => sum + info.conflicts.length, 0);
102
83
  if (totalConflicts === 0) {
@@ -140,18 +121,7 @@ async function promptConflictStrategy(conflictInfos) {
140
121
  });
141
122
  return strategy;
142
123
  }
143
- /**
144
- * Generate the shim script content for an agent.
145
- *
146
- * The shim resolves the version in order:
147
- * 1. agents.yaml in project root (walk up from $PWD, skip ~/.agents/agents.yaml)
148
- * 2. ~/.agents/agents.yaml default
149
- *
150
- * If version is specified but not installed, auto-installs it.
151
- *
152
- * Config isolation is handled via symlinks:
153
- * ~/.{agent} -> ~/.agents/versions/{agent}/{version}/home/.{agent}/
154
- */
124
+ /** Generate the shim script content for an agent. Resolves project/default version, auto-installs if missing, and execs the binary. */
155
125
  /**
156
126
  * Current shim schema version. Bump whenever `generateShimScript` changes
157
127
  * in a way that requires existing on-disk shims to be regenerated (new
@@ -745,9 +715,7 @@ export function shimTargetsFor(platform) {
745
715
  return { bash: false, cmd: true };
746
716
  return { bash: true, cmd: false };
747
717
  }
748
- /**
749
- * Create a shim for an agent.
750
- */
718
+ /** Create the shim(s) for an agent. */
751
719
  export function createShim(agent) {
752
720
  // A bare shim puts agents-cli first on PATH for this agent — the opposite of what
753
721
  // an isolated-only install promises.
@@ -859,9 +827,7 @@ function writeWindowsCmdShim(cmdPath, spec, extraMarkerLines = []) {
859
827
  `node "${indexJs}" __shim ${spec} %*\r\n`;
860
828
  fs.writeFileSync(cmdPath, content);
861
829
  }
862
- /**
863
- * Remove the shim for an agent.
864
- */
830
+ /** Remove the shim(s) for an agent. */
865
831
  export function removeShim(agent) {
866
832
  const shimsDir = getShimsDir();
867
833
  const agentConfig = AGENTS[agent];
@@ -1273,10 +1239,7 @@ export function removeVersionedAlias(agent, version) {
1273
1239
  export function versionedAliasExists(agent, version) {
1274
1240
  return fs.existsSync(versionedAliasOnDiskPath(agent, version));
1275
1241
  }
1276
- /**
1277
- * Get the path to the agent's config directory in HOME.
1278
- * e.g., ~/.claude for claude, ~/.codex for codex
1279
- */
1242
+ /** Get the agent's config directory path in HOME (e.g. ~/.claude). */
1280
1243
  export function getAgentConfigPath(agent) {
1281
1244
  const agentConfig = AGENTS[agent];
1282
1245
  const home = process.env.AGENTS_REAL_HOME || os.homedir();
@@ -1311,25 +1274,15 @@ export function readCodexConfiguredModel() {
1311
1274
  return undefined;
1312
1275
  }
1313
1276
  }
1314
- /**
1315
- * Get the path to the version's config directory.
1316
- * e.g., ~/.agents/versions/claude/2.0.65/home/.claude/
1317
- */
1277
+ /** Get the version-home config directory path. */
1318
1278
  function getVersionConfigPath(agent, version) {
1319
1279
  const agentConfig = AGENTS[agent];
1320
1280
  const versionsDir = getVersionsDir();
1321
- // Carry the agent's full configDir subpath so nested layouts work.
1322
- // e.g., antigravity → `.gemini/antigravity-cli`, claude → `.claude`.
1281
+ // Use the agent's full configDir subpath so nested layouts (e.g. antigravity) work.
1323
1282
  const configDirName = path.relative(os.homedir(), agentConfig.configDir);
1324
1283
  return path.join(versionsDir, agent, version, 'home', configDirName);
1325
1284
  }
1326
- /**
1327
- * Detect conflicts that would occur when switching config symlink for an agent/version.
1328
- * This allows collecting conflicts upfront before prompting for a strategy.
1329
- *
1330
- * Returns null if no migration is needed (already symlink or doesn't exist),
1331
- * or ConflictInfo with the list of conflicting files.
1332
- */
1285
+ /** Detect conflicts between the current config directory and the target version home. */
1333
1286
  function detectMigrationConflicts(agent, version) {
1334
1287
  const configPath = getAgentConfigPath(agent);
1335
1288
  const versionConfigPath = getVersionConfigPath(agent, version);
@@ -1360,42 +1313,11 @@ function detectMigrationConflicts(agent, version) {
1360
1313
  return null;
1361
1314
  }
1362
1315
  }
1363
- /**
1364
- * Switch the agent's config symlink to point to a specific version.
1365
- * e.g., ~/.claude -> ~/.agents/versions/claude/2.0.65/home/.claude/
1366
- *
1367
- * If a real directory exists at the config path, it will be backed up
1368
- * to ~/.agents/backups/{agent}/{timestamp}/ and replaced with a symlink.
1369
- *
1370
- * @param agent - The agent ID
1371
- * @param version - The version to switch to
1372
- *
1373
- * Returns: { success: boolean, backupPath?: string, error?: string }
1374
- */
1375
- /**
1376
- * Seed a version's config home with the account credential so switching versions
1377
- * doesn't log the CLI out. Droid/antigravity/kimi (registry `authFiles`) store
1378
- * login as files inside the per-version config dir; sign-in is account-global,
1379
- * so we copy the FRESHEST existing copy (by mtime, across all installed version
1380
- * homes) into `toConfigDir` when its copy is missing or older. mtime is
1381
- * preserved so the "freshest" comparison stays stable and switches don't
1382
- * ping-pong. Best-effort: a failed copy just means the user re-logs in.
1383
- */
1384
- /**
1385
- * Best-effort account identity for the credential *directory* of a file-auth
1386
- * agent (droid / kimi / antigravity), or null when the directory holds no
1387
- * decodable account claim. Delegates to readAuthAccountIdentity, which decrypts
1388
- * / decodes each agent's REAL on-disk format (droid AES-256-GCM + WorkOS JWT,
1389
- * kimi access-token JWT, antigravity refresh-token) — the earlier plaintext
1390
- * top-level-key scan matched NO real credential file, so the guard below never
1391
- * engaged. Two dirs for the SAME account compare equal; DIFFERENT accounts
1392
- * compare distinct. Used by carryForwardAuthFiles to refuse overwriting one
1393
- * account's login with a credential that belongs to a DIFFERENT account
1394
- * (RUSH-1764).
1395
- */
1316
+ /** Best-effort account identity for a file-auth agent's credential directory; null when no decodable account claim exists. */
1396
1317
  export function readAuthFileIdentity(agent, configDir) {
1397
1318
  return readAuthAccountIdentity(agent, configDir);
1398
1319
  }
1320
+ /** Carry the freshest existing account credential into `toConfigDir` so version switches don't log out file-auth agents. */
1399
1321
  export function carryForwardAuthFiles(agent, toConfigDir) {
1400
1322
  const authFiles = AGENTS[agent].authFiles;
1401
1323
  if (!authFiles || authFiles.length === 0)
@@ -1476,6 +1398,7 @@ export function carryForwardAuthFiles(agent, toConfigDir) {
1476
1398
  catch { /* best-effort; a failed carry just means a re-login */ }
1477
1399
  }
1478
1400
  }
1401
+ /** Switch the agent's config symlink to point at a specific version, backing up any real directory first. */
1479
1402
  export async function switchConfigSymlink(agent, version) {
1480
1403
  // Moves the user's real ~/.<agent> aside and symlinks it into a version home.
1481
1404
  assertIsolationBoundary(agent, 'repoint your real config directory');
@@ -1907,34 +1830,19 @@ async function copyDirContents(src, dest, strategy = 'keep-dest', context) {
1907
1830
  }
1908
1831
  }
1909
1832
  }
1910
- /**
1911
- * Check if shim exists for an agent.
1912
- */
1913
- /**
1914
- * The on-disk shim FILENAME for a platform — derived from `shimTargetsFor` (the
1915
- * write-side source of truth) so the exists/remove/version checks can never
1916
- * drift from what `createShim` actually writes: `<cmd>.cmd` on Windows (the only
1917
- * file written there), the bare `<cmd>` script on POSIX. Pure — testable on any
1918
- * host.
1919
- */
1833
+ /** The on-disk shim filename for a platform: `<cmd>.cmd` on Windows, bare `<cmd>` on POSIX. */
1920
1834
  export function onDiskShimFile(cliCommand, platform) {
1921
1835
  return shimTargetsFor(platform).cmd ? `${cliCommand}.cmd` : cliCommand;
1922
1836
  }
1923
- /**
1924
- * The actual on-disk shim path for the current platform. This is what
1925
- * exists/version checks must stat — `getShimPath` returns the logical
1926
- * (extensionless) launch path, which is not always a real file on Windows.
1927
- */
1837
+ /** The actual on-disk shim path for the current platform. */
1928
1838
  function onDiskShimPath(agent) {
1929
1839
  return path.join(getShimsDir(), onDiskShimFile(AGENTS[agent].cliCommand, process.platform));
1930
1840
  }
1841
+ /** Check whether the on-disk shim exists for an agent. */
1931
1842
  export function shimExists(agent) {
1932
1843
  return fs.existsSync(onDiskShimPath(agent));
1933
1844
  }
1934
- /**
1935
- * Read the schema version embedded in an existing on-disk shim. Returns
1936
- * `null` if the shim doesn't exist or has no version marker (pre-v2 shim).
1937
- */
1845
+ /** Read the schema version from the on-disk shim header, or null if missing/unreadable. */
1938
1846
  function readShimSchemaVersion(agent) {
1939
1847
  if (!shimExists(agent))
1940
1848
  return null;
@@ -1951,11 +1859,7 @@ function readShimSchemaVersion(agent) {
1951
1859
  return null;
1952
1860
  }
1953
1861
  }
1954
- /**
1955
- * True if the on-disk shim's schema version matches `SHIM_SCHEMA_VERSION`.
1956
- * False means either the shim is missing, is pre-v2 (no marker), or is an
1957
- * older version that needs regeneration.
1958
- */
1862
+ /** True when the on-disk shim's schema version matches the current schema. */
1959
1863
  export function isShimCurrent(agent) {
1960
1864
  const version = readShimSchemaVersion(agent);
1961
1865
  return version === SHIM_SCHEMA_VERSION;
@@ -2039,22 +1943,13 @@ export function pruneOrphanedCommandShim(fileName) {
2039
1943
  return false;
2040
1944
  }
2041
1945
  }
2042
- /**
2043
- * Regenerate the shim if it's missing or outdated. Returns a status describing
2044
- * what happened — callers can surface a one-line notice to the user ("Updated
2045
- * shim for codex") when appropriate.
2046
- */
1946
+ /** Regenerate the shim if missing or older than the current schema; never downgrade a newer on-disk shim. */
2047
1947
  export function ensureShimCurrent(agent) {
2048
1948
  if (!shimExists(agent)) {
2049
1949
  createShim(agent);
2050
1950
  return 'created';
2051
1951
  }
2052
- // Upgrade-only (newest-wins): regenerate only when the on-disk shim is
2053
- // unversioned/unreadable (null) or OLDER than this binary. Never downgrade a
2054
- // shim stamped by a NEWER agents-cli install. Two installs at different
2055
- // SHIM_SCHEMA_VERSION sharing ~/.agents/.cache/shims/ (e.g. a dev build on
2056
- // PATH alongside a Hermes-bundled published copy) otherwise ping-pong —
2057
- // rewriting every shim on each alternating launch and adding boot latency.
1952
+ // Upgrade-only: avoid ping-pong between two installs sharing the shims dir.
2058
1953
  const onDisk = readShimSchemaVersion(agent);
2059
1954
  if (onDisk === null || onDisk < SHIM_SCHEMA_VERSION) {
2060
1955
  createShim(agent);
@@ -2062,25 +1957,13 @@ export function ensureShimCurrent(agent) {
2062
1957
  }
2063
1958
  return 'current';
2064
1959
  }
2065
- /**
2066
- * Get the path to the shim for an agent.
2067
- */
1960
+ /** Get the logical (extensionless) shim path for an agent. */
2068
1961
  export function getShimPath(agent) {
2069
1962
  const shimsDir = getShimsDir();
2070
1963
  const agentConfig = AGENTS[agent];
2071
1964
  return path.join(shimsDir, agentConfig.cliCommand);
2072
1965
  }
2073
- /**
2074
- * Return the first executable path that would be launched for this agent when
2075
- * resolving against PATH, excluding the managed shim itself.
2076
- *
2077
- * Legacy ~/.agents/shims/<cli> (from the pre-split single-root layout) is NOT
2078
- * treated as a shadow when a current managed shim exists at getShimPath() —
2079
- * that file is dead weight from the old layout and the repair flow removes it
2080
- * separately. Treating it as "shadowing" caused an infinite repair-prompt
2081
- * loop because addShimsToPath() only edits the rc file, never the legacy
2082
- * shim file itself.
2083
- */
1966
+ /** Return the first executable on PATH that would shadow the managed shim, excluding the shim itself and legacy pre-split files. */
2084
1967
  export function getPathShadowingExecutable(agent, overrides) {
2085
1968
  const pathDirs = overrides?.pathDirs ?? (process.env.PATH || '').split(path.delimiter).filter(Boolean);
2086
1969
  const shimPath = path.resolve(overrides?.shimPath ?? getShimPath(agent));
@@ -566,7 +566,7 @@ export function getIsolatedDefault(agent) {
566
566
  *
567
567
  * It lives at the version-dir root (a sibling of `home/`), so it is carried
568
568
  * along when `softDeleteVersionDir` moves the whole directory to trash and is
569
- * restored intact by `agents trash restore`. Its mere presence is the marker;
569
+ * restored intact by `agents restore`. Its mere presence is the marker;
570
570
  * the contents are an informational timestamp only.
571
571
  */
572
572
  function getIsolatedMarkerPath(agent, version) {