@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
@@ -32,14 +32,15 @@ import { VERSION_RE, compareVersions } from '../agent-spec/primitives.js';
32
32
  import { AGENTS, agentConfigDirName, getAccountEmail, resolveAgentName, formatAgentError, findInPath, isSelfUpdatingAgent, isAgentHardDeprecated, hardDeprecationError } from '../agents.js';
33
33
  import { discoverPermissionGroups, getActivePermissionPresetName, readPermissionPresetRecipe, PERMISSION_PRESET_ENV_VAR } from '../permissions.js';
34
34
  import { parseMcpConfigForScan, isProjectMcpTrusted } from '../mcp.js';
35
- import { createVersionedAlias, removeVersionedAlias, getConfigSymlinkVersion, ensureClaudeInsideSymlink, assertIsolationBoundary, } from '../shims.js';
35
+ import { createVersionedAlias, removeVersionedAlias, getConfigSymlinkVersion, ensureClaudeInsideSymlink, assertIsolationBoundary, } from './shims.js';
36
36
  import { importInstallScriptBinary } from '../import.js';
37
- import { createInstallation } from './store.js';
37
+ import { createInstallation, getBinaryPath, getCliVersionFromPath, getGlobalDefault, getIsolatedDefault, getLiveVersion, getVersionDir, getVersionHomePath, invalidateInstalledVersionsCache, invalidateLiveVersionCache, isGlobalBinaryAgent, isVersionInstalled, isVersionIsolated, listInstalledVersions, pickCanonicalGlobalBinaryVersion, } from './store.js';
38
+ export * from './store.js';
38
39
  import { INSTALLATION_RECORD_FILE } from './types.js';
39
- import { IS_WINDOWS, composeWin32CommandLine } from '../platform/index.js';
40
+ import { composeWin32CommandLine } from '../platform/index.js';
40
41
  import { listInstalledSubagents } from '../subagents.js';
41
42
  import { listInstalledWorkflows } from '../workflows.js';
42
- import { parseHookManifest, registerHooksToSettings, selectHookManifest, pruneVersionHomeHookEntriesFromSettings, installSessionTrackerHookSync, installSessionTrackerHook } from '../hooks.js';
43
+ import { parseHookManifest, registerHooksToSettings, selectHookManifest, pruneVersionHomeHookEntriesFromSettings, installSessionTrackerHookSync, installSessionTrackerHook } from '../hooks/install.js';
43
44
  import { supports, explainSkip } from '../capabilities.js';
44
45
  import { discoverPlugins, syncPluginToVersion, pluginSupportsAgent, cleanOrphanedPluginSkills, marketplaceSpecForName } from '../plugins/plugins.js';
45
46
  import { loadManifest, saveManifest, buildManifest as buildSyncManifest, isStale } from '../staleness/index.js';
@@ -891,252 +892,6 @@ export function parseAgentSpec(spec) {
891
892
  version,
892
893
  };
893
894
  }
894
- /**
895
- * Get the directory where a specific version is installed.
896
- */
897
- export function getVersionDir(agent, version) {
898
- return path.join(getVersionsDir(), agent, version);
899
- }
900
- /**
901
- * Grok binaries are never trusted below this size when picking among several
902
- * `grok-*` candidates with no exact version match (see
903
- * {@link resolveGrokFallbackBinary}). Grok's real native binary is ~100MB+; a
904
- * stray non-binary artifact sharing the `grok-*` naming pattern (a wrapper or
905
- * alias script) is at most a few hundred bytes, so 1MB comfortably separates
906
- * the two without depending on file content.
907
- */
908
- const MIN_GROK_BINARY_BYTES = 1_000_000;
909
- /**
910
- * Pick the real grok binary among `grok-*` entries in `downloadsDir` when NO
911
- * filename carries the pinned version string. Grok self-updates its binary in
912
- * place while running under the shim, so a version-home's downloads dir can
913
- * accumulate several `grok-*` files whose names have drifted away from that
914
- * version-home's pinned version (RUSH-2459: on yosemite-s0, version-home
915
- * `0.2.82` held `grok-1.0.0-linux-aarch64` after grok self-updated, plus a
916
- * stale, unrelated 99-byte `grok-0.2.118-linux-aarch64` wrapper script that
917
- * happened to sort alphabetically first).
918
- *
919
- * Never blindly take whatever `fs.readdirSync` returns first — that is exactly
920
- * the bug this replaces. Instead: exclude anything under
921
- * {@link MIN_GROK_BINARY_BYTES} (rejects wrapper/alias artifacts on size
922
- * alone, no content sniffing needed) and, among the survivors, prefer the most
923
- * recently modified — the file grok's self-updater actually wrote last.
924
- * Returns null (fail loud, never guess) when nothing survives the size filter.
925
- */
926
- export function resolveGrokFallbackBinary(downloadsDir) {
927
- let entries;
928
- try {
929
- entries = fs.readdirSync(downloadsDir);
930
- }
931
- catch {
932
- return null;
933
- }
934
- let best = null;
935
- for (const entry of entries) {
936
- if (!entry.startsWith('grok-'))
937
- continue;
938
- const full = path.join(downloadsDir, entry);
939
- let stat;
940
- try {
941
- stat = fs.statSync(full);
942
- }
943
- catch {
944
- continue;
945
- }
946
- if (!stat.isFile() || stat.size < MIN_GROK_BINARY_BYTES)
947
- continue;
948
- if (!best || stat.mtimeMs > best.mtimeMs)
949
- best = { name: entry, mtimeMs: stat.mtimeMs };
950
- }
951
- return best ? path.join(downloadsDir, best.name) : null;
952
- }
953
- /**
954
- * Get the binary path for a specific agent version.
955
- */
956
- export function getBinaryPath(agent, version) {
957
- const agentConfig = AGENTS[agent];
958
- if (agent === 'grok') {
959
- const grokDownloads = path.join(getVersionHomePath(agent, version), '.grok', 'downloads');
960
- // Best effort: first matching file for this version
961
- try {
962
- const entries = fs.readdirSync(grokDownloads);
963
- const match = entries.find((e) => e.includes(version) && e.startsWith('grok-'));
964
- if (match)
965
- return path.join(grokDownloads, match);
966
- }
967
- catch { }
968
- const fallback = resolveGrokFallbackBinary(grokDownloads);
969
- if (fallback)
970
- return fallback;
971
- return path.join(grokDownloads, `grok-${version}`);
972
- }
973
- if (agent === 'droid') {
974
- // Factory.ai's installer drops a standalone native binary (no npm package,
975
- // nothing in node_modules/.bin). The binary is global, not per-version —
976
- // config isolation rides the ~/.factory symlink switch, not a separate
977
- // binary per version. Install location is platform-specific:
978
- // macOS/Linux: ~/.local/bin/droid (curl app.factory.ai/cli | sh)
979
- // Windows: %USERPROFILE%\bin\droid.exe (irm app.factory.ai/cli/windows | iex)
980
- // Mirror the shim's `droid` branch so isVersionInstalled/`agents view`
981
- // agree with what actually executes.
982
- return IS_WINDOWS
983
- ? path.join(getHomeDir(), 'bin', 'droid.exe')
984
- : path.join(getHomeDir(), '.local', 'bin', 'droid');
985
- }
986
- if (agent === 'muse') {
987
- // Muse Code install script drops a self-updating launcher at
988
- // ~/.local/bin/muse (curl -fsSL https://dev.meta.ai/install.sh | sh).
989
- // Same global-binary shape as droid: one binary for every version dir,
990
- // config isolation via version-home HOME rewrite. Mirror the shim's
991
- // `muse` branch so isVersionInstalled / agents view / agents import
992
- // agree with what executes.
993
- return IS_WINDOWS
994
- ? path.join(getHomeDir(), 'bin', 'muse.exe')
995
- : path.join(getHomeDir(), '.local', 'bin', 'muse');
996
- }
997
- if (agent === 'warp') {
998
- // Warp Agent CLI installs a single global, self-updating `warp` binary at
999
- // ~/.local/bin/warp via the curl installer (Windows: the agent-cli.ps1) —
1000
- // like droid/muse. Resolve the real binary on PATH (findInPath skips our own
1001
- // shims dir) so isVersionInstalled / agents view agree with what executes.
1002
- // When warp is not installed, fall back to its default install path so
1003
- // isVersionInstalled reports uninstalled honestly.
1004
- const onPath = findInPath('warp');
1005
- if (onPath)
1006
- return onPath;
1007
- return IS_WINDOWS
1008
- ? path.join(getHomeDir(), 'bin', 'warp.exe')
1009
- : path.join(getHomeDir(), '.local', 'bin', 'warp');
1010
- }
1011
- const versionDir = getVersionDir(agent, version);
1012
- return path.join(versionDir, 'node_modules', '.bin', agentConfig.cliCommand);
1013
- }
1014
- /**
1015
- * Does this agent resolve to ONE global binary that is the same file regardless
1016
- * of the `version` argument? (droid → always `~/.local/bin/droid`.) Computed
1017
- * generically by probing `getBinaryPath` with two distinct versions rather than
1018
- * hardcoding an agent id, so it stays correct if another global-binary agent is
1019
- * added.
1020
- *
1021
- * This is the narrower cousin of `isSelfUpdatingAgent`: every global-binary
1022
- * agent is self-updating, but grok is self-updating WITHOUT a global binary — it
1023
- * stores a real per-version binary copy under each version-home
1024
- * (`versions/grok/<v>/home/.grok/downloads/grok-<v>`), so its version-homes are
1025
- * genuinely distinct and must NOT be collapsed. Gate the single-binary
1026
- * collapse/live-version logic on THIS predicate; gate pin-refusal / "switch
1027
- * profile" copy on `isSelfUpdatingAgent`.
1028
- */
1029
- export function isGlobalBinaryAgent(agent) {
1030
- return getBinaryPath(agent, '0.0.0-probe-a') === getBinaryPath(agent, '0.0.0-probe-b');
1031
- }
1032
- // Live-version cache for self-updating global binaries. `<cli> --version` is a
1033
- // ~real shell-out, so hold the result briefly: the same `agents view` render
1034
- // asks for it from both listInstalledVersions (sync) and the label path (async).
1035
- const LIVE_VERSION_TTL_MS = 5000;
1036
- const liveVersionCache = new Map();
1037
- /** Drop the live-version cache (call after an install/remove that changes the
1038
- * running binary, e.g. `agents add droid@latest`). */
1039
- export function invalidateLiveVersionCache(agent) {
1040
- if (agent)
1041
- liveVersionCache.delete(agent);
1042
- else
1043
- liveVersionCache.clear();
1044
- }
1045
- /**
1046
- * Resolve the version the ONE globally-installed binary actually reports via
1047
- * `<cli> --version`, cached for {@link LIVE_VERSION_TTL_MS}. For a self-updating
1048
- * global-binary agent (droid) this is the single source of truth for "which
1049
- * version is installed" — the on-disk version-dir NAMES are just stale labels
1050
- * left behind by successive `agents add`/self-update cycles. Returns null when
1051
- * the binary isn't on PATH or the probe fails.
1052
- */
1053
- export async function getLiveVersion(agent) {
1054
- const cached = liveVersionCache.get(agent);
1055
- if (cached && Date.now() - cached.at < LIVE_VERSION_TTL_MS)
1056
- return cached.version;
1057
- const version = await getCliVersionFromPath(agent);
1058
- liveVersionCache.set(agent, { at: Date.now(), version });
1059
- return version;
1060
- }
1061
- /**
1062
- * Synchronous, non-blocking read of the live-version cache — returns the value
1063
- * only if a recent {@link getLiveVersion} call already warmed it, else null.
1064
- * `listInstalledVersions` is sync and must not shell out, so it prefers this
1065
- * warm value (accurate) and otherwise falls back to the newest on-disk dir.
1066
- */
1067
- export function getCachedLiveVersion(agent) {
1068
- const cached = liveVersionCache.get(agent);
1069
- if (cached && Date.now() - cached.at < LIVE_VERSION_TTL_MS)
1070
- return cached.version;
1071
- return null;
1072
- }
1073
- /**
1074
- * Get the isolated HOME directory for a specific agent version.
1075
- * Each version has its own config isolation (like jobs sandbox).
1076
- */
1077
- export function getVersionHomePath(agent, version) {
1078
- return path.join(getVersionDir(agent, version), 'home');
1079
- }
1080
- /**
1081
- * Resolve the REAL launch binary for an npm-package agent version: the file the
1082
- * installed package's `bin` entry points to — e.g.
1083
- * node_modules/@anthropic-ai/claude-code/bin/claude.exe on Windows. This is the
1084
- * executable that the node_modules/.bin/<cli>.cmd wrapper ultimately execs (and
1085
- * what `agents run` spawns), NOT the wrapper itself.
1086
- *
1087
- * The distinction is load-bearing: npm leaves the tiny node_modules/.bin/<cli>
1088
- * and <cli>.cmd wrappers in place even after a vendor auto-updater destroys the
1089
- * multi-hundred-MB real binary the wrapper points at. Keying "installed" on the
1090
- * wrapper (getBinaryPath) — or on the version dir — therefore reports a gutted
1091
- * install as healthy, and `agents run` then dies at spawn with
1092
- * "'...claude.exe' is not recognized".
1093
- *
1094
- * Returns null for agents without an npm package (grok/droid/installScript,
1095
- * whose getBinaryPath already resolves the real per-host binary) or when the
1096
- * installed package.json can't be read/parsed — callers fall back to the
1097
- * generic getBinaryPath check in those cases.
1098
- */
1099
- function getPackageBinaryPath(agent, version) {
1100
- const agentConfig = AGENTS[agent];
1101
- if (!agentConfig.npmPackage)
1102
- return null;
1103
- const pkgRoot = path.join(getVersionDir(agent, version), 'node_modules', agentConfig.npmPackage);
1104
- let bin;
1105
- try {
1106
- const pkg = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'package.json'), 'utf-8'));
1107
- bin = pkg.bin;
1108
- }
1109
- catch {
1110
- return null;
1111
- }
1112
- let rel;
1113
- if (typeof bin === 'string') {
1114
- rel = bin;
1115
- }
1116
- else if (bin && typeof bin === 'object') {
1117
- const map = bin;
1118
- // Prefer the entry named after our launch command; else the first bin.
1119
- rel = map[agentConfig.cliCommand] ?? Object.values(map)[0];
1120
- }
1121
- if (!rel || typeof rel !== 'string')
1122
- return null;
1123
- return path.join(pkgRoot, rel);
1124
- }
1125
- /**
1126
- * Check if a specific version is installed.
1127
- *
1128
- * Probes the actual launch binary (the same executable the shims run), not just
1129
- * the version dir or the node_modules/.bin/<cli> wrapper. For npm agents that
1130
- * means statting the package's real `bin` target (getPackageBinaryPath), so a
1131
- * present-but-gutted install (vendor auto-updater destroyed the binary) reports
1132
- * as NOT installed and `agents add` re-runs its install step to repair it.
1133
- */
1134
- export function isVersionInstalled(agent, version) {
1135
- const packageBinary = getPackageBinaryPath(agent, version);
1136
- if (packageBinary !== null)
1137
- return fs.existsSync(packageBinary);
1138
- return fs.existsSync(getBinaryPath(agent, version));
1139
- }
1140
895
  /**
1141
896
  * Get the latest available version from npm for an agent.
1142
897
  */
@@ -1192,89 +947,6 @@ export async function isOldestInstalled(agent) {
1192
947
  }
1193
948
  return { installed: isVersionInstalled(agent, oldestVersion), version: oldestVersion };
1194
949
  }
1195
- // Per-process cache for listInstalledVersions. The agent's versions dir mtime
1196
- // changes whenever a version dir is added or removed (install/remove), so a
1197
- // stamp match means the installed set is unchanged and we skip the readdir +
1198
- // N binary stats. Mirrors the readMeta() cache in state.ts. Hot path:
1199
- // resolveAgentTargets and every enumerate-style consumer hit this.
1200
- const installedVersionsCache = new Map();
1201
- /** Drop the installed-versions cache (call after install/remove mutations). */
1202
- export function invalidateInstalledVersionsCache(agent) {
1203
- if (agent)
1204
- installedVersionsCache.delete(agent);
1205
- else
1206
- installedVersionsCache.clear();
1207
- }
1208
- /**
1209
- * Choose the single canonical version-dir to represent a self-updating
1210
- * global-binary agent (droid). All its version dirs map to ONE binary, so
1211
- * exactly one is real; the rest are stale labels. Prefer, in order: the dir the
1212
- * live config symlink points at (what actually runs), the recorded global
1213
- * default, the live `--version` (when the cache is warm), else the newest dir.
1214
- * `versions` MUST be sorted ascending. Callers guarantee it is non-empty.
1215
- */
1216
- function pickCanonicalGlobalBinaryVersion(agent, versions) {
1217
- const symlinkVersion = getConfigSymlinkVersion(agent);
1218
- if (symlinkVersion && versions.includes(symlinkVersion))
1219
- return symlinkVersion;
1220
- const globalDefault = getGlobalDefault(agent);
1221
- if (globalDefault && versions.includes(globalDefault))
1222
- return globalDefault;
1223
- const live = getCachedLiveVersion(agent);
1224
- if (live && versions.includes(live))
1225
- return live;
1226
- return versions[versions.length - 1];
1227
- }
1228
- /**
1229
- * Collapse a global-binary agent's phantom version dirs to the single canonical
1230
- * one (see {@link pickCanonicalGlobalBinaryVersion}). No-op for npm-packaged and
1231
- * per-version agents (claude/codex/grok/…), whose version dirs are genuinely
1232
- * distinct installs.
1233
- */
1234
- function collapseGlobalBinaryVersions(agent, versions) {
1235
- if (!isGlobalBinaryAgent(agent) || versions.length === 0)
1236
- return versions;
1237
- return [pickCanonicalGlobalBinaryVersion(agent, versions)];
1238
- }
1239
- /**
1240
- * List all installed versions for an agent (cached by versions-dir mtime).
1241
- *
1242
- * For a self-updating global-binary agent (droid) every version dir resolves to
1243
- * the SAME binary, so this collapses them to a single canonical entry — one
1244
- * install, one row in `agents view`, never the phantom set of semver dir names.
1245
- */
1246
- export function listInstalledVersions(agent) {
1247
- const agentVersionsDir = path.join(getVersionsDir(), agent);
1248
- let stamp;
1249
- try {
1250
- stamp = fs.statSync(agentVersionsDir).mtimeMs;
1251
- }
1252
- catch {
1253
- installedVersionsCache.set(agent, { stamp: 0, versions: [] });
1254
- return [];
1255
- }
1256
- const cached = installedVersionsCache.get(agent);
1257
- if (cached && cached.stamp === stamp) {
1258
- // Collapse is applied per-call (not cached): it depends on the live-version
1259
- // cache + config symlink, which can change without the versions-dir mtime.
1260
- return collapseGlobalBinaryVersions(agent, cached.versions);
1261
- }
1262
- const entries = fs.readdirSync(agentVersionsDir, { withFileTypes: true });
1263
- const versions = [];
1264
- for (const entry of entries) {
1265
- if (entry.isDirectory()) {
1266
- // Probe the real launch binary (isVersionInstalled), not just the
1267
- // node_modules/.bin wrapper — a gutted install must not count as healthy
1268
- // in the balanced account/version picker.
1269
- if (isVersionInstalled(agent, entry.name)) {
1270
- versions.push(entry.name);
1271
- }
1272
- }
1273
- }
1274
- versions.sort(compareVersions);
1275
- installedVersionsCache.set(agent, { stamp, versions });
1276
- return collapseGlobalBinaryVersions(agent, versions);
1277
- }
1278
950
  /**
1279
951
  * List every version directory for an agent, including ones missing the
1280
952
  * binary (typically home-only leftovers from a prior `removeVersion`).
@@ -1300,13 +972,6 @@ export function listInstalledVersionDirs(agent) {
1300
972
  }
1301
973
  return out.sort((a, b) => compareVersions(a.version, b.version));
1302
974
  }
1303
- /**
1304
- * Get the global default version for an agent.
1305
- */
1306
- export function getGlobalDefault(agent) {
1307
- const meta = readMeta();
1308
- return meta.agents?.[agent] || null;
1309
- }
1310
975
  /**
1311
976
  * Set the global default version for an agent.
1312
977
  */
@@ -1332,14 +997,6 @@ export function setGlobalDefault(agent, version) {
1332
997
  }
1333
998
  writeMeta(meta);
1334
999
  }
1335
- /**
1336
- * Get the preferred ISOLATED version for an agent — the copy a bare
1337
- * `agents run <agent>` falls back to when there is no global default.
1338
- */
1339
- export function getIsolatedDefault(agent) {
1340
- const meta = readMeta();
1341
- return meta.isolatedAgents?.[agent] || null;
1342
- }
1343
1000
  /**
1344
1001
  * Set (or clear, with `undefined`) the preferred isolated version.
1345
1002
  *
@@ -1360,37 +1017,6 @@ export function setIsolatedDefault(agent, version) {
1360
1017
  }
1361
1018
  writeMeta(meta);
1362
1019
  }
1363
- /**
1364
- * Path to the sentinel file that marks a version as an isolated install.
1365
- *
1366
- * It lives at the version-dir root (a sibling of `home/`), so it is carried
1367
- * along when `softDeleteVersionDir` moves the whole directory to trash and is
1368
- * restored intact by `agents trash restore`. Its mere presence is the marker;
1369
- * the contents are an informational timestamp only.
1370
- */
1371
- function getIsolatedMarkerPath(agent, version) {
1372
- return path.join(getVersionDir(agent, version), '.isolated');
1373
- }
1374
- /**
1375
- * Mark an installed version as an isolated install (`agents add --isolated`).
1376
- *
1377
- * Isolated versions are fully self-contained: they never become the global
1378
- * default and never own the user's real `~/.<agent>` config directory. This
1379
- * flag is what keeps every "adopting" code path away from them.
1380
- */
1381
- export function markVersionIsolated(agent, version) {
1382
- fs.writeFileSync(getIsolatedMarkerPath(agent, version), `${new Date().toISOString()}\n`, { mode: 0o600 });
1383
- }
1384
- /**
1385
- * Whether a version was installed as an isolated install (`agents add --isolated`).
1386
- *
1387
- * Used to exclude such versions from global-default promotion and from any
1388
- * flow that would touch the user's real `~/.<agent>` directory, and to gate the
1389
- * `--isolated` safety check on `agents remove`.
1390
- */
1391
- export function isVersionIsolated(agent, version) {
1392
- return fs.existsSync(getIsolatedMarkerPath(agent, version));
1393
- }
1394
1020
  /**
1395
1021
  * Move any `grok-<installedVersion>-...` binary (and its generic platform
1396
1022
  * copy) found directly under `sourceDownloads` into `targetDownloads`.
@@ -2119,37 +1745,6 @@ export function removeAllVersions(agent) {
2119
1745
  }
2120
1746
  return removed;
2121
1747
  }
2122
- /**
2123
- * Get the resolved version for an agent in the current context.
2124
- * Checks project manifest first, then global default.
2125
- */
2126
- export function resolveVersion(agent, projectPath) {
2127
- // Check project manifest
2128
- if (projectPath) {
2129
- const version = getProjectVersion(agent, projectPath);
2130
- if (version) {
2131
- return version;
2132
- }
2133
- }
2134
- // Fall back to global default
2135
- const globalDefault = getGlobalDefault(agent);
2136
- if (globalDefault)
2137
- return globalDefault;
2138
- // Last resort: the preferred isolated copy. Strictly a fallback — a global
2139
- // default always wins, so nothing changes for anyone who has one. Without this,
2140
- // an isolated-only user cannot reach their installs by bare name at all: the
2141
- // resolution chain ended here, so `agents run codex` fell through to whatever
2142
- // `codex` meant on PATH, and only `agents run codex@<version>` worked.
2143
- //
2144
- // The pointer is verified on read. It survives in agents.yaml across a trash +
2145
- // restore cycle, but a version removed for good would otherwise leave a dangling
2146
- // pin that resolves to a directory that is not there.
2147
- const isolated = getIsolatedDefault(agent);
2148
- if (isolated && isVersionInstalled(agent, isolated) && isVersionIsolated(agent, isolated)) {
2149
- return isolated;
2150
- }
2151
- return null;
2152
- }
2153
1748
  /**
2154
1749
  * Normalize a user-supplied @version token across CLI subcommands.
2155
1750
  *
@@ -2208,38 +1803,6 @@ export function resolveVersionAliasLoose(agent, raw) {
2208
1803
  }
2209
1804
  return raw;
2210
1805
  }
2211
- /**
2212
- * Get version specified in a project-root agents.yaml (not the user ~/.agents/.system/agents.yaml).
2213
- */
2214
- export function getProjectVersion(agent, startPath) {
2215
- const userAgentsYaml = path.join(getUserAgentsDir(), 'agents.yaml');
2216
- let dir = path.resolve(startPath);
2217
- while (dir !== path.dirname(dir)) {
2218
- const manifestPath = path.join(dir, 'agents.yaml');
2219
- if (manifestPath !== userAgentsYaml && fs.existsSync(manifestPath)) {
2220
- try {
2221
- const content = fs.readFileSync(manifestPath, 'utf-8');
2222
- const parsed = yaml.parse(content);
2223
- const version = parsed?.agents?.[agent];
2224
- if (typeof version === 'string' && version.trim()) {
2225
- const normalized = version.trim();
2226
- if (!VERSION_RE.test(normalized)) {
2227
- throw new Error(`Invalid version in agents.yaml for ${agent}: ${normalized}. Allowed: latest or [A-Za-z0-9._+-]{1,64}`);
2228
- }
2229
- return normalized;
2230
- }
2231
- }
2232
- catch (err) {
2233
- if (err instanceof Error && err.message.startsWith('Invalid version in agents.yaml')) {
2234
- throw err;
2235
- }
2236
- // Ignore parsing errors
2237
- }
2238
- }
2239
- dir = path.dirname(dir);
2240
- }
2241
- return null;
2242
- }
2243
1806
  // compareVersions is defined in ./agent-spec/primitives.ts and re-exported here
2244
1807
  // so existing `import { compareVersions } from './versions.js'` sites keep working.
2245
1808
  export { compareVersions };
@@ -2531,17 +2094,6 @@ export async function healBrokenDefaultLaunches(log, opts) {
2531
2094
  }
2532
2095
  return { repaired, unhealed };
2533
2096
  }
2534
- async function getCliVersionFromPath(agent) {
2535
- const agentConfig = AGENTS[agent];
2536
- try {
2537
- const { stdout } = await execFileAsync(agentConfig.cliCommand, ['--version'], { timeout: 3000, shell: process.platform === 'win32' });
2538
- const match = stdout.match(/(\d+\.\d+\.\d+)/);
2539
- return match ? match[1] : null;
2540
- }
2541
- catch {
2542
- return null;
2543
- }
2544
- }
2545
2097
  /**
2546
2098
  * Enumerate the DotAgent repo names that resources can be scoped to:
2547
2099
  * the fixed `project` / `user` / `system` layers plus every enabled extra
@@ -3252,18 +2804,6 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
3252
2804
  }
3253
2805
  return result;
3254
2806
  }
3255
- /**
3256
- * Get the effective HOME directory for an agent.
3257
- * If version-managed with a resolved version, returns the version's home directory.
3258
- * Otherwise returns the real HOME.
3259
- */
3260
- export function getEffectiveHome(agentId) {
3261
- const resolved = resolveVersion(agentId, process.cwd());
3262
- if (resolved && isVersionInstalled(agentId, resolved)) {
3263
- return getVersionHomePath(agentId, resolved);
3264
- }
3265
- return getHomeDir();
3266
- }
3267
2807
  /**
3268
2808
  * Thrown when the user references an agent@version that is not installed.
3269
2809
  * Carries the parsed (agentId, version) so callers can react — e.g. prompt
@@ -1,4 +1,4 @@
1
- import { IsolationBoundaryError } from './shims.js';
1
+ import { IsolationBoundaryError } from './installations/shims.js';
2
2
  /**
3
3
  * Turn an {@link IsolationBoundaryError} into guidance. The boundary is enforced by a
4
4
  * throw so it cannot be forgotten; this is what keeps that from surfacing as a stack
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { IsolationBoundaryError } from './shims.js';
2
+ import { IsolationBoundaryError } from './installations/shims.js';
3
3
  import { listInstalledVersions } from './installations/versions.js';
4
4
  /**
5
5
  * Turn an {@link IsolationBoundaryError} into guidance. The boundary is enforced by a
@@ -11,6 +11,7 @@ import * as path from 'path';
11
11
  import { getFeedDir, getMailboxRootDir } from './state.js';
12
12
  import { mailboxDir, isValidMailboxId, readMessage, sweepExpired, } from './mailbox.js';
13
13
  import { listBlocks, removeBlock, recordMessageReceipt } from './feed/feed.js';
14
+ import { atomicWriteJsonSync } from './fs-atomic.js';
14
15
  const DEFAULT_MAX_CONSUMED_AGE_MINUTES = 24 * 60;
15
16
  function consumedAgeMinutes(file, now) {
16
17
  try {
@@ -41,9 +42,7 @@ function archiveAllPending(boxDir, reason, feedRoot) {
41
42
  const msg = readMessage(src);
42
43
  if (msg) {
43
44
  msg.dropped = reason;
44
- const tmp = `${dest}.${process.pid}.tmp`;
45
- fs.writeFileSync(tmp, JSON.stringify(msg, null, 2), 'utf-8');
46
- fs.renameSync(tmp, dest);
45
+ atomicWriteJsonSync(dest, msg);
47
46
  fs.unlinkSync(src);
48
47
  if (msg.blockId) {
49
48
  try {
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * On a fire, the monitor feeds the event to an action. Every `run`/`routine`
5
5
  * action goes through the *same* detached spawn cron and webhook fires use
6
- * (executeJobDetached, lib/runner.ts) — a monitor never duplicates spawn logic,
6
+ * (executeJobDetached, lib/daemon/runner.ts) — a monitor never duplicates spawn logic,
7
7
  * it synthesizes a JobConfig and hands it to the one dispatch seam. `notify`
8
8
  * routes the owner through the one channel seam (sendToOwner → lookupTransport,
9
9
  * lib/notify.ts) — recipient from notify.owner, no hardcoded chat id, and an
@@ -3,14 +3,14 @@
3
3
  *
4
4
  * On a fire, the monitor feeds the event to an action. Every `run`/`routine`
5
5
  * action goes through the *same* detached spawn cron and webhook fires use
6
- * (executeJobDetached, lib/runner.ts) — a monitor never duplicates spawn logic,
6
+ * (executeJobDetached, lib/daemon/runner.ts) — a monitor never duplicates spawn logic,
7
7
  * it synthesizes a JobConfig and hands it to the one dispatch seam. `notify`
8
8
  * routes the owner through the one channel seam (sendToOwner → lookupTransport,
9
9
  * lib/notify.ts) — recipient from notify.owner, no hardcoded chat id, and an
10
10
  * unresolvable channel comes back as `ok: false` instead of exiting the daemon;
11
11
  * `webhook-out` POSTs the event.
12
12
  */
13
- import { executeJobDetached } from '../runner.js';
13
+ import { executeJobDetached } from '../daemon/runner.js';
14
14
  import { readJob } from '../scheduling/routines.js';
15
15
  import { sendToOwner } from '../notify.js';
16
16
  /** Replace `{event}` in a prompt with the fired event summary. */
@@ -110,7 +110,7 @@ export interface FireRecord extends MonitorEvent {
110
110
  * snapshot: `executeJobDetached` writes `status: 'running'` before spawning
111
111
  * and returns immediately — the real outcome (`completed`/`failed`/`timeout`)
112
112
  * lands later, asynchronously, in `executeJobDetachedClaimed`'s own
113
- * `settle()` on child exit/error (lib/runner.ts). So a fire recorded
113
+ * `settle()` on child exit/error (lib/daemon/runner.ts). So a fire recorded
114
114
  * `runStatusAtFire: 'running'` had its `ok` frozen before the run actually
115
115
  * finished — that is the signal a future reconciliation pass (a daemon tick
116
116
  * that revisits `running`-at-fire records and patches `ok` for real) would
@@ -5,13 +5,13 @@
5
5
  * remain the stores; these are doors that point at the right reader.
6
6
  *
7
7
  * inbox → feed (needs-you default)
8
- * timeline → feed --filter updates (agent progress stream)
9
8
  *
10
9
  * `roster` was removed — use `agents sessions --active`.
10
+ * `timeline` was removed — use `agents feed --filter updates` (RUSH-2692).
11
11
  * `audit` is NOT an alias here — `agents audit` is already the tamper-evident
12
12
  * run-dispatch log. Ops trail = `agents events` (optionally `--audit`).
13
13
  */
14
- export type ObserveAlias = 'inbox' | 'timeline';
14
+ export type ObserveAlias = 'inbox';
15
15
  export declare const OBSERVE_ALIASES: readonly ObserveAlias[];
16
16
  export interface ObserveExpandResult {
17
17
  /** argv for the real command (no program name): e.g. ['feed', '--filter', 'updates'] */
@@ -5,13 +5,13 @@
5
5
  * remain the stores; these are doors that point at the right reader.
6
6
  *
7
7
  * inbox → feed (needs-you default)
8
- * timeline → feed --filter updates (agent progress stream)
9
8
  *
10
9
  * `roster` was removed — use `agents sessions --active`.
10
+ * `timeline` was removed — use `agents feed --filter updates` (RUSH-2692).
11
11
  * `audit` is NOT an alias here — `agents audit` is already the tamper-evident
12
12
  * run-dispatch log. Ops trail = `agents events` (optionally `--audit`).
13
13
  */
14
- export const OBSERVE_ALIASES = ['inbox', 'timeline'];
14
+ export const OBSERVE_ALIASES = ['inbox'];
15
15
  /** True when `rest` already carries a `--filter` / `--filter=…` flag. */
16
16
  export function hasFilterFlag(rest) {
17
17
  return rest.some((a) => a === '--filter' || a.startsWith('--filter='));
@@ -32,15 +32,6 @@ export function expandObserveAlias(alias, rest = []) {
32
32
  argv: ['feed', ...tail],
33
33
  note: 'agents inbox → agents feed (needs-you inbox)',
34
34
  };
35
- case 'timeline': {
36
- const argv = hasFilterFlag(tail)
37
- ? ['feed', ...tail]
38
- : ['feed', '--filter', 'updates', ...tail];
39
- return {
40
- argv,
41
- note: 'agents timeline → agents feed --filter updates',
42
- };
43
- }
44
35
  default:
45
36
  return null;
46
37
  }