@phnx-labs/agents-cli 1.22.40 → 1.22.42

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 (201) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +14 -3
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.js +0 -1
  5. package/dist/cli/command-registry.d.ts +2 -0
  6. package/dist/cli/command-registry.js +5 -2
  7. package/dist/commands/accounts.d.ts +29 -0
  8. package/dist/commands/accounts.js +220 -29
  9. package/dist/commands/alias.js +1 -1
  10. package/dist/commands/auth.d.ts +9 -0
  11. package/dist/commands/auth.js +104 -0
  12. package/dist/commands/browser-sessions-picker.js +42 -62
  13. package/dist/commands/browser.d.ts +15 -0
  14. package/dist/commands/browser.js +145 -78
  15. package/dist/commands/computer-actions.d.ts +1 -1
  16. package/dist/commands/computer-actions.js +1 -1
  17. package/dist/commands/computer-sessions-picker.js +32 -55
  18. package/dist/commands/computer.d.ts +1 -1
  19. package/dist/commands/computer.js +2 -2
  20. package/dist/commands/doctor.d.ts +1 -1
  21. package/dist/commands/doctor.js +3 -3
  22. package/dist/commands/feed.js +4 -12
  23. package/dist/commands/hooks.js +1 -1
  24. package/dist/commands/import.js +1 -1
  25. package/dist/commands/insights.js +163 -85
  26. package/dist/commands/inspect.js +2 -2
  27. package/dist/commands/mine.js +1 -1
  28. package/dist/commands/org.d.ts +6 -0
  29. package/dist/commands/org.js +174 -0
  30. package/dist/commands/packages.js +1 -1
  31. package/dist/commands/prune.js +1 -1
  32. package/dist/commands/routines.js +1 -1
  33. package/dist/commands/run-account-picker.d.ts +19 -0
  34. package/dist/commands/run-account-picker.js +81 -0
  35. package/dist/commands/secrets.js +87 -97
  36. package/dist/commands/sessions-picker-factory.d.ts +40 -0
  37. package/dist/commands/sessions-picker-factory.js +67 -0
  38. package/dist/commands/sessions-picker.d.ts +1 -0
  39. package/dist/commands/sessions-picker.js +27 -5
  40. package/dist/commands/sessions-share.d.ts +25 -0
  41. package/dist/commands/sessions-share.js +166 -0
  42. package/dist/commands/sessions.js +2 -0
  43. package/dist/commands/setup-browser.js +5 -5
  44. package/dist/commands/setup-mine.js +1 -1
  45. package/dist/commands/setup-preferences.js +4 -4
  46. package/dist/commands/setup.js +5 -3
  47. package/dist/commands/sync.js +1 -1
  48. package/dist/commands/versions.js +1 -1
  49. package/dist/commands/view.js +1 -1
  50. package/dist/commands/webhook.js +14 -9
  51. package/dist/lib/account-capabilities.d.ts +10 -0
  52. package/dist/lib/account-capabilities.js +34 -0
  53. package/dist/lib/agent-spec/agents.d.ts +584 -0
  54. package/dist/lib/agent-spec/agents.js +3127 -0
  55. package/dist/lib/agent-spec/provider.js +1 -1
  56. package/dist/lib/agents.d.ts +2 -584
  57. package/dist/lib/agents.js +2 -3127
  58. package/dist/lib/browser/cdp.js +4 -0
  59. package/dist/lib/browser/chrome.js +12 -1
  60. package/dist/lib/browser/drivers/local.d.ts +8 -2
  61. package/dist/lib/browser/drivers/local.js +8 -2
  62. package/dist/lib/browser/drivers/ssh.d.ts +8 -2
  63. package/dist/lib/browser/drivers/ssh.js +11 -4
  64. package/dist/lib/browser/hygiene.d.ts +1 -1
  65. package/dist/lib/browser/hygiene.js +5 -1
  66. package/dist/lib/browser/profiles.d.ts +77 -4
  67. package/dist/lib/browser/profiles.js +121 -12
  68. package/dist/lib/browser/runtime-state.d.ts +11 -5
  69. package/dist/lib/browser/runtime-state.js +13 -9
  70. package/dist/lib/browser/service.d.ts +91 -30
  71. package/dist/lib/browser/service.js +342 -207
  72. package/dist/lib/browser/types.d.ts +96 -4
  73. package/dist/lib/browser/types.js +51 -0
  74. package/dist/lib/catchup.js +1 -1
  75. package/dist/lib/computer/actions.d.ts +1 -1
  76. package/dist/lib/computer/actions.js +1 -1
  77. package/dist/lib/{computer-rpc.js → computer/computer-rpc.js} +5 -5
  78. package/dist/lib/computer/dispatch.d.ts +1 -1
  79. package/dist/lib/computer/download.d.ts +1 -1
  80. package/dist/lib/computer/download.js +3 -3
  81. package/dist/lib/{ssh-tunnel.d.ts → computer/ssh-tunnel.d.ts} +1 -1
  82. package/dist/lib/{ssh-tunnel.js → computer/ssh-tunnel.js} +12 -12
  83. package/dist/lib/daemon/daemon.js +1 -1
  84. package/dist/lib/{runner.d.ts → daemon/runner.d.ts} +10 -10
  85. package/dist/lib/{runner.js → daemon/runner.js} +59 -118
  86. package/dist/lib/daemon-webhooks.d.ts +3 -1
  87. package/dist/lib/daemon-webhooks.js +12 -7
  88. package/dist/lib/device-config.js +1 -1
  89. package/dist/lib/devices/doctor-findings.d.ts +1 -1
  90. package/dist/lib/devices/doctor-findings.js +3 -3
  91. package/dist/lib/devices/fleet-inventory.js +1 -1
  92. package/dist/lib/devices/harness-inventory.js +1 -1
  93. package/dist/lib/devices/registry.js +4 -22
  94. package/dist/lib/doctor-diff.d.ts +1 -1
  95. package/dist/lib/doctor-diff.js +1 -1
  96. package/dist/lib/drift.js +1 -1
  97. package/dist/lib/entitlement.d.ts +31 -0
  98. package/dist/lib/entitlement.js +137 -0
  99. package/dist/lib/exec.js +39 -202
  100. package/dist/lib/feed/feed.js +2 -8
  101. package/dist/lib/fs-atomic.d.ts +7 -0
  102. package/dist/lib/fs-atomic.js +9 -0
  103. package/dist/lib/harness/adapter.d.ts +160 -0
  104. package/dist/lib/harness/adapter.js +47 -0
  105. package/dist/lib/harness/adapters/claude.d.ts +2 -0
  106. package/dist/lib/harness/adapters/claude.js +110 -0
  107. package/dist/lib/harness/adapters/codex.d.ts +2 -0
  108. package/dist/lib/harness/adapters/codex.js +83 -0
  109. package/dist/lib/harness/adapters/copilot.d.ts +2 -0
  110. package/dist/lib/harness/adapters/copilot.js +23 -0
  111. package/dist/lib/harness/adapters/cursor.d.ts +2 -0
  112. package/dist/lib/harness/adapters/cursor.js +38 -0
  113. package/dist/lib/harness/adapters/droid.d.ts +2 -0
  114. package/dist/lib/harness/adapters/droid.js +17 -0
  115. package/dist/lib/harness/adapters/grok.d.ts +2 -0
  116. package/dist/lib/harness/adapters/grok.js +14 -0
  117. package/dist/lib/harness/adapters/kimi.d.ts +2 -0
  118. package/dist/lib/harness/adapters/kimi.js +49 -0
  119. package/dist/lib/harness/adapters/muse.d.ts +2 -0
  120. package/dist/lib/harness/adapters/muse.js +44 -0
  121. package/dist/lib/harness/adapters/opencode.d.ts +2 -0
  122. package/dist/lib/harness/adapters/opencode.js +14 -0
  123. package/dist/lib/harness/exec-config-version.d.ts +8 -0
  124. package/dist/lib/harness/exec-config-version.js +22 -0
  125. package/dist/lib/harness/index.d.ts +1 -0
  126. package/dist/lib/harness/index.js +34 -0
  127. package/dist/lib/{hooks.d.ts → hooks/install.d.ts} +1 -1
  128. package/dist/lib/{hooks.js → hooks/install.js} +11 -11
  129. package/dist/lib/import.js +1 -1
  130. package/dist/lib/installations/index.d.ts +4 -4
  131. package/dist/lib/installations/index.js +3 -4
  132. package/dist/lib/installations/migrate.js +6 -6
  133. package/dist/lib/{shims.d.ts → installations/shims.d.ts} +2 -2
  134. package/dist/lib/{shims.js → installations/shims.js} +12 -114
  135. package/dist/lib/installations/store.d.ts +141 -3
  136. package/dist/lib/installations/store.js +475 -5
  137. package/dist/lib/installations/versions.d.ts +1 -126
  138. package/dist/lib/installations/versions.js +5 -465
  139. package/dist/lib/isolation-boundary-report.d.ts +1 -1
  140. package/dist/lib/isolation-boundary-report.js +1 -1
  141. package/dist/lib/mailbox-gc.js +2 -3
  142. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  143. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  144. package/dist/lib/monitors/dispatch.d.ts +1 -1
  145. package/dist/lib/monitors/dispatch.js +2 -2
  146. package/dist/lib/monitors/state.d.ts +1 -1
  147. package/dist/lib/observe-aliases.d.ts +2 -2
  148. package/dist/lib/observe-aliases.js +2 -11
  149. package/dist/lib/plugins/plugins.js +1 -1
  150. package/dist/lib/plugins/skills.js +1 -1
  151. package/dist/lib/prix-account.d.ts +158 -0
  152. package/dist/lib/prix-account.js +214 -0
  153. package/dist/lib/project-key.js +7 -0
  154. package/dist/lib/project-launch.js +1 -1
  155. package/dist/lib/refresh.js +2 -2
  156. package/dist/lib/resource-inventory.d.ts +1 -1
  157. package/dist/lib/resource-inventory.js +1 -1
  158. package/dist/lib/rules/rules.js +1 -1
  159. package/dist/lib/scheduling/routines.js +1 -1
  160. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  161. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  162. package/dist/lib/secrets/bundles.d.ts +1 -1
  163. package/dist/lib/secrets/bundles.js +1 -1
  164. package/dist/lib/secrets/headless.d.ts +16 -0
  165. package/dist/lib/secrets/headless.js +21 -0
  166. package/dist/lib/secrets/remote.d.ts +9 -7
  167. package/dist/lib/secrets/remote.js +18 -9
  168. package/dist/lib/self-heal/checks/hook-runtime.js +1 -1
  169. package/dist/lib/self-heal/checks/path.js +1 -1
  170. package/dist/lib/self-heal/checks/shadowing.js +1 -1
  171. package/dist/lib/self-heal/checks/shims.js +1 -1
  172. package/dist/lib/session/discover.d.ts +39 -0
  173. package/dist/lib/session/discover.js +3 -3
  174. package/dist/lib/session/parse.js +24 -42
  175. package/dist/lib/session/recovery.js +1 -1
  176. package/dist/lib/session/share-html.d.ts +55 -0
  177. package/dist/lib/session/share-html.js +319 -0
  178. package/dist/lib/settings-manifest.d.ts +2 -0
  179. package/dist/lib/settings-manifest.js +81 -3
  180. package/dist/lib/share/publish.d.ts +38 -0
  181. package/dist/lib/share/publish.js +81 -8
  182. package/dist/lib/staleness/prune.d.ts +1 -1
  183. package/dist/lib/staleness/prune.js +1 -1
  184. package/dist/lib/staleness/writers/hooks.js +1 -1
  185. package/dist/lib/startup/command-registry.d.ts +2 -1
  186. package/dist/lib/startup/command-registry.js +5 -3
  187. package/dist/lib/teams/agents.js +2 -19
  188. package/dist/lib/teams/parsers.js +20 -26
  189. package/dist/lib/teams/registry.js +2 -21
  190. package/dist/lib/triggers/handlers.d.ts +37 -2
  191. package/dist/lib/triggers/handlers.js +57 -6
  192. package/dist/lib/triggers/webhook.d.ts +80 -6
  193. package/dist/lib/triggers/webhook.js +128 -4
  194. package/dist/lib/types.d.ts +1 -1
  195. package/dist/lib/uninstall.js +1 -1
  196. package/dist/lib/version.d.ts +9 -2
  197. package/dist/lib/version.js +11 -9
  198. package/dist/lib/wrap.d.ts +33 -0
  199. package/dist/lib/wrap.js +70 -0
  200. package/package.json +1 -1
  201. /package/dist/lib/{computer-rpc.d.ts → computer/computer-rpc.d.ts} +0 -0
@@ -7,8 +7,7 @@
7
7
  * it, so the internal split can change without breaking callers (the Cursor
8
8
  * per-installation isolation track consumes exactly this).
9
9
  */
10
- export { INSTALLATION_RECORD_FILE, INSTALLATION_SCHEMA, } from './types.js';
11
- export { createInstallation, ensureInstallation, installationDir, installationRecordPath, listInstallationLabels, listInstallations, mintInstallationId, readInstallation, recordRelease, writeInstallation, } from './store.js';
12
- export { InstallationAmbiguousError, InstallationNotFoundError, describeInstallation, resolveInstallation, } from './resolve.js';
13
- export { assertValidRelease, selectUpdateStrategy, supportsPinnedUpdate, } from './strategies.js';
10
+ export { createInstallation, listInstallations, recordRelease, } from './store.js';
11
+ export { describeInstallation, resolveInstallation, } from './resolve.js';
12
+ export { selectUpdateStrategy, supportsPinnedUpdate, } from './strategies.js';
14
13
  export { updateInstallation } from './update.js';
@@ -23,7 +23,7 @@ import { evaluateActivationReadiness } from '../routine-readiness.js';
23
23
  import { migrateDeviceConfigStores } from '../devices/config-migration.js';
24
24
  // Two constants only, never the read/write API — migrations still operate on raw
25
25
  // YAML so they never take the meta lock or prime the meta cache mid-migration.
26
- import { DEFAULT_BROWSER_PROFILE_NAME } from '../browser/profiles.js';
26
+ import { LEGACY_DEFAULT_BROWSER_PROFILE_NAME } from '../browser/profiles.js';
27
27
  import { META_HEADER as DEVICE_META_HEADER } from '../state.js';
28
28
  import { COMPILED_HEADER_PROJECT } from '../rules/compile.js';
29
29
  const HOME = process.env.HOME ?? os.homedir();
@@ -1763,7 +1763,7 @@ export function migrateMachineLocalBrowserProfileOutOfCentral(userDir = USER_DIR
1763
1763
  const browser = central.browser;
1764
1764
  if (!browser || typeof browser !== 'object' || Array.isArray(browser))
1765
1765
  return;
1766
- const entry = browser[DEFAULT_BROWSER_PROFILE_NAME];
1766
+ const entry = browser[LEGACY_DEFAULT_BROWSER_PROFILE_NAME];
1767
1767
  if (entry === undefined)
1768
1768
  return;
1769
1769
  // Device file first — a crash before central is rewritten leaves a harmless
@@ -1777,15 +1777,15 @@ export function migrateMachineLocalBrowserProfileOutOfCentral(userDir = USER_DIR
1777
1777
  const deviceBrowser = (deviceDoc.browser && typeof deviceDoc.browser === 'object' && !Array.isArray(deviceDoc.browser))
1778
1778
  ? deviceDoc.browser
1779
1779
  : {};
1780
- if (deviceBrowser[DEFAULT_BROWSER_PROFILE_NAME] === undefined) {
1781
- deviceBrowser[DEFAULT_BROWSER_PROFILE_NAME] = entry;
1780
+ if (deviceBrowser[LEGACY_DEFAULT_BROWSER_PROFILE_NAME] === undefined) {
1781
+ deviceBrowser[LEGACY_DEFAULT_BROWSER_PROFILE_NAME] = entry;
1782
1782
  deviceDoc.browser = deviceBrowser;
1783
1783
  fs.mkdirSync(path.dirname(devicePath), { recursive: true });
1784
1784
  atomicWriteFileSync(devicePath, DEVICE_META_HEADER + yaml.stringify(deviceDoc));
1785
1785
  }
1786
1786
  // Then strip it from the synced file, dropping `browser:` entirely when the
1787
1787
  // machine-local entry was its only member.
1788
- doc.deleteIn(['browser', DEFAULT_BROWSER_PROFILE_NAME]);
1788
+ doc.deleteIn(['browser', LEGACY_DEFAULT_BROWSER_PROFILE_NAME]);
1789
1789
  if (Object.keys(browser).length === 1) {
1790
1790
  const itemsOf = () => (doc.contents?.items) ?? [];
1791
1791
  const idx = itemsOf().findIndex((pair) => pair.key?.value === 'browser');
@@ -1806,7 +1806,7 @@ export function migrateMachineLocalBrowserProfileOutOfCentral(userDir = USER_DIR
1806
1806
  // the identical case (state.ts, `isEmpty ? META_HEADER : stringifyDoc(doc)`).
1807
1807
  const remaining = Object.keys(doc.toJSON() ?? {}).length;
1808
1808
  atomicWriteFileSync(metaFile, remaining === 0 ? DEVICE_META_HEADER : stringifyDoc(doc));
1809
- console.error(`Migrated agents.yaml: browser '${DEFAULT_BROWSER_PROFILE_NAME}' profile -> devices/${machine}/agents.yaml`);
1809
+ console.error(`Migrated agents.yaml: browser '${LEGACY_DEFAULT_BROWSER_PROFILE_NAME}' profile -> devices/${machine}/agents.yaml`);
1810
1810
  }
1811
1811
  /**
1812
1812
  * Rename the legacy `extras-extras/` plugin-marketplace dir to `agents-extras/`
@@ -1,5 +1,5 @@
1
- import type { AgentId } from './types.js';
2
- import { getShimsDir } from './state.js';
1
+ import type { AgentId } from '../types.js';
2
+ import { getShimsDir } from '../state.js';
3
3
  export { getShimsDir };
4
4
  /**
5
5
  * Strategy for handling file conflicts during config migration.
@@ -13,12 +13,12 @@ import * as path from 'path';
13
13
  import * as os from 'os';
14
14
  import { fileURLToPath } from 'url';
15
15
  import { confirm, select } from '@inquirer/prompts';
16
- import { IS_WINDOWS, prependToWindowsUserPath } from './platform/index.js';
17
- import { getShimsDir, getVersionsDir, getBackupsDir, getHistoryDir, ensureAgentsDir } from './state.js';
16
+ import { IS_WINDOWS, prependToWindowsUserPath } from '../platform/index.js';
17
+ import { getShimsDir, getVersionsDir, getBackupsDir, getHistoryDir, ensureAgentsDir } from '../state.js';
18
18
  export { getShimsDir };
19
- import { AGENTS, agentConfigDirName, readAuthAccountIdentity } from './agents.js';
20
- import { codexHomeShimBash } from './codex-home.js';
21
- import { codexPolicyArgs } from './codex-policy.js';
19
+ import { AGENTS, agentConfigDirName, readAuthAccountIdentity } from '../agents.js';
20
+ import { codexHomeShimBash } from '../codex-home.js';
21
+ import { resolveHarnessAdapter } from '../harness/index.js';
22
22
  /**
23
23
  * Files and directories to always skip during conflict detection and migration.
24
24
  * These are never user config that should be migrated.
@@ -296,52 +296,8 @@ const GROK_RESOLVE_BINARY_FN = `_resolve_grok_binary() {
296
296
  done
297
297
  printf '%s\\n' "$best"
298
298
  }`;
299
- function codexShimLaunchArgs() {
300
- return [
301
- '-c',
302
- 'check_for_update_on_startup=false',
303
- ...codexPolicyArgs('edit'),
304
- ].map(shellQuote).join(' ');
305
- }
306
- /**
307
- * The `exec` tail for a generated shim. For most agents this is a plain
308
- * `exec "$BINARY"<launchArgs> "$@"`.
309
- *
310
- * Codex is special: its `workspace-write` sandbox hardcodes any `.agents/` (and
311
- * `.codex/`) directory read-only, but agents-cli keeps every worktree at
312
- * `<repo>/.agents/worktrees/<slug>`, so an in-repo build under a static
313
- * shim would hit `EROFS`. The `agents run codex` path fixes this by adding
314
- * `<repo>/.agents` to the profile's `workspace_roots` (see codexEditWritableRoots
315
- * / repoAgentsDirForCwd), but a static shim has no cwd at generation time. So the
316
- * shim resolves the repo's `.agents` from `$PWD` at RUN time — worktree-aware,
317
- * mirroring repoAgentsDirForCwd — and passes it via Codex's own `--add-dir`
318
- * (verified to compose with the `agents-edit` profile). The resolution is inline
319
- * bash because the shim is the launch hot path and must not spawn Node to compute
320
- * one directory. `--add-dir` is added only when the resolved `.agents` exists.
321
- */
322
- function shimExecTail(agent, launchArgs) {
323
- if (agent !== 'codex')
324
- return `exec "$BINARY"${launchArgs} "$@"`;
325
- return `_repo_agents=""
326
- case "$PWD" in
327
- */.agents/worktrees/*) _repo_agents="\${PWD%%/.agents/worktrees/*}/.agents" ;;
328
- *)
329
- _d="$PWD"
330
- # Stop before $HOME so a dotfiles repo at $HOME is not treated as the project
331
- # root (mirrors repoRootForCwd's home exclusion in project-key.ts).
332
- while [ -n "$_d" ] && [ "$_d" != "/" ] && [ "$_d" != "$HOME" ]; do
333
- if [ -e "$_d/.git" ]; then _repo_agents="$_d/.agents"; break; fi
334
- _d=$(dirname "$_d")
335
- done
336
- ;;
337
- esac
338
- if [ -n "$_repo_agents" ] && [ -d "$_repo_agents" ]; then
339
- exec "$BINARY"${launchArgs} --add-dir "$_repo_agents" "$@"
340
- fi
341
- exec "$BINARY"${launchArgs} "$@"`;
342
- }
343
299
  function getAgentsBinForGeneratedShim() {
344
- return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'index.js');
300
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'index.js');
345
301
  }
346
302
  /**
347
303
  * Generate the full bash shim script for the given agent. The returned string
@@ -356,66 +312,8 @@ export function generateShimScript(agent) {
356
312
  // subpath so per-version HOME symlinks reach the right place.
357
313
  const configDirName = path.relative(os.homedir(), agentConfig.configDir);
358
314
  const agentsBin = shellQuote(getAgentsBinForGeneratedShim());
359
- const managedEnv = agent === 'claude'
360
- ? `
361
- # Claude stores OAuth credentials in the macOS keychain. Scope them to the
362
- # selected version's config directory so switching versions also switches the
363
- # live Claude account.
364
- export CLAUDE_CONFIG_DIR="$VERSION_DIR/home/${configDirName}"
365
- # Managed installs are pinned in a per-version dir; Claude Code's background
366
- # auto-updater would rewrite the pinned binary in place. Disable it so a pin
367
- # stays a pin. An explicit user value always wins.
368
- export DISABLE_AUTOUPDATER="\${DISABLE_AUTOUPDATER:-1}"
369
- # On Linux sandboxes (no keychain), fall back to a per-version token file.
370
- # The env var always wins if already set; no-op on macOS.
371
- if [ "\$(uname -s)" = "Linux" ] && [ -z "\${CLAUDE_CODE_OAUTH_TOKEN:-}" ] && [ -f "\$CLAUDE_CONFIG_DIR/.oauth_token" ]; then
372
- CLAUDE_CODE_OAUTH_TOKEN=\$(cat "\$CLAUDE_CONFIG_DIR/.oauth_token")
373
- export CLAUDE_CODE_OAUTH_TOKEN
374
- fi
375
- `
376
- : agent === 'codex'
377
- ? codexHomeShimBash(`$VERSION_DIR/home/${configDirName}`, `$AGENTS_USER_DIR/.codex-homes/$VERSION`)
378
- : agent === 'copilot'
379
- ? `
380
- # GitHub Copilot CLI honors COPILOT_HOME to relocate its config and state
381
- # (settings.json, mcp-config.json, session-state/, logs/, plugins/). Point
382
- # it at the versioned home so MCP servers, custom agents, and session
383
- # history are isolated per copilot version.
384
- export COPILOT_HOME="$VERSION_DIR/home/${configDirName}"
385
- `
386
- : agent === 'grok'
387
- ? `
388
- # Grok Build uses GROK_HOME to isolate its entire configuration tree
389
- # (skills, hooks, plugins, agents, memory, sessions, config.toml, MCP, etc.).
390
- # This gives agents-cli full versioned isolation + resource sync for grok.
391
- export GROK_HOME="$VERSION_DIR/home/.grok"
392
- `
393
- : agent === 'opencode'
394
- ? `
395
- # OpenCode reads plugins, agents, commands, and other config-directory
396
- # resources from OPENCODE_CONFIG_DIR. Point it at the versioned global config
397
- # tree where agents-cli syncs OpenCode resources.
398
- export OPENCODE_CONFIG_DIR="$VERSION_DIR/home/.config/opencode"
399
- `
400
- : agent === 'kimi'
401
- ? `
402
- # Kimi Code CLI honors KIMI_CODE_HOME to relocate ~/.kimi-code (config.toml,
403
- # mcp.json, sessions, skills, hooks). Point it at the versioned home.
404
- export KIMI_CODE_HOME="$VERSION_DIR/home/${configDirName}"
405
- `
406
- : agent === 'muse'
407
- ? `
408
- # Muse Code has no MUSE_CONFIG_DIR. It resolves config via XDG:
409
- # $XDG_CONFIG_HOME/muse (settings, skills, hooks, auth)
410
- # $XDG_DATA_HOME/muse (sessions, plugins)
411
- # Pin XDG into the version home so managed runs never walk the adopt-time
412
- # ~/.config/muse -> version-home symlink — Muse refuses agent-definition
413
- # sources that are SymlinkOrReparse (exit 1). Same idea as CLAUDE_CONFIG_DIR.
414
- export XDG_CONFIG_HOME="$VERSION_DIR/home/.config"
415
- export XDG_DATA_HOME="$VERSION_DIR/home/.local/share"
416
- `
417
- : '';
418
- const launchArgs = agent === 'codex' ? ` ${codexShimLaunchArgs()}` : '';
315
+ const managedEnv = resolveHarnessAdapter(agent).shimConfigEnvBash?.({ configDirName }) ?? '';
316
+ const launchArgs = resolveHarnessAdapter(agent).shimLaunchArgs?.() ?? '';
419
317
  return `#!/bin/bash
420
318
  # Auto-generated by agents-cli - do not edit
421
319
  # Shim for ${agentConfig.name}
@@ -831,7 +729,7 @@ if [ "\$LAUNCH_SKIP" = "0" ]; then
831
729
  "\$AGENTS_BIN" sync --agent "\$AGENT" --agent-version "\$VERSION" --launch --cwd "\$PWD" --quiet 2>/dev/null || true
832
730
  fi
833
731
 
834
- ${shimExecTail(agent, launchArgs)}
732
+ ${resolveHarnessAdapter(agent).shimExecTail?.(launchArgs) ?? `exec "$BINARY"${launchArgs} "$@"`}
835
733
  `;
836
734
  }
837
735
  /**
@@ -1145,7 +1043,7 @@ export XDG_DATA_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/.
1145
1043
  export XDG_CONFIG_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/.config"
1146
1044
  `
1147
1045
  : '';
1148
- const launchArgs = agent === 'codex' ? ` ${codexShimLaunchArgs()}` : '';
1046
+ const launchArgs = resolveHarnessAdapter(agent).shimLaunchArgs?.() ?? '';
1149
1047
  // Resolve the binary the same way the main shim does (see generateShimScript).
1150
1048
  // Grok and Droid do NOT ship into node_modules/.bin — Grok downloads a native
1151
1049
  // binary to ~/.grok/downloads and Droid (Factory AI) installs a standalone
@@ -1231,7 +1129,7 @@ if [ -z "$BINARY" ] || [ ! -x "$BINARY" ]; then
1231
1129
  fi
1232
1130
  ${managedEnv}
1233
1131
 
1234
- ${shimExecTail(agent, launchArgs)}
1132
+ ${resolveHarnessAdapter(agent).shimExecTail?.(launchArgs) ?? `exec "$BINARY"${launchArgs} "$@"`}
1235
1133
  `;
1236
1134
  }
1237
1135
  /**
@@ -1639,7 +1537,7 @@ export async function switchConfigSymlink(agent, version) {
1639
1537
  // Dynamic import so loading shims.ts doesn't transitively open the
1640
1538
  // sessions DB — many tests partially mock state.js and would break.
1641
1539
  try {
1642
- const { updateSessionFilePaths } = await import('./session/db.js');
1540
+ const { updateSessionFilePaths } = await import('../session/db.js');
1643
1541
  updateSessionFilePaths(configPath, finalBackupPath);
1644
1542
  }
1645
1543
  catch (err) {
@@ -10,10 +10,12 @@ import { type Installation } from './types.js';
10
10
  * follow. It is also why the file name is registered in versions.ts's
11
11
  * `PRESERVED_ON_CLEAN_REINSTALL` — a repair reinstall must not mint a new id.
12
12
  *
13
- * Deliberately depends on nothing but `state`/`fs-atomic`/`primitives` so
14
- * versions.ts can import it without an import cycle.
13
+ * Path/enumeration plus frozen installation records. Depends on Node builtins,
14
+ * installations-local modules, and base lib (`state`/`fs-atomic`/`primitives`/
15
+ * `agents`/`shims`/`platform`) — never `plugins/`, `rules/`, `session/`, or
16
+ * `devices/`, so those modules can import this file without an import cycle.
15
17
  */
16
- /** Directory holding one installation. Mirrors versions.ts `getVersionDir`. */
18
+ /** Directory holding one installation. Same path as {@link getVersionDir}. */
17
19
  export declare function installationDir(agent: AgentId, label: string): string;
18
20
  export declare function installationRecordPath(agent: AgentId, label: string): string;
19
21
  /** Mint an opaque installation id. Random, never derived from the release. */
@@ -54,3 +56,139 @@ export declare function listInstallationLabels(agent: AgentId): string[];
54
56
  * that disappears mid-scan is skipped rather than failing the whole listing.
55
57
  */
56
58
  export declare function listInstallations(agent: AgentId): Installation[];
59
+ /**
60
+ * Get the directory where a specific version is installed.
61
+ */
62
+ export declare function getVersionDir(agent: AgentId, version: string): string;
63
+ /**
64
+ * Pick the real grok binary among `grok-*` entries in `downloadsDir` when NO
65
+ * filename carries the pinned version string. Grok self-updates its binary in
66
+ * place while running under the shim, so a version-home's downloads dir can
67
+ * accumulate several `grok-*` files whose names have drifted away from that
68
+ * version-home's pinned version (RUSH-2459: on yosemite-s0, version-home
69
+ * `0.2.82` held `grok-1.0.0-linux-aarch64` after grok self-updated, plus a
70
+ * stale, unrelated 99-byte `grok-0.2.118-linux-aarch64` wrapper script that
71
+ * happened to sort alphabetically first).
72
+ *
73
+ * Never blindly take whatever `fs.readdirSync` returns first — that is exactly
74
+ * the bug this replaces. Instead: exclude anything under
75
+ * {@link MIN_GROK_BINARY_BYTES} (rejects wrapper/alias artifacts on size
76
+ * alone, no content sniffing needed) and, among the survivors, prefer the most
77
+ * recently modified — the file grok's self-updater actually wrote last.
78
+ * Returns null (fail loud, never guess) when nothing survives the size filter.
79
+ */
80
+ export declare function resolveGrokFallbackBinary(downloadsDir: string): string | null;
81
+ /**
82
+ * Get the binary path for a specific agent version.
83
+ */
84
+ export declare function getBinaryPath(agent: AgentId, version: string): string;
85
+ /**
86
+ * Does this agent resolve to ONE global binary that is the same file regardless
87
+ * of the `version` argument? (droid → always `~/.local/bin/droid`.) Computed
88
+ * generically by probing `getBinaryPath` with two distinct versions rather than
89
+ * hardcoding an agent id, so it stays correct if another global-binary agent is
90
+ * added.
91
+ *
92
+ * This is the narrower cousin of `isSelfUpdatingAgent`: every global-binary
93
+ * agent is self-updating, but grok is self-updating WITHOUT a global binary — it
94
+ * stores a real per-version binary copy under each version-home
95
+ * (`versions/grok/<v>/home/.grok/downloads/grok-<v>`), so its version-homes are
96
+ * genuinely distinct and must NOT be collapsed. Gate the single-binary
97
+ * collapse/live-version logic on THIS predicate; gate pin-refusal / "switch
98
+ * profile" copy on `isSelfUpdatingAgent`.
99
+ */
100
+ export declare function isGlobalBinaryAgent(agent: AgentId): boolean;
101
+ /** Drop the live-version cache (call after an install/remove that changes the
102
+ * running binary, e.g. `agents add droid@latest`). */
103
+ export declare function invalidateLiveVersionCache(agent?: AgentId): void;
104
+ /**
105
+ * Resolve the version the ONE globally-installed binary actually reports via
106
+ * `<cli> --version`, cached for {@link LIVE_VERSION_TTL_MS}. For a self-updating
107
+ * global-binary agent (droid) this is the single source of truth for "which
108
+ * version is installed" — the on-disk version-dir NAMES are just stale labels
109
+ * left behind by successive `agents add`/self-update cycles. Returns null when
110
+ * the binary isn't on PATH or the probe fails.
111
+ */
112
+ export declare function getCliVersionFromPath(agent: AgentId): Promise<string | null>;
113
+ export declare function getLiveVersion(agent: AgentId): Promise<string | null>;
114
+ /**
115
+ * Synchronous, non-blocking read of the live-version cache — returns the value
116
+ * only if a recent {@link getLiveVersion} call already warmed it, else null.
117
+ * `listInstalledVersions` is sync and must not shell out, so it prefers this
118
+ * warm value (accurate) and otherwise falls back to the newest on-disk dir.
119
+ */
120
+ export declare function getCachedLiveVersion(agent: AgentId): string | null;
121
+ /**
122
+ * Get the isolated HOME directory for a specific agent version.
123
+ * Each version has its own config isolation (like jobs sandbox).
124
+ */
125
+ export declare function getVersionHomePath(agent: AgentId, version: string): string;
126
+ /**
127
+ * Check if a specific version is installed.
128
+ *
129
+ * Probes the actual launch binary (the same executable the shims run), not just
130
+ * the version dir or the node_modules/.bin/<cli> wrapper. For npm agents that
131
+ * means statting the package's real `bin` target (getPackageBinaryPath), so a
132
+ * present-but-gutted install (vendor auto-updater destroyed the binary) reports
133
+ * as NOT installed and `agents add` re-runs its install step to repair it.
134
+ */
135
+ export declare function isVersionInstalled(agent: AgentId, version: string): boolean;
136
+ /** Drop the installed-versions cache (call after install/remove mutations). */
137
+ export declare function invalidateInstalledVersionsCache(agent?: AgentId): void;
138
+ /**
139
+ * Choose the single canonical version-dir to represent a self-updating
140
+ * global-binary agent (droid). All its version dirs map to ONE binary, so
141
+ * exactly one is real; the rest are stale labels. Prefer, in order: the dir the
142
+ * live config symlink points at (what actually runs), the recorded global
143
+ * default, the live `--version` (when the cache is warm), else the newest dir.
144
+ * `versions` MUST be sorted ascending. Callers guarantee it is non-empty.
145
+ */
146
+ export declare function pickCanonicalGlobalBinaryVersion(agent: AgentId, versions: string[]): string;
147
+ /**
148
+ * List all installed versions for an agent (cached by versions-dir mtime).
149
+ *
150
+ * For a self-updating global-binary agent (droid) every version dir resolves to
151
+ * the SAME binary, so this collapses them to a single canonical entry — one
152
+ * install, one row in `agents view`, never the phantom set of semver dir names.
153
+ */
154
+ export declare function listInstalledVersions(agent: AgentId): string[];
155
+ /**
156
+ * Get the global default version for an agent.
157
+ */
158
+ export declare function getGlobalDefault(agent: AgentId): string | null;
159
+ /**
160
+ * Get the preferred ISOLATED version for an agent — the copy a bare
161
+ * `agents run <agent>` falls back to when there is no global default.
162
+ */
163
+ export declare function getIsolatedDefault(agent: AgentId): string | null;
164
+ /**
165
+ * Mark an installed version as an isolated install (`agents add --isolated`).
166
+ *
167
+ * Isolated versions are fully self-contained: they never become the global
168
+ * default and never own the user's real `~/.<agent>` config directory. This
169
+ * flag is what keeps every "adopting" code path away from them.
170
+ */
171
+ export declare function markVersionIsolated(agent: AgentId, version: string): void;
172
+ /**
173
+ * Whether a version was installed as an isolated install (`agents add --isolated`).
174
+ *
175
+ * Used to exclude such versions from global-default promotion and from any
176
+ * flow that would touch the user's real `~/.<agent>` directory, and to gate the
177
+ * `--isolated` safety check on `agents remove`.
178
+ */
179
+ export declare function isVersionIsolated(agent: AgentId, version: string): boolean;
180
+ /**
181
+ * Get version specified in a project-root agents.yaml (not the user ~/.agents/.system/agents.yaml).
182
+ */
183
+ export declare function getProjectVersion(agent: AgentId, startPath: string): string | null;
184
+ /**
185
+ * Get the resolved version for an agent in the current context.
186
+ * Checks project manifest first, then global default.
187
+ */
188
+ export declare function resolveVersion(agent: AgentId, projectPath?: string): string | null;
189
+ /**
190
+ * Get the effective HOME directory for an agent.
191
+ * If version-managed with a resolved version, returns the version's home directory.
192
+ * Otherwise returns the real HOME.
193
+ */
194
+ export declare function getEffectiveHome(agentId: AgentId): string;