@phnx-labs/agents-cli 1.22.116 → 1.22.118

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 (254) hide show
  1. package/CHANGELOG.md +247 -0
  2. package/dist/bootstrap.js +4 -11
  3. package/dist/commands/browser.d.ts +52 -70
  4. package/dist/commands/browser.js +192 -3562
  5. package/dist/commands/daemon.d.ts +39 -2
  6. package/dist/commands/daemon.js +72 -73
  7. package/dist/commands/events.js +1 -1
  8. package/dist/commands/exec.d.ts +18 -0
  9. package/dist/commands/exec.js +104 -12
  10. package/dist/commands/repo.js +13 -49
  11. package/dist/commands/route.js +5 -5
  12. package/dist/commands/routines.js +39 -12
  13. package/dist/commands/sessions-render.d.ts +1 -1
  14. package/dist/commands/sessions-render.js +0 -1
  15. package/dist/commands/sessions.test-fixture.d.ts +0 -1
  16. package/dist/commands/sessions.test-fixture.js +0 -37
  17. package/dist/commands/setup-browser.d.ts +17 -12
  18. package/dist/commands/setup-browser.js +52 -87
  19. package/dist/commands/setup-preferences.d.ts +0 -17
  20. package/dist/commands/setup-preferences.js +18 -49
  21. package/dist/commands/setup-term.d.ts +4 -3
  22. package/dist/commands/setup-term.js +5 -4
  23. package/dist/commands/setup.js +11 -11
  24. package/dist/commands/ssh.js +10 -1
  25. package/dist/commands/sync.js +6 -44
  26. package/dist/commands/teams.js +0 -1
  27. package/dist/commands/update.d.ts +19 -0
  28. package/dist/commands/update.js +34 -6
  29. package/dist/commands/view.d.ts +12 -1
  30. package/dist/commands/view.js +47 -2
  31. package/dist/index.js +107 -26
  32. package/dist/lib/account-capabilities.js +0 -2
  33. package/dist/lib/account-catalog.d.ts +38 -0
  34. package/dist/lib/account-catalog.js +110 -26
  35. package/dist/lib/account-provider-registry.js +1 -1
  36. package/dist/lib/account-registry.d.ts +1 -1
  37. package/dist/lib/account-registry.js +36 -2
  38. package/dist/lib/accounting/rotate.js +22 -8
  39. package/dist/lib/accounting/usage-ingest.d.ts +6 -1
  40. package/dist/lib/accounting/usage-ingest.js +115 -45
  41. package/dist/lib/accounting/usage-sync.d.ts +134 -31
  42. package/dist/lib/accounting/usage-sync.js +227 -66
  43. package/dist/lib/accounting/usage.d.ts +16 -3
  44. package/dist/lib/accounting/usage.js +26 -8
  45. package/dist/lib/accounts/slots.d.ts +8 -0
  46. package/dist/lib/accounts/slots.js +17 -0
  47. package/dist/lib/add-dir.js +0 -1
  48. package/dist/lib/agent-cli-commands.js +0 -1
  49. package/dist/lib/agent-spec/agents.d.ts +6 -7
  50. package/dist/lib/agent-spec/agents.js +6 -60
  51. package/dist/lib/auth-health.d.ts +69 -1
  52. package/dist/lib/auth-health.js +133 -4
  53. package/dist/lib/browser/context.d.ts +82 -0
  54. package/dist/lib/browser/context.js +88 -0
  55. package/dist/lib/browser/paths.d.ts +25 -0
  56. package/dist/lib/browser/paths.js +51 -0
  57. package/dist/lib/browser/record.d.ts +38 -0
  58. package/dist/lib/browser/record.js +57 -0
  59. package/dist/lib/browser/sessions-list.js +1 -2
  60. package/dist/lib/browser-client.d.ts +158 -0
  61. package/dist/lib/browser-client.js +234 -0
  62. package/dist/lib/channels/owner-sink.d.ts +1 -18
  63. package/dist/lib/channels/owner-sink.js +29 -82
  64. package/dist/lib/channels/providers/rush.d.ts +7 -7
  65. package/dist/lib/channels/providers/rush.js +127 -57
  66. package/dist/lib/claude-statusline.d.ts +1 -1
  67. package/dist/lib/claude-statusline.js +15 -3
  68. package/dist/lib/commands.js +1 -1
  69. package/dist/lib/crabbox/lease.js +1 -1
  70. package/dist/lib/crabbox/runtimes.js +1 -3
  71. package/dist/lib/daemon/account-state-daemon-service.js +1 -0
  72. package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
  73. package/dist/lib/daemon/auth-sync-service.js +37 -43
  74. package/dist/lib/daemon/daemon.d.ts +25 -14
  75. package/dist/lib/daemon/daemon.js +92 -154
  76. package/dist/lib/daemon/runner.d.ts +2 -2
  77. package/dist/lib/daemon/runner.js +32 -9
  78. package/dist/lib/daemon/self-update-service.d.ts +35 -34
  79. package/dist/lib/daemon/self-update-service.js +40 -36
  80. package/dist/lib/daemon/service.d.ts +12 -8
  81. package/dist/lib/daemon/supervisor.d.ts +65 -54
  82. package/dist/lib/daemon/supervisor.js +136 -171
  83. package/dist/lib/daemon/testdata/process-view-start.js +5 -2
  84. package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
  85. package/dist/lib/daemon/usage-sync-service.js +46 -78
  86. package/dist/lib/daemon-health.d.ts +22 -5
  87. package/dist/lib/daemon-health.js +47 -3
  88. package/dist/lib/daemon-services.d.ts +1 -1
  89. package/dist/lib/daemon-services.js +0 -10
  90. package/dist/lib/daemon-ticks.js +4 -2
  91. package/dist/lib/devices/doctor-findings.js +8 -11
  92. package/dist/lib/devices/harness-inventory.js +14 -5
  93. package/dist/lib/exec-account-home.js +5 -0
  94. package/dist/lib/exec.js +0 -11
  95. package/dist/lib/feed/events.js +0 -1
  96. package/dist/lib/feed/tool-activity.js +5 -2
  97. package/dist/lib/fleet-shared-state.d.ts +45 -0
  98. package/dist/lib/fleet-shared-state.js +78 -9
  99. package/dist/lib/fs-atomic.js +25 -0
  100. package/dist/lib/harness-auth-capabilities.js +0 -1
  101. package/dist/lib/helper-versions.js +1 -1
  102. package/dist/lib/hooks/cache.d.ts +7 -0
  103. package/dist/lib/hooks/cache.js +27 -2
  104. package/dist/lib/hooks/install.js +7 -1
  105. package/dist/lib/hosts/ready.d.ts +38 -8
  106. package/dist/lib/hosts/ready.js +62 -14
  107. package/dist/lib/hosts/remote-cmd.js +1 -0
  108. package/dist/lib/installations/active-check.d.ts +27 -0
  109. package/dist/lib/installations/active-check.js +61 -0
  110. package/dist/lib/installations/migrate.js +6 -3
  111. package/dist/lib/installations/shims.js +9 -7
  112. package/dist/lib/installations/update.js +21 -15
  113. package/dist/lib/installations/versions.js +12 -1
  114. package/dist/lib/manifest.js +1 -1
  115. package/dist/lib/models.js +0 -2
  116. package/dist/lib/open-url.js +28 -54
  117. package/dist/lib/placement.d.ts +2 -0
  118. package/dist/lib/placement.js +5 -0
  119. package/dist/lib/profiles.js +0 -2
  120. package/dist/lib/refresh.js +1 -1
  121. package/dist/lib/routine-readiness.d.ts +35 -0
  122. package/dist/lib/routine-readiness.js +74 -16
  123. package/dist/lib/rules/compile.js +6 -0
  124. package/dist/lib/sandbox.js +0 -2
  125. package/dist/lib/secrets-policy.d.ts +89 -11
  126. package/dist/lib/secrets-policy.js +175 -28
  127. package/dist/lib/session/db.d.ts +1 -1
  128. package/dist/lib/session/db.js +65 -18
  129. package/dist/lib/session/discover.js +1 -283
  130. package/dist/lib/session/mirror.d.ts +4 -3
  131. package/dist/lib/session/mirror.js +4 -3
  132. package/dist/lib/session/parse.d.ts +0 -2
  133. package/dist/lib/session/parse.js +1 -138
  134. package/dist/lib/session/throughput.d.ts +1 -3
  135. package/dist/lib/session/throughput.js +0 -20
  136. package/dist/lib/session/types.d.ts +2 -2
  137. package/dist/lib/session/types.js +2 -2
  138. package/dist/lib/sessions-client.d.ts +60 -1
  139. package/dist/lib/sessions-client.js +227 -4
  140. package/dist/lib/setup-tool-status.js +8 -12
  141. package/dist/lib/signin-badge.d.ts +3 -2
  142. package/dist/lib/signin-badge.js +6 -3
  143. package/dist/lib/smart-launch.js +4 -1
  144. package/dist/lib/staleness/detectors/commands.d.ts +1 -1
  145. package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
  146. package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
  147. package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
  148. package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
  149. package/dist/lib/staleness/detectors/rules.d.ts +1 -1
  150. package/dist/lib/staleness/detectors/skills.d.ts +1 -1
  151. package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
  152. package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
  153. package/dist/lib/staleness/writers/commands.d.ts +1 -1
  154. package/dist/lib/staleness/writers/hooks.d.ts +1 -1
  155. package/dist/lib/staleness/writers/mcp.d.ts +1 -1
  156. package/dist/lib/staleness/writers/permissions.d.ts +1 -1
  157. package/dist/lib/staleness/writers/plugins.d.ts +1 -1
  158. package/dist/lib/staleness/writers/rules.d.ts +1 -1
  159. package/dist/lib/staleness/writers/skills.d.ts +1 -1
  160. package/dist/lib/staleness/writers/sources.js +5 -0
  161. package/dist/lib/staleness/writers/subagents.d.ts +1 -1
  162. package/dist/lib/staleness/writers/workflows.d.ts +1 -1
  163. package/dist/lib/state.d.ts +6 -4
  164. package/dist/lib/state.js +7 -17
  165. package/dist/lib/teams/parsers.d.ts +1 -1
  166. package/dist/lib/teams/parsers.js +1 -136
  167. package/dist/lib/teams/placement-probe.js +50 -15
  168. package/dist/lib/teams/scheduler.d.ts +7 -0
  169. package/dist/lib/types.d.ts +10 -1
  170. package/dist/lib/types.js +1 -1
  171. package/dist/lib/view-types.d.ts +32 -0
  172. package/dist/lib/watchdog/read.js +0 -1
  173. package/dist/lib/watchdog/rotate.js +1 -2
  174. package/dist/lib/watchdog/runner.js +2 -2
  175. package/dist/lib/watchdog/watchdog.d.ts +1 -1
  176. package/dist/lib/watchdog/watchdogTail.js +0 -31
  177. package/package.json +1 -1
  178. package/scripts/postinstall.js +8 -8
  179. package/dist/browser.d.ts +0 -2
  180. package/dist/browser.js +0 -17
  181. package/dist/commands/browser-picker.d.ts +0 -18
  182. package/dist/commands/browser-picker.js +0 -97
  183. package/dist/lib/browser/arc-discovery.d.ts +0 -52
  184. package/dist/lib/browser/arc-discovery.js +0 -197
  185. package/dist/lib/browser/arc-dom.d.ts +0 -14
  186. package/dist/lib/browser/arc-dom.js +0 -121
  187. package/dist/lib/browser/caller-identity.d.ts +0 -40
  188. package/dist/lib/browser/caller-identity.js +0 -175
  189. package/dist/lib/browser/cdp.d.ts +0 -51
  190. package/dist/lib/browser/cdp.js +0 -254
  191. package/dist/lib/browser/chrome.d.ts +0 -141
  192. package/dist/lib/browser/chrome.js +0 -704
  193. package/dist/lib/browser/chromium-discovery.d.ts +0 -28
  194. package/dist/lib/browser/chromium-discovery.js +0 -96
  195. package/dist/lib/browser/devices.d.ts +0 -34
  196. package/dist/lib/browser/devices.js +0 -61
  197. package/dist/lib/browser/domain-skills.d.ts +0 -71
  198. package/dist/lib/browser/domain-skills.js +0 -195
  199. package/dist/lib/browser/drivers/arc.d.ts +0 -40
  200. package/dist/lib/browser/drivers/arc.js +0 -276
  201. package/dist/lib/browser/drivers/firefox.d.ts +0 -99
  202. package/dist/lib/browser/drivers/firefox.js +0 -377
  203. package/dist/lib/browser/drivers/local.d.ts +0 -55
  204. package/dist/lib/browser/drivers/local.js +0 -266
  205. package/dist/lib/browser/drivers/ssh.d.ts +0 -120
  206. package/dist/lib/browser/drivers/ssh.js +0 -467
  207. package/dist/lib/browser/editor.d.ts +0 -3
  208. package/dist/lib/browser/editor.js +0 -50
  209. package/dist/lib/browser/ffmpeg.d.ts +0 -12
  210. package/dist/lib/browser/ffmpeg.js +0 -184
  211. package/dist/lib/browser/firefox-discovery.d.ts +0 -69
  212. package/dist/lib/browser/firefox-discovery.js +0 -162
  213. package/dist/lib/browser/har.d.ts +0 -85
  214. package/dist/lib/browser/har.js +0 -77
  215. package/dist/lib/browser/hygiene.d.ts +0 -97
  216. package/dist/lib/browser/hygiene.js +0 -153
  217. package/dist/lib/browser/index.d.ts +0 -5
  218. package/dist/lib/browser/index.js +0 -5
  219. package/dist/lib/browser/input.d.ts +0 -7
  220. package/dist/lib/browser/input.js +0 -92
  221. package/dist/lib/browser/ipc.d.ts +0 -204
  222. package/dist/lib/browser/ipc.js +0 -1453
  223. package/dist/lib/browser/login-detection.d.ts +0 -87
  224. package/dist/lib/browser/login-detection.js +0 -274
  225. package/dist/lib/browser/profiles.d.ts +0 -422
  226. package/dist/lib/browser/profiles.js +0 -1157
  227. package/dist/lib/browser/refs.d.ts +0 -89
  228. package/dist/lib/browser/refs.js +0 -191
  229. package/dist/lib/browser/registry.d.ts +0 -70
  230. package/dist/lib/browser/registry.js +0 -252
  231. package/dist/lib/browser/remote-control.d.ts +0 -59
  232. package/dist/lib/browser/remote-control.js +0 -85
  233. package/dist/lib/browser/resolve-target.d.ts +0 -66
  234. package/dist/lib/browser/resolve-target.js +0 -264
  235. package/dist/lib/browser/runtime-state.d.ts +0 -291
  236. package/dist/lib/browser/runtime-state.js +0 -584
  237. package/dist/lib/browser/secret-ref.d.ts +0 -10
  238. package/dist/lib/browser/secret-ref.js +0 -14
  239. package/dist/lib/browser/service.d.ts +0 -812
  240. package/dist/lib/browser/service.js +0 -4693
  241. package/dist/lib/browser/stream.d.ts +0 -17
  242. package/dist/lib/browser/stream.js +0 -72
  243. package/dist/lib/browser/task-index.d.ts +0 -78
  244. package/dist/lib/browser/task-index.js +0 -199
  245. package/dist/lib/browser/types.d.ts +0 -647
  246. package/dist/lib/browser/types.js +0 -61
  247. package/dist/lib/browser/upload.d.ts +0 -29
  248. package/dist/lib/browser/upload.js +0 -298
  249. package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
  250. package/dist/lib/daemon/browser-ipc-service.js +0 -46
  251. package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
  252. package/dist/lib/daemon/browser-task-reap-service.js +0 -26
  253. package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
  254. package/dist/lib/fleet-shared-repo-sync.js +0 -490
@@ -268,6 +268,7 @@ export const AGENTS = {
268
268
  format: 'markdown',
269
269
  variableSyntax: '$ARGUMENTS',
270
270
  supportsHooks: true,
271
+ nativePluginSkills: true,
271
272
  // Claude Code grew a native `claude --cloud "<prompt>"` (Anthropic-managed
272
273
  // infra, claude.ai/code; requires claude.ai subscription auth). Routing
273
274
  // still goes to Rush Cloud deliberately — it keeps cloud tasks in one
@@ -305,46 +306,6 @@ export const AGENTS = {
305
306
  // ~/.codex/agents/*.toml (name, description, developer_instructions).
306
307
  capabilities: { hooks: { since: '0.116.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: { since: '0.138.0' }, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: { since: '0.117.0' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: true, modes: ['plan', 'edit', 'auto', 'skip'], interactiveRepl: true },
307
308
  },
308
- gemini: {
309
- id: 'gemini',
310
- name: 'Gemini',
311
- sessionDir: ['.gemini', 'tmp'],
312
- sessionFileExt: '.json',
313
- versionStdoutMatch: 'semver',
314
- unmanagedBinary: 'path',
315
- mcpRegister: 'cli',
316
- mcpAddHttp: 'transport',
317
- mcpAddStdio: 'simple',
318
- mcpConfigWrite: 'json-mcpServers',
319
- color: 'blue',
320
- cliCommand: 'gemini',
321
- npmPackage: '@google/gemini-cli',
322
- configDir: path.join(HOME, '.gemini'),
323
- commandsDir: path.join(HOME, '.gemini', 'commands'),
324
- commandsSubdir: 'commands',
325
- skillsDir: path.join(HOME, '.gemini', 'skills'),
326
- hooksDir: 'hooks',
327
- instructionsFile: 'GEMINI.md',
328
- format: 'toml',
329
- variableSyntax: '{{args}}',
330
- supportsHooks: true,
331
- nativeAgentsSkillsDir: true,
332
- // Google retired the Gemini CLI (announced at Google I/O 2026, May 19); the `gemini`
333
- // command stopped serving free/Pro/Ultra requests on June 18, 2026. Antigravity CLI
334
- // (`agy`) is the official successor. Hard deprecation keeps legacy Gemini
335
- // sessions/config parseable while blocking install/import/sync.
336
- deprecated: {
337
- by: 'Google',
338
- date: 'June 18, 2026',
339
- reason: 'The Gemini CLI was retired for free, Pro, and Ultra tiers and no longer serves requests (announced at Google I/O 2026 on May 19).',
340
- replacement: 'antigravity',
341
- url: 'https://developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/',
342
- hard: true,
343
- },
344
- // gemini hooks: shipped in v0.26.0 (Jan 2026); older binaries silently ignore the `hooks` key.
345
- // extensions: gemini-extension.json bundles shipped in v0.8.0; custom subagents in v0.36.0.
346
- capabilities: { hooks: { since: '0.26.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: { since: '0.8.0' }, subagents: { since: '0.36.0' }, rules: { file: 'GEMINI.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'skip'], rulesImports: true, interactiveRepl: false },
347
- },
348
309
  cursor: {
349
310
  id: 'cursor',
350
311
  name: 'Cursor',
@@ -1019,16 +980,13 @@ export const ALL_AGENT_IDS = Object.keys(AGENTS);
1019
980
  * import (runner.ts already imports from routines.ts).
1020
981
  *
1021
982
  * This is a curated subset of AGENT_COMMANDS, not "every harness that can run
1022
- * headlessly". Expanding it is a product change (gemini is hard-deprecated and
1023
- * is deliberately absent). Argv itself is baked from AGENT_COMMANDS in
1024
- * daemon/runner.ts bakeRoutineArgv — do not reintroduce a second token table.
983
+ * headlessly". Expanding it is a product change. Argv itself is baked from
984
+ * AGENT_COMMANDS in daemon/runner.ts bakeRoutineArgv — do not reintroduce a
985
+ * second token table.
1025
986
  */
1026
987
  export const ROUTINE_AGENT_IDS = Object.freeze([
1027
988
  'claude',
1028
989
  'codex',
1029
- // gemini is hard-deprecated (Antigravity replaced it) — no routine target. A
1030
- // legacy gemini routine now fails validateJob loud instead of firing a
1031
- // retired backend; the id survives only for reading old sessions/config.
1032
990
  'cursor',
1033
991
  'kimi',
1034
992
  'droid',
@@ -1338,7 +1296,6 @@ export function accountOrgBadge(info) {
1338
1296
  export const ACCOUNT_INSPECTION_AGENT_IDS = [
1339
1297
  'claude',
1340
1298
  'codex',
1341
- 'gemini',
1342
1299
  'cursor',
1343
1300
  'grok',
1344
1301
  'antigravity',
@@ -1429,7 +1386,6 @@ function resolveAccountCredentialPath(base, ...segments) {
1429
1386
  const CREDENTIAL_FILE_SEGMENTS = {
1430
1387
  claude: [['.claude', '.claude.json'], ['.claude.json']],
1431
1388
  codex: [['.codex', 'auth.json']],
1432
- gemini: [['.gemini', 'google_accounts.json']],
1433
1389
  grok: [['.grok', 'auth.json']],
1434
1390
  kimi: [['.kimi-code', 'credentials', 'kimi-code.json']],
1435
1391
  droid: [['.factory', 'auth.v2.file']],
@@ -2028,7 +1984,6 @@ export async function getAccountInfo(agentId, home) {
2028
1984
  const configFiles = {
2029
1985
  claude: path.join(base, '.claude.json'),
2030
1986
  codex: path.join(base, '.codex', 'auth.json'),
2031
- gemini: path.join(base, '.gemini', 'google_accounts.json'),
2032
1987
  // OpenCode keeps every session in ONE sqlite file rather than a directory of
2033
1988
  // per-session transcripts, so the session-file walk resolveLastActive runs
2034
1989
  // for other agents finds nothing. Its mtime fallback is the right read here:
@@ -2151,11 +2106,6 @@ export async function getAccountInfo(agentId, home) {
2151
2106
  signedIn: !!email,
2152
2107
  };
2153
2108
  }
2154
- case 'gemini': {
2155
- const data = JSON.parse(await fs.promises.readFile(path.join(base, '.gemini', 'google_accounts.json'), 'utf-8'));
2156
- const email = data.active || null;
2157
- return { ...empty, email, signedIn: !!email, lastActive };
2158
- }
2159
2109
  case 'cursor': {
2160
2110
  // Cursor CLI keeps account metadata in ~/.cursor/cli-config.json
2161
2111
  // (authInfo: { email, userId, authId }) and its OAuth tokens SEPARATELY
@@ -2902,9 +2852,8 @@ function parseMcpFromOpenCodeConfig(configPath) {
2902
2852
  *
2903
2853
  * All three MCP path resolvers read `MCP_TARGETS`, so the file the writer
2904
2854
  * targets, the file the parser reads, and the file the staleness detector
2905
- * watches cannot drift apart. An agent with no MCP target (the hard-deprecated
2906
- * `gemini`) keeps the historical settings.json default so old configs still
2907
- * parse.
2855
+ * watches cannot drift apart. An agent with no MCP target keeps the historical
2856
+ * settings.json default so old configs still parse.
2908
2857
  */
2909
2858
  export function getUserMcpConfigPath(agentId) {
2910
2859
  return getMcpConfigPathForHome(agentId, HOME);
@@ -3036,9 +2985,6 @@ const AGENT_NAME_ALIASES = {
3036
2985
  codex: 'codex',
3037
2986
  'openai-codex': 'codex',
3038
2987
  cx: 'codex',
3039
- gemini: 'gemini',
3040
- 'gemini-cli': 'gemini',
3041
- gx: 'gemini',
3042
2988
  cursor: 'cursor',
3043
2989
  'cursor-agent': 'cursor',
3044
2990
  cr: 'cursor',
@@ -19,10 +19,21 @@ import { type ProviderProbe } from './accounting/usage.js';
19
19
  * endpoint cannot prove live for a known non-revocation
20
20
  * reason (Claude setup-token usage-scope gap — RUSH-2392),
21
21
  * OR a throttled probe with no fresh previous verdict (PHNX-4051).
22
+ * - `no_evidence` — a credential is present on disk but THIS box has no evidence,
23
+ * either way, that it can authenticate: no probe was possible
24
+ * (a worker's setup-token box does not probe the usage endpoint)
25
+ * and no run outcome has been recorded here yet. Distinct from
26
+ * `unverified` (which is "signed in, no probe endpoint exists for
27
+ * this harness") — `no_evidence` is "we did not look", the exact
28
+ * state PHNX-4116 stops rendering as a verdict word. A worker
29
+ * never PUBLISHES this row (it is dropped in
30
+ * {@link probeLocalFleetAuth}); it surfaces only as an in-memory
31
+ * account-catalog verdict whose display is a FACT (token presence
32
+ * + recorded run outcomes), never the word "no_evidence".
22
33
  * - `unconfigured`— no usable credential on disk.
23
34
  * - `error` — network/other failure; verdict indeterminate (keep the last known one).
24
35
  */
25
- export type AuthVerdict = 'live' | 'revoked' | 'expired' | 'rate_limited' | 'unverified' | 'unconfigured' | 'error';
36
+ export type AuthVerdict = 'live' | 'revoked' | 'expired' | 'rate_limited' | 'unverified' | 'no_evidence' | 'unconfigured' | 'error';
26
37
  export interface AuthHealth {
27
38
  verdict: AuthVerdict;
28
39
  /** epoch ms of the probe. */
@@ -33,6 +44,15 @@ export interface AuthHealth {
33
44
  account?: string;
34
45
  /** Stable registered account id. Display labels are never used as identity. */
35
46
  accountId?: string;
47
+ /**
48
+ * How this row was observed (PHNX-4116). `probe` (default when absent) is a
49
+ * network/usage-endpoint probe the daemon ran; `run` is a REAL agent run
50
+ * outcome recorded on exit — a successful inference (`live`) or an auth failure
51
+ * (`revoked`/`expired`/`rate_limited`). The auth FACT `agents view` renders
52
+ * (`last used ok 12m ago` vs `last auth failure 401 …`) reads this so a token's
53
+ * usability is stated as evidence, not inferred from a stale usage file.
54
+ */
55
+ source?: 'probe' | 'run';
36
56
  }
37
57
  /** Maximum age of an auth verdict used for automatic routing decisions. */
38
58
  export declare const AUTH_PROBE_MAX_AGE_MS: number;
@@ -284,4 +304,52 @@ export declare function probeLocalFleetAuth(opts?: {
284
304
  * merges as before.
285
305
  */
286
306
  export declare function writeFleetAuthRows(host: string, rows: AuthProbeRow[], installed?: ReadonlySet<string>): void;
307
+ /**
308
+ * The installed-version label a run outcome falls back to when the account has no
309
+ * slot on this box: the explicit `version`, else the label derived from `home` (a
310
+ * version home is `<versionDir>/home`, so the label is its parent dir's basename).
311
+ * Null when neither resolves. Pure (no fs) so it is unit-tested directly.
312
+ */
313
+ export declare function runOutcomeVersionKey(opts: {
314
+ version?: string | null;
315
+ home?: string | null;
316
+ }): string | null;
317
+ /** A real agent-run outcome — the evidence behind `last used ok` / `last auth failure`. */
318
+ export type RunAuthOutcome = {
319
+ ok: true;
320
+ } | {
321
+ ok: false;
322
+ verdict: 'revoked' | 'expired' | 'rate_limited' | 'error';
323
+ detail?: string;
324
+ resetsAt?: number | null;
325
+ };
326
+ /**
327
+ * Record a REAL run's auth outcome into the auth-health cache with
328
+ * `source: 'run'`. Best-effort and never throws: an unattributable run (no
329
+ * accountId/version/home) writes nothing, and the underlying write already
330
+ * swallows IO errors. Keyed exactly as the catalog reads, so the recorded fact
331
+ * surfaces on the same account row.
332
+ */
333
+ export declare function recordRunAuthOutcome(opts: {
334
+ agent: AgentId | string;
335
+ accountId?: string | null;
336
+ version?: string | null;
337
+ home?: string | null;
338
+ account?: string;
339
+ outcome: RunAuthOutcome;
340
+ host?: string;
341
+ now?: number;
342
+ }): void;
343
+ /** `Sep 20 14:02` — the fact timestamp for an auth failure. */
344
+ export declare function factTimestamp(ms: number): string;
345
+ /**
346
+ * The auth FACT `agents view` / `agents accounts` render per account per box
347
+ * (PHNX-4116): what actually happened here, with its time — never a word that
348
+ * means "we did not look". A `live` row (a recorded run OR a real probe) reads
349
+ * `last used ok <age>`; a server rejection reads `last auth failure <detail>
350
+ * <time>`; a throttle reads `rate-limited <detail> (<time>)`; anything with no
351
+ * usable evidence — including `no_evidence`/`unverified` — reads `not used on
352
+ * this box yet`.
353
+ */
354
+ export declare function formatAuthFact(health: AuthHealth | null | undefined, now?: number): string;
287
355
  export {};
@@ -27,6 +27,7 @@ import { getVersionHomePath, listInstalledVersions } from './installations/versi
27
27
  import { atomicWriteFileSync, ensureLockTarget, withFileLock } from './fs-atomic.js';
28
28
  import { selfConfiguredDeviceRole } from './device-config.js';
29
29
  import { mayIssueUsageEndpointProbe, trySpendUsageApiCall } from './usage-refresh.js';
30
+ import { machineId } from './machine-id.js';
30
31
  /** Maximum age of an auth verdict used for automatic routing decisions. */
31
32
  export const AUTH_PROBE_MAX_AGE_MS = 20 * 60_000;
32
33
  /** Agents with a live network probe wired up today. The rest are best-effort. */
@@ -82,6 +83,7 @@ const VERDICT_GLYPHS = {
82
83
  expired: '○', // ○
83
84
  rate_limited: '◐', // ◐
84
85
  unverified: '◐', // ◐
86
+ no_evidence: '◌', // ◌ — credential present, no evidence either way
85
87
  unconfigured: '·', // ·
86
88
  error: '·', // ·
87
89
  };
@@ -97,6 +99,7 @@ export function verdictLabel(verdict) {
97
99
  case 'expired': return 'expired';
98
100
  case 'rate_limited': return 'limited';
99
101
  case 'unverified': return 'unverified';
102
+ case 'no_evidence': return 'no evidence';
100
103
  case 'unconfigured': return '—';
101
104
  case 'error': return '?';
102
105
  }
@@ -109,7 +112,7 @@ export function summarizeVerdicts(verdicts) {
109
112
  for (const v of verdicts) {
110
113
  if (v === 'live')
111
114
  live++;
112
- else if (v === 'unverified')
115
+ else if (v === 'unverified' || v === 'no_evidence')
113
116
  present++;
114
117
  else if (v === 'revoked')
115
118
  bad++;
@@ -132,6 +135,7 @@ export function verdictColor(verdict) {
132
135
  case 'live': return 'green';
133
136
  case 'revoked': return 'red';
134
137
  case 'unverified': return 'gray';
138
+ case 'no_evidence': return 'gray';
135
139
  case 'unconfigured': return 'dim';
136
140
  default: return 'yellow'; // expired / rate_limited / error — soft, self-healing/indeterminate
137
141
  }
@@ -188,6 +192,9 @@ export function summarizeHostAuth(cache, host) {
188
192
  case 'unverified':
189
193
  present++;
190
194
  break; // signed in, no probe — benign
195
+ case 'no_evidence':
196
+ present++;
197
+ break; // credential present, no evidence — benign
191
198
  case 'revoked':
192
199
  revoked++;
193
200
  break; // server said no — re-login
@@ -437,6 +444,14 @@ function verdictFromFreshUsage(usageKey, signedIn, now) {
437
444
  const capturedAt = snapshot?.capturedAt?.getTime();
438
445
  if (!capturedAt || now - capturedAt >= FRESH_USAGE_VERDICT_MAX_AGE_MS)
439
446
  return null;
447
+ // A `sync`-sourced snapshot arrived from ANOTHER box's poller over the fleet
448
+ // store — it proves the shared setup-token works SOMEWHERE, not that THIS box
449
+ // can authenticate (PHNX-4116). Deriving `live` from it is exactly the false
450
+ // "we did not look here, but a synced file is fresh, so call it live" the
451
+ // ticket removes. Only a reading THIS box captured itself (statusline/poll)
452
+ // is admissible live evidence.
453
+ if (snapshot?.freshness?.source === 'sync')
454
+ return null;
440
455
  const ageMin = Math.max(1, Math.round((now - capturedAt) / 60_000));
441
456
  return { verdict: 'live', checkedAt: now, detail: `token proven live by a usage fetch ${ageMin}m ago` };
442
457
  }
@@ -461,15 +476,21 @@ export async function probeAuthHealth(agent, home, opts) {
461
476
  role: selfConfiguredDeviceRole(),
462
477
  forceLive: opts?.forceLive,
463
478
  })) {
479
+ // A worker's setup-token cannot read the usage endpoint (RUSH-2392), so this
480
+ // box has no probe evidence for the account. This is NOT `unverified` (which
481
+ // conflated it with codex/grok's permanent no-probe-endpoint state): it is
482
+ // "we did not look here". A worker never publishes it — probeLocalFleetAuth
483
+ // drops `no_evidence` like `unconfigured` — so the account's facts come from
484
+ // token presence + recorded run outcomes instead (PHNX-4116).
464
485
  return {
465
- verdict: 'unverified',
486
+ verdict: 'no_evidence',
466
487
  checkedAt,
467
488
  detail: 'setup-token box does not probe the usage endpoint',
468
489
  };
469
490
  }
470
491
  if (opts?.forceLive !== true && usageScope && !trySpendUsageApiCall(usageScope, agent, checkedAt)) {
471
492
  return {
472
- verdict: 'unverified',
493
+ verdict: 'no_evidence',
473
494
  checkedAt,
474
495
  detail: 'usage-endpoint budget already spent this hour',
475
496
  };
@@ -573,7 +594,12 @@ export async function probeLocalFleetAuth(opts) {
573
594
  const health = await probeAuthHealth(rep.agent, rep.home, { cliVersion: opts?.cliVersion, info: rep.info, forceLive: opts?.forceLive, signal: opts?.signal });
574
595
  health.account = authAccountLabel(rep.info);
575
596
  health.accountId = rep.accountId;
576
- if (health.verdict === 'unconfigured') {
597
+ health.source = 'probe';
598
+ // `unconfigured` (no credential) and `no_evidence` (a worker that cannot probe)
599
+ // both write NO row: the first has nothing to say, the second says only "we
600
+ // did not look", which is not a fleet-publishable verdict — the account's
601
+ // facts come from token presence + recorded run outcomes instead (PHNX-4116).
602
+ if (health.verdict === 'unconfigured' || health.verdict === 'no_evidence') {
577
603
  perGroup.push([]);
578
604
  }
579
605
  else {
@@ -618,3 +644,106 @@ export function writeFleetAuthRows(host, rows, installed) {
618
644
  : undefined;
619
645
  writeAuthHealthEntries(entries, drop);
620
646
  }
647
+ // ---------------------------------------------------------------------------
648
+ // Run-outcome recording + auth-fact rendering (PHNX-4116)
649
+ //
650
+ // A worker never probes, so the honest evidence that its token authenticates is
651
+ // a REAL run: a successful inference (`live`) or an auth failure. These rows are
652
+ // written with `source: 'run'` and read back as a FACT — `last used ok 12m ago`
653
+ // / `last auth failure 401 Sep 20 14:02` / `not used on this box yet` — instead
654
+ // of the misleading verdict word the ticket removes.
655
+ // ---------------------------------------------------------------------------
656
+ /**
657
+ * The installed-version label a run outcome falls back to when the account has no
658
+ * slot on this box: the explicit `version`, else the label derived from `home` (a
659
+ * version home is `<versionDir>/home`, so the label is its parent dir's basename).
660
+ * Null when neither resolves. Pure (no fs) so it is unit-tested directly.
661
+ */
662
+ export function runOutcomeVersionKey(opts) {
663
+ if (opts.version)
664
+ return opts.version;
665
+ if (opts.home) {
666
+ const label = path.basename(path.dirname(opts.home));
667
+ if (label && label !== '.' && label !== path.sep)
668
+ return label;
669
+ }
670
+ return null;
671
+ }
672
+ /**
673
+ * Record a REAL run's auth outcome into the auth-health cache with
674
+ * `source: 'run'`. Best-effort and never throws: an unattributable run (no
675
+ * accountId/version/home) writes nothing, and the underlying write already
676
+ * swallows IO errors. Keyed exactly as the catalog reads, so the recorded fact
677
+ * surfaces on the same account row.
678
+ */
679
+ export function recordRunAuthOutcome(opts) {
680
+ // Key exactly as the account catalog READS (slot key first, then the version
681
+ // label) so the fact lands on the row: the slot key ONLY when a slot dir
682
+ // actually exists for this account on this box (workers), else the installed
683
+ // version label (headed native logins have no slot). A last-resort slot key
684
+ // keeps an attributable-by-id run recorded even with no version hint.
685
+ const slotKey = opts.accountId && readSlots(readMeta())[opts.accountId]
686
+ ? slotAuthVersionKey(opts.accountId)
687
+ : null;
688
+ const versionKey = slotKey
689
+ ?? runOutcomeVersionKey(opts)
690
+ ?? (opts.accountId ? slotAuthVersionKey(opts.accountId) : null);
691
+ if (!versionKey)
692
+ return;
693
+ const checkedAt = opts.now ?? Date.now();
694
+ const host = opts.host ?? machineId();
695
+ let detail;
696
+ if (opts.outcome.ok) {
697
+ detail = 'run ok';
698
+ }
699
+ else if (opts.outcome.verdict === 'rate_limited' && opts.outcome.resetsAt) {
700
+ detail = `until ${factClock(opts.outcome.resetsAt)}`;
701
+ }
702
+ else {
703
+ detail = opts.outcome.detail;
704
+ }
705
+ const health = {
706
+ verdict: opts.outcome.ok ? 'live' : opts.outcome.verdict,
707
+ checkedAt,
708
+ source: 'run',
709
+ ...(detail ? { detail } : {}),
710
+ ...(opts.account ? { account: opts.account } : {}),
711
+ ...(opts.accountId ? { accountId: opts.accountId } : {}),
712
+ };
713
+ writeAuthHealthEntries({ [authCacheKey(host, opts.agent, versionKey)]: health });
714
+ }
715
+ const FACT_MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
716
+ /** `HH:MM` local clock — used for a throttle reset time. */
717
+ function factClock(ms) {
718
+ const d = new Date(ms);
719
+ return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
720
+ }
721
+ /** `Sep 20 14:02` — the fact timestamp for an auth failure. */
722
+ export function factTimestamp(ms) {
723
+ const d = new Date(ms);
724
+ return `${FACT_MONTHS[d.getMonth()]} ${d.getDate()} ${factClock(ms)}`;
725
+ }
726
+ /**
727
+ * The auth FACT `agents view` / `agents accounts` render per account per box
728
+ * (PHNX-4116): what actually happened here, with its time — never a word that
729
+ * means "we did not look". A `live` row (a recorded run OR a real probe) reads
730
+ * `last used ok <age>`; a server rejection reads `last auth failure <detail>
731
+ * <time>`; a throttle reads `rate-limited <detail> (<time>)`; anything with no
732
+ * usable evidence — including `no_evidence`/`unverified` — reads `not used on
733
+ * this box yet`.
734
+ */
735
+ export function formatAuthFact(health, now = Date.now()) {
736
+ if (!health)
737
+ return 'not used on this box yet';
738
+ switch (health.verdict) {
739
+ case 'live':
740
+ return `last used ok ${formatCheckedAge(health.checkedAt, now)}`;
741
+ case 'revoked':
742
+ case 'expired':
743
+ return `last auth failure${health.detail ? ` ${health.detail}` : ''} ${factTimestamp(health.checkedAt)}`;
744
+ case 'rate_limited':
745
+ return `rate-limited${health.detail ? ` ${health.detail}` : ''} (${factTimestamp(health.checkedAt)})`;
746
+ default:
747
+ return 'not used on this box yet';
748
+ }
749
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * context.ts — everything agents-cli knows that the standalone `browser` engine
3
+ * cannot work out for itself, serialized as one JSON object onto fd 3.
4
+ *
5
+ * This is the whole contract of the consumer half (browser-cli integration
6
+ * contract §1). The engine accepts exactly this shape:
7
+ *
8
+ * version `1`. The engine matches on it; a field added later must be
9
+ * optional so an older engine keeps working.
10
+ * target the `--device <name>` target, resolved against the fleet:
11
+ * device registry, ssh identity, platform. The engine matches
12
+ * its own `--device <alias>` against this instead of reading a
13
+ * registry it does not have, then falls back to `~/.ssh/config`.
14
+ * Absent for a local invocation.
15
+ * session who is acting — actor id and agent session — so an action
16
+ * lands in the right session history.
17
+ * remoteControl whether THIS machine consents to being driven by a peer.
18
+ * agents-cli owns the policy; browser-cli enforces the flag.
19
+ *
20
+ * WHAT IS DELIBERATELY NOT HERE. The CDP/BiDi/Arc endpoint, the IPC socket, the
21
+ * chrome-data store and the profile declarations are the ENGINE's: browser-cli
22
+ * keeps every on-disk path the in-repo subsystem used (integration contract §4)
23
+ * and hydrates its own transport from them. agents-cli publishing an endpoint
24
+ * here would be a second, drifting copy of that answer.
25
+ *
26
+ * The context is PUSHED (written and closed) rather than exposed as a callback,
27
+ * so the engine never re-enters agents-cli and there is exactly one direction of
28
+ * dependency.
29
+ */
30
+ /** A `--device <name>` target, resolved against the fleet. */
31
+ export interface BrowserTargetContext {
32
+ /** The device name as the user typed it. */
33
+ alias: string;
34
+ /** `user@host`, already validated against ssh option injection. */
35
+ host: string;
36
+ user: string;
37
+ /** Bare host — the ssh-config Host name or address, without the user. */
38
+ hostname: string;
39
+ platform: string;
40
+ /** Per-device ssh identity flags, in argv order. Possibly empty. */
41
+ sshArgs: string[];
42
+ }
43
+ /** Who is acting, so the engine can stamp the action it reports back. */
44
+ interface BrowserSessionContext {
45
+ sessionId?: string;
46
+ launchId?: string;
47
+ actor: string;
48
+ }
49
+ interface BrowserContext {
50
+ version: 1;
51
+ target?: BrowserTargetContext;
52
+ session: BrowserSessionContext;
53
+ remoteControl: {
54
+ allowed: boolean;
55
+ };
56
+ }
57
+ /**
58
+ * Whether this machine allows other fleet machines to drive its browser. Reads
59
+ * the device-scope `browser.remote-control` config key. Unset = off (deny).
60
+ * browser-cli reads and writes the same key, so a `remote-control on` typed at
61
+ * either surface agrees.
62
+ */
63
+ export declare function remoteControlEnabled(): boolean;
64
+ interface BuildContextOptions {
65
+ /** `--device <name>`, if given (only meaningful on `start`). */
66
+ device?: string;
67
+ /**
68
+ * A precomputed target, when the caller already resolved the device. When
69
+ * present, `device` need not be re-resolved.
70
+ */
71
+ target?: BrowserTargetContext;
72
+ }
73
+ /**
74
+ * Resolve `--device <name>` to a fleet target and build the context handed to
75
+ * the engine on fd 3.
76
+ *
77
+ * Unlike `agents computer`, browser drives ANY platform, so `resolveRemoteDevice`
78
+ * is called with no platform expectation — a `--device` pointing at a Mac, Linux
79
+ * box or Windows host all resolve.
80
+ */
81
+ export declare function buildBrowserContext(opts?: BuildContextOptions): Promise<BrowserContext>;
82
+ export {};
@@ -0,0 +1,88 @@
1
+ /**
2
+ * context.ts — everything agents-cli knows that the standalone `browser` engine
3
+ * cannot work out for itself, serialized as one JSON object onto fd 3.
4
+ *
5
+ * This is the whole contract of the consumer half (browser-cli integration
6
+ * contract §1). The engine accepts exactly this shape:
7
+ *
8
+ * version `1`. The engine matches on it; a field added later must be
9
+ * optional so an older engine keeps working.
10
+ * target the `--device <name>` target, resolved against the fleet:
11
+ * device registry, ssh identity, platform. The engine matches
12
+ * its own `--device <alias>` against this instead of reading a
13
+ * registry it does not have, then falls back to `~/.ssh/config`.
14
+ * Absent for a local invocation.
15
+ * session who is acting — actor id and agent session — so an action
16
+ * lands in the right session history.
17
+ * remoteControl whether THIS machine consents to being driven by a peer.
18
+ * agents-cli owns the policy; browser-cli enforces the flag.
19
+ *
20
+ * WHAT IS DELIBERATELY NOT HERE. The CDP/BiDi/Arc endpoint, the IPC socket, the
21
+ * chrome-data store and the profile declarations are the ENGINE's: browser-cli
22
+ * keeps every on-disk path the in-repo subsystem used (integration contract §4)
23
+ * and hydrates its own transport from them. agents-cli publishing an endpoint
24
+ * here would be a second, drifting copy of that answer.
25
+ *
26
+ * The context is PUSHED (written and closed) rather than exposed as a callback,
27
+ * so the engine never re-enters agents-cli and there is exactly one direction of
28
+ * dependency.
29
+ */
30
+ import { resolveActor } from '../actor.js';
31
+ import { resolveRemoteDevice } from '../ssh-tunnel.js';
32
+ import { getConfigValue } from '../device-config.js';
33
+ /**
34
+ * Which agent session is acting. The harness-native id first, then agents' own —
35
+ * the same precedence `lib/computer/context.ts` uses.
36
+ */
37
+ function agentSessionId(env = process.env) {
38
+ return env.CODEX_THREAD_ID
39
+ || env.CLAUDE_CODE_SESSION_ID
40
+ || env.CLAUDE_SESSION_ID
41
+ || env.AGENTS_SESSION_ID
42
+ || env.AGENT_SESSION_ID
43
+ || env.AGENTS_RUN_ID
44
+ || undefined;
45
+ }
46
+ /**
47
+ * Whether this machine allows other fleet machines to drive its browser. Reads
48
+ * the device-scope `browser.remote-control` config key. Unset = off (deny).
49
+ * browser-cli reads and writes the same key, so a `remote-control on` typed at
50
+ * either surface agrees.
51
+ */
52
+ export function remoteControlEnabled() {
53
+ return getConfigValue('browser.remote-control').value === true;
54
+ }
55
+ /**
56
+ * Resolve `--device <name>` to a fleet target and build the context handed to
57
+ * the engine on fd 3.
58
+ *
59
+ * Unlike `agents computer`, browser drives ANY platform, so `resolveRemoteDevice`
60
+ * is called with no platform expectation — a `--device` pointing at a Mac, Linux
61
+ * box or Windows host all resolve.
62
+ */
63
+ export async function buildBrowserContext(opts = {}) {
64
+ let target = opts.target;
65
+ if (!target && opts.device && opts.device !== 'local') {
66
+ const resolved = await resolveRemoteDevice(opts.device);
67
+ target = {
68
+ alias: opts.device,
69
+ host: resolved.target,
70
+ user: resolved.user,
71
+ hostname: resolved.host,
72
+ platform: resolved.device.platform,
73
+ sshArgs: resolved.identityArgs,
74
+ };
75
+ }
76
+ return {
77
+ version: 1,
78
+ // Spread rather than assigned: a local invocation must not ship a `target`
79
+ // key at all, so the engine never has to distinguish absent from null.
80
+ ...(target ? { target } : {}),
81
+ session: {
82
+ sessionId: agentSessionId(),
83
+ launchId: process.env.AGENT_LAUNCH_ID,
84
+ actor: resolveActor().id,
85
+ },
86
+ remoteControl: { allowed: remoteControlEnabled() },
87
+ };
88
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * paths.ts — the on-disk browser layout the CONSUMER still reads.
3
+ *
4
+ * browser-cli owns the browser process, drivers and IPC, but it keeps every
5
+ * on-disk path the in-repo subsystem used (integration contract §4). So
6
+ * `agents browser sessions` (`sessions-list.ts`) and the feed's browser tool
7
+ * rows (`feed/tool-activity.ts`) read a profile's captures straight off disk —
8
+ * no daemon, no engine — and need just these path helpers. They were the pure
9
+ * path corner of the deleted `profiles.ts` / `runtime-state.ts` engine files.
10
+ *
11
+ * A profile's live tasks and captures may live under a COMPOSITE runtime dir
12
+ * (`<profile>@<endpoint>`, `<profile>@<endpoint>.<fork>`), not the bare
13
+ * `<profile>` dir, so `listProfileCacheDirs` matches every dir that belongs to a
14
+ * profile — the same last-`@` rule browser-cli names them with.
15
+ */
16
+ export { getBrowserDurableDir } from '../state.js';
17
+ /** Root of the browser runtime cache — `~/.agents/.cache/browser/`. */
18
+ export declare function getBrowserRuntimeDir(): string;
19
+ /** The runtime cache dir for one profile's bare name. */
20
+ export declare function getProfileRuntimeDir(name: string): string;
21
+ /**
22
+ * Every runtime-cache dir that belongs to a profile, composite forms included.
23
+ * Empty when the root does not exist yet.
24
+ */
25
+ export declare function listProfileCacheDirs(profileName: string): string[];