@phnx-labs/agents-cli 1.20.34 → 1.20.36

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 (291) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/LICENSE +185 -21
  3. package/README.md +13 -5
  4. package/dist/commands/audit.d.ts +14 -0
  5. package/dist/commands/audit.js +68 -0
  6. package/dist/commands/browser.js +82 -8
  7. package/dist/commands/check.d.ts +15 -0
  8. package/dist/commands/check.js +84 -0
  9. package/dist/commands/cloud.js +143 -3
  10. package/dist/commands/computer-actions.d.ts +15 -0
  11. package/dist/commands/computer-actions.js +120 -22
  12. package/dist/commands/computer.js +81 -0
  13. package/dist/commands/cost.js +17 -12
  14. package/dist/commands/daemon.js +4 -1
  15. package/dist/commands/doctor.d.ts +1 -0
  16. package/dist/commands/doctor.js +54 -97
  17. package/dist/commands/events.d.ts +16 -0
  18. package/dist/commands/events.js +173 -0
  19. package/dist/commands/exec.d.ts +19 -0
  20. package/dist/commands/exec.js +283 -12
  21. package/dist/commands/hosts.js +37 -29
  22. package/dist/commands/inspect.d.ts +2 -0
  23. package/dist/commands/inspect.js +62 -16
  24. package/dist/commands/lock.d.ts +12 -0
  25. package/dist/commands/lock.js +70 -0
  26. package/dist/commands/logs.d.ts +17 -0
  27. package/dist/commands/logs.js +139 -0
  28. package/dist/commands/message.d.ts +15 -0
  29. package/dist/commands/message.js +56 -0
  30. package/dist/commands/models.d.ts +3 -0
  31. package/dist/commands/models.js +27 -8
  32. package/dist/commands/permissions.js +9 -2
  33. package/dist/commands/repo.d.ts +34 -0
  34. package/dist/commands/repo.js +243 -65
  35. package/dist/commands/resource-view.d.ts +20 -0
  36. package/dist/commands/resource-view.js +90 -28
  37. package/dist/commands/routines.js +101 -5
  38. package/dist/commands/secrets-migrate.js +106 -57
  39. package/dist/commands/secrets.d.ts +31 -18
  40. package/dist/commands/secrets.js +196 -91
  41. package/dist/commands/serve.d.ts +10 -0
  42. package/dist/commands/serve.js +37 -0
  43. package/dist/commands/sessions-inject.d.ts +14 -0
  44. package/dist/commands/sessions-inject.js +111 -0
  45. package/dist/commands/sessions-picker.d.ts +2 -0
  46. package/dist/commands/sessions-picker.js +24 -3
  47. package/dist/commands/sessions-resume.d.ts +2 -0
  48. package/dist/commands/sessions-resume.js +221 -0
  49. package/dist/commands/sessions-tail.d.ts +10 -0
  50. package/dist/commands/sessions-tail.js +11 -0
  51. package/dist/commands/sessions.d.ts +143 -1
  52. package/dist/commands/sessions.js +628 -80
  53. package/dist/commands/setup.js +4 -2
  54. package/dist/commands/sync.d.ts +3 -1
  55. package/dist/commands/sync.js +156 -4
  56. package/dist/commands/teams.js +217 -0
  57. package/dist/commands/versions.js +2 -4
  58. package/dist/commands/view.d.ts +3 -0
  59. package/dist/commands/view.js +19 -8
  60. package/dist/commands/wallet.d.ts +6 -0
  61. package/dist/commands/wallet.js +22 -5
  62. package/dist/commands/watchdog.d.ts +18 -0
  63. package/dist/commands/watchdog.js +238 -0
  64. package/dist/index.js +82 -32
  65. package/dist/lib/agents.js +30 -9
  66. package/dist/lib/audit/log.d.ts +92 -0
  67. package/dist/lib/audit/log.js +177 -0
  68. package/dist/lib/auto-pull.js +2 -1
  69. package/dist/lib/browser/cdp.js +5 -1
  70. package/dist/lib/browser/chrome.d.ts +10 -0
  71. package/dist/lib/browser/chrome.js +18 -7
  72. package/dist/lib/browser/drivers/ssh.d.ts +8 -0
  73. package/dist/lib/browser/drivers/ssh.js +77 -16
  74. package/dist/lib/browser/har.d.ts +84 -0
  75. package/dist/lib/browser/har.js +77 -0
  76. package/dist/lib/browser/ipc.d.ts +3 -0
  77. package/dist/lib/browser/ipc.js +37 -12
  78. package/dist/lib/browser/profiles.d.ts +1 -1
  79. package/dist/lib/browser/profiles.js +8 -10
  80. package/dist/lib/browser/refs.d.ts +65 -0
  81. package/dist/lib/browser/refs.js +73 -1
  82. package/dist/lib/browser/runtime-state.js +1 -0
  83. package/dist/lib/browser/service.d.ts +38 -2
  84. package/dist/lib/browser/service.js +183 -23
  85. package/dist/lib/browser/types.d.ts +14 -1
  86. package/dist/lib/budget/live-cloud.d.ts +42 -0
  87. package/dist/lib/budget/live-cloud.js +79 -0
  88. package/dist/lib/budget/live-team.d.ts +31 -0
  89. package/dist/lib/budget/live-team.js +115 -0
  90. package/dist/lib/cloud/codex.js +4 -0
  91. package/dist/lib/cloud/rush.d.ts +12 -1
  92. package/dist/lib/cloud/rush.js +13 -3
  93. package/dist/lib/cloud/types.d.ts +9 -0
  94. package/dist/lib/computer/dispatch.d.ts +8 -0
  95. package/dist/lib/computer/dispatch.js +125 -0
  96. package/dist/lib/computer/loop.d.ts +62 -0
  97. package/dist/lib/computer/loop.js +98 -0
  98. package/dist/lib/computer/model.d.ts +44 -0
  99. package/dist/lib/computer/model.js +157 -0
  100. package/dist/lib/concurrency.d.ts +19 -0
  101. package/dist/lib/concurrency.js +33 -0
  102. package/dist/lib/daemon.d.ts +92 -0
  103. package/dist/lib/daemon.js +222 -18
  104. package/dist/lib/devices/connect.d.ts +3 -2
  105. package/dist/lib/devices/connect.js +5 -3
  106. package/dist/lib/devices/registry.d.ts +7 -0
  107. package/dist/lib/devices/registry.js +24 -0
  108. package/dist/lib/devices/tailscale.js +1 -1
  109. package/dist/lib/drift.d.ts +52 -0
  110. package/dist/lib/drift.js +112 -0
  111. package/dist/lib/events.d.ts +10 -3
  112. package/dist/lib/events.js +101 -24
  113. package/dist/lib/exec.d.ts +17 -0
  114. package/dist/lib/exec.js +100 -12
  115. package/dist/lib/git.d.ts +27 -0
  116. package/dist/lib/git.js +56 -1
  117. package/dist/lib/hooks/cache.d.ts +6 -0
  118. package/dist/lib/hooks/cache.js +54 -12
  119. package/dist/lib/hooks.d.ts +27 -0
  120. package/dist/lib/hooks.js +135 -10
  121. package/dist/lib/hosts/dispatch.d.ts +15 -0
  122. package/dist/lib/hosts/dispatch.js +45 -13
  123. package/dist/lib/hosts/logs.d.ts +16 -0
  124. package/dist/lib/hosts/logs.js +74 -0
  125. package/dist/lib/hosts/option.js +1 -1
  126. package/dist/lib/hosts/passthrough.js +3 -1
  127. package/dist/lib/hosts/progress.d.ts +66 -0
  128. package/dist/lib/hosts/progress.js +125 -17
  129. package/dist/lib/hosts/ready.d.ts +51 -7
  130. package/dist/lib/hosts/ready.js +98 -25
  131. package/dist/lib/hosts/reconcile.d.ts +53 -0
  132. package/dist/lib/hosts/reconcile.js +81 -0
  133. package/dist/lib/hosts/registry.d.ts +19 -2
  134. package/dist/lib/hosts/registry.js +58 -2
  135. package/dist/lib/hosts/remote-cmd.d.ts +62 -1
  136. package/dist/lib/hosts/remote-cmd.js +70 -1
  137. package/dist/lib/hosts/remote-os.d.ts +17 -0
  138. package/dist/lib/hosts/remote-os.js +30 -0
  139. package/dist/lib/hosts/session-index.d.ts +34 -0
  140. package/dist/lib/hosts/session-index.js +56 -0
  141. package/dist/lib/hosts/tasks.d.ts +22 -0
  142. package/dist/lib/hosts/tasks.js +29 -0
  143. package/dist/lib/lock.d.ts +93 -0
  144. package/dist/lib/lock.js +207 -0
  145. package/dist/lib/loop.js +16 -1
  146. package/dist/lib/machine-id.d.ts +21 -0
  147. package/dist/lib/machine-id.js +26 -0
  148. package/dist/lib/mailbox-target.d.ts +36 -0
  149. package/dist/lib/mailbox-target.js +45 -0
  150. package/dist/lib/mailbox.d.ts +47 -0
  151. package/dist/lib/mailbox.js +194 -0
  152. package/dist/lib/mcp.d.ts +5 -0
  153. package/dist/lib/mcp.js +24 -8
  154. package/dist/lib/migrate.d.ts +19 -0
  155. package/dist/lib/migrate.js +134 -26
  156. package/dist/lib/overdue.js +3 -0
  157. package/dist/lib/permissions.d.ts +4 -0
  158. package/dist/lib/permissions.js +35 -0
  159. package/dist/lib/picker.d.ts +26 -0
  160. package/dist/lib/picker.js +127 -0
  161. package/dist/lib/platform/exec.d.ts +46 -0
  162. package/dist/lib/platform/exec.js +74 -0
  163. package/dist/lib/platform/process.d.ts +31 -0
  164. package/dist/lib/platform/process.js +34 -1
  165. package/dist/lib/platform/winpath.js +2 -0
  166. package/dist/lib/plugin-marketplace.d.ts +30 -0
  167. package/dist/lib/plugin-marketplace.js +215 -2
  168. package/dist/lib/plugins.d.ts +5 -0
  169. package/dist/lib/plugins.js +61 -10
  170. package/dist/lib/profiles.d.ts +25 -0
  171. package/dist/lib/profiles.js +22 -6
  172. package/dist/lib/pty-client.js +2 -1
  173. package/dist/lib/rotate.d.ts +61 -0
  174. package/dist/lib/rotate.js +52 -0
  175. package/dist/lib/routines.d.ts +40 -2
  176. package/dist/lib/routines.js +66 -8
  177. package/dist/lib/runner.d.ts +11 -2
  178. package/dist/lib/runner.js +49 -7
  179. package/dist/lib/scheduler.js +6 -1
  180. package/dist/lib/secrets/bundles.d.ts +60 -4
  181. package/dist/lib/secrets/bundles.js +131 -12
  182. package/dist/lib/secrets/filestore.d.ts +3 -0
  183. package/dist/lib/secrets/filestore.js +42 -16
  184. package/dist/lib/secrets/index.d.ts +43 -2
  185. package/dist/lib/secrets/index.js +102 -3
  186. package/dist/lib/secrets/mcp.d.ts +93 -0
  187. package/dist/lib/secrets/mcp.js +205 -0
  188. package/dist/lib/secrets/remote.js +12 -5
  189. package/dist/lib/secrets/sync.js +83 -4
  190. package/dist/lib/secrets/windows.js +14 -3
  191. package/dist/lib/serve/data.d.ts +81 -0
  192. package/dist/lib/serve/data.js +91 -0
  193. package/dist/lib/serve/page.d.ts +7 -0
  194. package/dist/lib/serve/page.js +140 -0
  195. package/dist/lib/serve/server.d.ts +46 -0
  196. package/dist/lib/serve/server.js +115 -0
  197. package/dist/lib/session/active.d.ts +61 -0
  198. package/dist/lib/session/active.js +196 -17
  199. package/dist/lib/session/discover.d.ts +37 -0
  200. package/dist/lib/session/discover.js +204 -27
  201. package/dist/lib/session/inject.d.ts +18 -0
  202. package/dist/lib/session/inject.js +21 -0
  203. package/dist/lib/session/parse.d.ts +6 -0
  204. package/dist/lib/session/parse.js +286 -2
  205. package/dist/lib/session/pid-registry.d.ts +22 -0
  206. package/dist/lib/session/pid-registry.js +104 -0
  207. package/dist/lib/session/provenance.d.ts +14 -2
  208. package/dist/lib/session/provenance.js +39 -8
  209. package/dist/lib/session/remote-active.d.ts +26 -0
  210. package/dist/lib/session/remote-active.js +153 -0
  211. package/dist/lib/session/remote-list.d.ts +51 -0
  212. package/dist/lib/session/remote-list.js +213 -0
  213. package/dist/lib/session/remote.d.ts +7 -1
  214. package/dist/lib/session/remote.js +18 -8
  215. package/dist/lib/session/render.js +1 -1
  216. package/dist/lib/session/sync/config.d.ts +1 -7
  217. package/dist/lib/session/sync/config.js +4 -11
  218. package/dist/lib/session/types.d.ts +18 -1
  219. package/dist/lib/session/types.js +1 -1
  220. package/dist/lib/shims.d.ts +44 -6
  221. package/dist/lib/shims.js +143 -57
  222. package/dist/lib/ssh-exec.d.ts +26 -3
  223. package/dist/lib/ssh-exec.js +47 -3
  224. package/dist/lib/ssh-tunnel.d.ts +24 -5
  225. package/dist/lib/ssh-tunnel.js +62 -63
  226. package/dist/lib/staleness/writers/hooks.js +1 -1
  227. package/dist/lib/startup/command-registry.d.ts +8 -0
  228. package/dist/lib/startup/command-registry.js +17 -1
  229. package/dist/lib/state.d.ts +13 -0
  230. package/dist/lib/state.js +103 -9
  231. package/dist/lib/sync-umbrella.d.ts +14 -7
  232. package/dist/lib/sync-umbrella.js +17 -9
  233. package/dist/lib/teams/forEach.d.ts +110 -0
  234. package/dist/lib/teams/forEach.js +186 -0
  235. package/dist/lib/teams/index.d.ts +1 -0
  236. package/dist/lib/teams/index.js +1 -0
  237. package/dist/lib/teams/pr-watch.d.ts +226 -0
  238. package/dist/lib/teams/pr-watch.js +371 -0
  239. package/dist/lib/teams/registry.js +25 -9
  240. package/dist/lib/teams/supervisor.d.ts +14 -1
  241. package/dist/lib/teams/supervisor.js +19 -0
  242. package/dist/lib/teams/worktree.d.ts +9 -0
  243. package/dist/lib/teams/worktree.js +32 -0
  244. package/dist/lib/terminal/backends/ghostty.d.ts +6 -0
  245. package/dist/lib/terminal/backends/ghostty.js +69 -0
  246. package/dist/lib/terminal/backends/index.d.ts +19 -0
  247. package/dist/lib/terminal/backends/index.js +31 -0
  248. package/dist/lib/terminal/backends/iterm.d.ts +6 -0
  249. package/dist/lib/terminal/backends/iterm.js +62 -0
  250. package/dist/lib/terminal/backends/tmux.d.ts +14 -0
  251. package/dist/lib/terminal/backends/tmux.js +23 -0
  252. package/dist/lib/terminal/backends/vscodium-agent.d.ts +31 -0
  253. package/dist/lib/terminal/backends/vscodium-agent.js +72 -0
  254. package/dist/lib/terminal/engine.d.ts +39 -0
  255. package/dist/lib/terminal/engine.js +54 -0
  256. package/dist/lib/terminal/index.d.ts +17 -0
  257. package/dist/lib/terminal/index.js +8 -0
  258. package/dist/lib/terminal/inject.d.ts +204 -0
  259. package/dist/lib/terminal/inject.js +247 -0
  260. package/dist/lib/terminal/policy.d.ts +11 -0
  261. package/dist/lib/terminal/policy.js +11 -0
  262. package/dist/lib/terminal/quote.d.ts +11 -0
  263. package/dist/lib/terminal/quote.js +13 -0
  264. package/dist/lib/terminal/resolve.d.ts +64 -0
  265. package/dist/lib/terminal/resolve.js +90 -0
  266. package/dist/lib/terminal/shell.d.ts +6 -0
  267. package/dist/lib/terminal/shell.js +23 -0
  268. package/dist/lib/terminal/transport.d.ts +15 -0
  269. package/dist/lib/terminal/transport.js +40 -0
  270. package/dist/lib/terminal/types.d.ts +59 -0
  271. package/dist/lib/terminal/types.js +13 -0
  272. package/dist/lib/triggers/webhook.d.ts +85 -0
  273. package/dist/lib/triggers/webhook.js +141 -0
  274. package/dist/lib/types.d.ts +7 -0
  275. package/dist/lib/versions.d.ts +30 -3
  276. package/dist/lib/versions.js +129 -18
  277. package/dist/lib/watchdog/index.d.ts +3 -0
  278. package/dist/lib/watchdog/index.js +5 -0
  279. package/dist/lib/watchdog/read.d.ts +35 -0
  280. package/dist/lib/watchdog/read.js +149 -0
  281. package/dist/lib/watchdog/runner.d.ts +127 -0
  282. package/dist/lib/watchdog/runner.js +322 -0
  283. package/dist/lib/watchdog/watchdog.d.ts +40 -0
  284. package/dist/lib/watchdog/watchdog.js +166 -0
  285. package/dist/lib/watchdog/watchdogTail.d.ts +5 -0
  286. package/dist/lib/watchdog/watchdogTail.js +154 -0
  287. package/dist/lib/whats-new.d.ts +9 -0
  288. package/dist/lib/whats-new.js +35 -0
  289. package/dist/lib/workflows.d.ts +166 -0
  290. package/dist/lib/workflows.js +193 -0
  291. package/package.json +5 -4
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Session → Terminal-Engine injection adapter.
3
+ *
4
+ * The Terminal Engine owns the injection primitive (`injectIntoTerminal`,
5
+ * src/lib/terminal/inject.ts). This thin adapter maps a session's provenance
6
+ * `ReplyRail` (the addressable terminal the feed already derives — provenance.ts:47)
7
+ * to the engine's `InjectTarget`, so a caller can go session → keystrokes in one
8
+ * hop. Kept on the session side because `ReplyRail` is a session concept; the
9
+ * engine stays agnostic of how a target was discovered.
10
+ */
11
+ import type { ReplyRail } from './provenance.js';
12
+ import type { InjectTarget } from '../terminal/inject.js';
13
+ /**
14
+ * Map a session's `ReplyRail` to an engine `InjectTarget`. Today only tmux rails
15
+ * are externally addressable (provenance.ts:143-149); a null rail yields null and
16
+ * the caller must supply a target another way (a pty id, a macOS window).
17
+ */
18
+ export declare function injectTargetFromReplyRail(rail: ReplyRail): InjectTarget | null;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Session → Terminal-Engine injection adapter.
3
+ *
4
+ * The Terminal Engine owns the injection primitive (`injectIntoTerminal`,
5
+ * src/lib/terminal/inject.ts). This thin adapter maps a session's provenance
6
+ * `ReplyRail` (the addressable terminal the feed already derives — provenance.ts:47)
7
+ * to the engine's `InjectTarget`, so a caller can go session → keystrokes in one
8
+ * hop. Kept on the session side because `ReplyRail` is a session concept; the
9
+ * engine stays agnostic of how a target was discovered.
10
+ */
11
+ /**
12
+ * Map a session's `ReplyRail` to an engine `InjectTarget`. Today only tmux rails
13
+ * are externally addressable (provenance.ts:143-149); a null rail yields null and
14
+ * the caller must supply a target another way (a pty id, a macOS window).
15
+ */
16
+ export function injectTargetFromReplyRail(rail) {
17
+ if (rail && rail.rail === 'tmux') {
18
+ return { backend: 'tmux', pane: rail.target, socket: rail.socket };
19
+ }
20
+ return null;
21
+ }
@@ -50,6 +50,12 @@ export declare function parseCodex(filePath: string): SessionEvent[];
50
50
  export declare function parseCodexContent(content: string): SessionEvent[];
51
51
  /** Parse a Gemini JSON session file into normalized events. */
52
52
  export declare function parseGemini(filePath: string): SessionEvent[];
53
+ /**
54
+ * Parse an Antigravity conversation SQLite DB into normalized tool_use events.
55
+ * Deduped by the tool-call id so each tool appears once (Antigravity writes a
56
+ * request step and a completion step that share the id).
57
+ */
58
+ export declare function parseAntigravity(dbPath: string): SessionEvent[];
53
59
  /**
54
60
  * Parse an OpenCode session from its SQLite database.
55
61
  * filePath format: "/path/to/opencode.db#session_id"
@@ -8,6 +8,7 @@
8
8
  import * as fs from 'fs';
9
9
  import * as path from 'path';
10
10
  import { execFileSync } from 'child_process';
11
+ import Database from '../sqlite.js';
11
12
  /**
12
13
  * Largest session file we will load into memory. Above this we throw a clean
13
14
  * error instead of OOMing or hitting V8's ERR_STRING_TOO_LONG. Aligns with
@@ -96,6 +97,9 @@ export function parseSession(filePath, agent) {
96
97
  case 'gemini':
97
98
  events = parseGemini(filePath);
98
99
  break;
100
+ case 'antigravity':
101
+ events = parseAntigravity(filePath);
102
+ break;
99
103
  case 'opencode':
100
104
  events = parseOpenCode(filePath);
101
105
  break;
@@ -131,6 +135,12 @@ export function detectAgent(filePath) {
131
135
  return 'claude';
132
136
  if (filePath.includes('/.codex/') || filePath.includes('\\.codex\\'))
133
137
  return 'codex';
138
+ // Antigravity lives under ~/.gemini/antigravity-cli/conversations/<uuid>.db, so
139
+ // it must be matched BEFORE the generic /.gemini/ check below or it would be
140
+ // misdetected as Gemini.
141
+ if ((filePath.includes('/antigravity-cli/conversations/') || filePath.includes('\\antigravity-cli\\conversations\\'))
142
+ && filePath.endsWith('.db'))
143
+ return 'antigravity';
134
144
  if (filePath.includes('/.gemini/') || filePath.includes('\\.gemini\\'))
135
145
  return 'gemini';
136
146
  if (filePath.includes('/.grok/') || filePath.includes('\\.grok\\'))
@@ -178,6 +188,11 @@ export function summarizeToolUse(tool, args) {
178
188
  case 'WebSearch':
179
189
  case 'WebFetch':
180
190
  return `${tool}: ${truncate(args.query || args.url || '', 80)}`;
191
+ // Codex plan tool: arrives as a function_call with {plan:[{step,status}]}.
192
+ case 'update_plan': {
193
+ const steps = Array.isArray(args.plan) ? args.plan.length : 0;
194
+ return `Plan: ${steps} step${steps === 1 ? '' : 's'}`;
195
+ }
181
196
  // Codex tools
182
197
  case 'exec_command':
183
198
  return `Bash: ${truncate(String(args.command || args.cmd || '').replace(/\n/g, ' ').trim(), 120)}`;
@@ -418,6 +433,16 @@ export function parseClaudeContent(content) {
418
433
  export function parseCodex(filePath) {
419
434
  return parseCodexContent(safeReadSessionFile(filePath));
420
435
  }
436
+ /**
437
+ * Extract the target file path from a Codex apply_patch envelope. The patch body
438
+ * opens with `*** Begin Patch` and carries one or more file ops of the form
439
+ * `*** Update File: <path>` / `*** Add File: <path>` / `*** Delete File: <path>`.
440
+ * Returns the first file path found, or undefined for an unparseable body.
441
+ */
442
+ function applyPatchTargetPath(input) {
443
+ const m = input.match(/^\*\*\* (?:Update|Add|Delete) File: (.+)$/m);
444
+ return m ? m[1].trim() : undefined;
445
+ }
421
446
  /**
422
447
  * Parse Codex JSONL *content* (already read into a string) into normalized
423
448
  * events. Split from `parseCodex` so the tail reader can parse just the last
@@ -449,6 +474,23 @@ export function parseCodexContent(content) {
449
474
  });
450
475
  continue;
451
476
  }
477
+ if (lineType === 'event_msg') {
478
+ // Web search is reported out-of-band as event_msg (not response_item).
479
+ // The paired begin (`web_search_call`) has no query; only `web_search_end`
480
+ // carries the resolved query string, so we emit exactly one tool_use per
481
+ // search off the end event and ignore the begin to avoid duplicates.
482
+ if (payload.type === 'web_search_end') {
483
+ const query = typeof payload.query === 'string' ? payload.query : '';
484
+ events.push({
485
+ type: 'tool_use',
486
+ agent: 'codex',
487
+ timestamp,
488
+ tool: 'WebSearch',
489
+ args: { query },
490
+ });
491
+ }
492
+ continue;
493
+ }
452
494
  if (lineType === 'response_item') {
453
495
  const ptype = payload.type;
454
496
  if (ptype === 'message') {
@@ -523,6 +565,45 @@ export function parseCodexContent(content) {
523
565
  if (callId)
524
566
  callMap.delete(callId);
525
567
  }
568
+ else if (ptype === 'custom_tool_call') {
569
+ // Codex edits arrive as custom_tool_call (apply_patch), NOT function_call.
570
+ const rawName = payload.name || 'unknown';
571
+ const input = typeof payload.input === 'string' ? payload.input : '';
572
+ const isApplyPatch = rawName === 'apply_patch';
573
+ const patchPath = isApplyPatch ? applyPatchTargetPath(input) : undefined;
574
+ // Normalize apply_patch to the shared Edit tool so it flows through
575
+ // artifact discovery (WRITE_TOOLS) and the Edit summarizer.
576
+ const tool = isApplyPatch ? 'Edit' : rawName;
577
+ const args = { input: input.length > 500 ? input.slice(0, 497) + '...' : input };
578
+ if (patchPath)
579
+ args.file_path = patchPath;
580
+ const callId = payload.call_id || payload.id;
581
+ if (callId)
582
+ callMap.set(callId, { name: tool, args });
583
+ events.push({
584
+ type: 'tool_use',
585
+ agent: 'codex',
586
+ timestamp,
587
+ tool,
588
+ args,
589
+ path: patchPath,
590
+ });
591
+ }
592
+ else if (ptype === 'custom_tool_call_output') {
593
+ const callId = payload.call_id || payload.id;
594
+ const callInfo = callId ? callMap.get(callId) : undefined;
595
+ const output = String(payload.output || '');
596
+ events.push({
597
+ type: 'tool_result',
598
+ agent: 'codex',
599
+ timestamp,
600
+ tool: callInfo?.name,
601
+ success: true,
602
+ output: output.length > 500 ? output.slice(0, 497) + '...' : output,
603
+ });
604
+ if (callId)
605
+ callMap.delete(callId);
606
+ }
526
607
  else if (ptype === 'reasoning') {
527
608
  // Codex reasoning -- try to get the readable summary
528
609
  const summaries = payload.summary || [];
@@ -670,6 +751,206 @@ function extractGeminiContent(content) {
670
751
  }
671
752
  return '';
672
753
  }
754
+ /** Read a base-128 varint from `b` at offset `i`; returns [value, nextOffset]. */
755
+ function readVarint(b, i) {
756
+ let shift = 0;
757
+ let val = 0;
758
+ for (;;) {
759
+ const byte = b[i++];
760
+ val += (byte & 0x7f) * 2 ** shift;
761
+ if ((byte & 0x80) === 0)
762
+ break;
763
+ shift += 7;
764
+ }
765
+ return [val, i];
766
+ }
767
+ /** Decode a protobuf message into a flat list of fields (one nesting level). */
768
+ function decodeProtoMessage(b) {
769
+ const out = [];
770
+ let i = 0;
771
+ while (i < b.length) {
772
+ let tag;
773
+ [tag, i] = readVarint(b, i);
774
+ const field = tag >>> 3;
775
+ const wire = tag & 7;
776
+ if (wire === 0) {
777
+ let v;
778
+ [v, i] = readVarint(b, i);
779
+ out.push({ field, wire, value: v });
780
+ }
781
+ else if (wire === 2) {
782
+ let len;
783
+ [len, i] = readVarint(b, i);
784
+ out.push({ field, wire, value: b.subarray(i, i + len) });
785
+ i += len;
786
+ }
787
+ else if (wire === 5) {
788
+ i += 4; // fixed32 — skipped
789
+ out.push({ field, wire, value: 0 });
790
+ }
791
+ else if (wire === 1) {
792
+ i += 8; // fixed64 — skipped
793
+ out.push({ field, wire, value: 0 });
794
+ }
795
+ else {
796
+ break; // unknown wire type — stop to avoid runaway reads
797
+ }
798
+ }
799
+ return out;
800
+ }
801
+ const ANTIGRAVITY_TEXT_DECODER = new TextDecoder('utf-8', { fatal: false });
802
+ /**
803
+ * Recursively locate the tool-call node: the first sub-message that carries an
804
+ * f2 string tool-name AND an f3 JSON-args string. Also captures the f1 call id
805
+ * (used to dedupe the request + completion steps) and the f30/f31 human labels.
806
+ */
807
+ function findAntigravityToolCall(fields) {
808
+ let id;
809
+ let name;
810
+ let argsJson;
811
+ let summary;
812
+ let action;
813
+ const subs = [];
814
+ for (const f of fields) {
815
+ if (f.wire !== 2)
816
+ continue;
817
+ const bytes = f.value;
818
+ const s = ANTIGRAVITY_TEXT_DECODER.decode(bytes);
819
+ if (f.field === 1 && !id && /^[a-z0-9]{4,16}$/.test(s))
820
+ id = s;
821
+ else if (f.field === 2 && !name && /^[a-z][a-z_]{2,30}$/.test(s))
822
+ name = s;
823
+ else if (f.field === 3 && !argsJson && s.startsWith('{') && s.includes('"toolAction"'))
824
+ argsJson = s;
825
+ else if (f.field === 30 && !summary)
826
+ summary = s;
827
+ else if (f.field === 31 && !action)
828
+ action = s;
829
+ else {
830
+ try {
831
+ subs.push(decodeProtoMessage(bytes));
832
+ }
833
+ catch {
834
+ /* not a nested message */
835
+ }
836
+ }
837
+ }
838
+ if (name && argsJson) {
839
+ let args = {};
840
+ try {
841
+ args = JSON.parse(argsJson);
842
+ }
843
+ catch {
844
+ args = { _raw: argsJson };
845
+ }
846
+ return { id, name, args, summary, action };
847
+ }
848
+ for (const sub of subs) {
849
+ const hit = findAntigravityToolCall(sub);
850
+ if (hit)
851
+ return hit;
852
+ }
853
+ return null;
854
+ }
855
+ /**
856
+ * Map an Antigravity tool name onto the shared normalized vocabulary that the
857
+ * renderer already handles (so no render.ts changes are needed). Unknown tools
858
+ * pass through untouched (with their JSON args) so future tools are captured.
859
+ */
860
+ const ANTIGRAVITY_TOOL_MAP = {
861
+ run_command: 'Bash',
862
+ view_file: 'Read',
863
+ read_file: 'Read',
864
+ list_dir: 'LS',
865
+ grep_search: 'Grep',
866
+ replace_file_content: 'Edit',
867
+ write_to_file: 'Write',
868
+ // Web tools surface identically once observed:
869
+ search_web: 'WebSearch',
870
+ read_url: 'WebFetch',
871
+ execute_url: 'WebFetch',
872
+ };
873
+ /**
874
+ * Parse an Antigravity conversation SQLite DB into normalized tool_use events.
875
+ * Deduped by the tool-call id so each tool appears once (Antigravity writes a
876
+ * request step and a completion step that share the id).
877
+ */
878
+ export function parseAntigravity(dbPath) {
879
+ // Read the raw BLOB payloads through the node/bun SQLite wrapper (not the
880
+ // `sqlite3` CLI) so this works on every OS — the CLI is absent on Windows.
881
+ let rows;
882
+ let db;
883
+ try {
884
+ db = new Database(dbPath);
885
+ rows = db
886
+ .prepare('SELECT idx, step_type, step_payload FROM steps ORDER BY idx;')
887
+ .all();
888
+ }
889
+ catch {
890
+ /* DB not accessible, sqlite module unavailable, or query failed */
891
+ return [];
892
+ }
893
+ finally {
894
+ try {
895
+ db?.close();
896
+ }
897
+ catch {
898
+ /* best-effort close */
899
+ }
900
+ }
901
+ // Single timestamp for the whole session: the steps table carries no per-step
902
+ // time column, so fall back to the DB file's mtime for a stable, sortable value.
903
+ let timestamp = new Date().toISOString();
904
+ try {
905
+ timestamp = fs.statSync(dbPath).mtime.toISOString();
906
+ }
907
+ catch {
908
+ /* file vanished between query and stat — keep now() */
909
+ }
910
+ const events = [];
911
+ const seenCallIds = new Set();
912
+ for (const row of rows) {
913
+ const payload = row.step_payload;
914
+ // Both node:sqlite and bun:sqlite return a BLOB as a Uint8Array (Buffer is
915
+ // a subclass). NULL / non-blob payloads are skipped.
916
+ if (!(payload instanceof Uint8Array))
917
+ continue;
918
+ const bytes = payload;
919
+ let fields;
920
+ try {
921
+ fields = decodeProtoMessage(bytes);
922
+ }
923
+ catch {
924
+ continue;
925
+ }
926
+ const call = findAntigravityToolCall(fields);
927
+ if (!call)
928
+ continue;
929
+ // Dedupe: the request + completion steps of one tool share the f1 call id.
930
+ if (call.id) {
931
+ if (seenCallIds.has(call.id))
932
+ continue;
933
+ seenCallIds.add(call.id);
934
+ }
935
+ const norm = ANTIGRAVITY_TOOL_MAP[call.name] || call.name;
936
+ const a = call.args || {};
937
+ events.push({
938
+ type: 'tool_use',
939
+ agent: 'antigravity',
940
+ timestamp,
941
+ tool: norm,
942
+ args: a,
943
+ command: norm === 'Bash' ? a.CommandLine : undefined,
944
+ // Antigravity uses PascalCase arg keys; probe the known path-bearing ones.
945
+ path: a.AbsolutePath || a.TargetFile || a.DirectoryPath || a.SearchPath || undefined,
946
+ // Antigravity's own short human label — free, high quality. It surfaces
947
+ // either as the f30 string or (more reliably) as `toolSummary` inside the
948
+ // f3 JSON args.
949
+ content: call.summary || (typeof a.toolSummary === 'string' ? a.toolSummary : undefined) || undefined,
950
+ });
951
+ }
952
+ return events;
953
+ }
673
954
  // ---------------------------------------------------------------------------
674
955
  // OpenCode parser
675
956
  // ---------------------------------------------------------------------------
@@ -1086,7 +1367,10 @@ export function parseKimi(filePath) {
1086
1367
  const fn = event.function || {};
1087
1368
  const toolName = typeof event.name === 'string' ? event.name : (fn.name || 'unknown');
1088
1369
  let args = {};
1089
- if (typeof fn.arguments === 'string') {
1370
+ if (event.args && typeof event.args === 'object') {
1371
+ args = event.args;
1372
+ }
1373
+ else if (typeof fn.arguments === 'string') {
1090
1374
  try {
1091
1375
  args = JSON.parse(fn.arguments);
1092
1376
  }
@@ -1212,7 +1496,7 @@ export function parseDroid(filePath) {
1212
1496
  tool: toolName,
1213
1497
  args: toolInput,
1214
1498
  path: toolInput.file_path || toolInput.path || undefined,
1215
- command: toolName === 'Bash' ? toolInput.command : undefined,
1499
+ command: (toolName === 'Bash' || toolName === 'Execute') ? toolInput.command : undefined,
1216
1500
  });
1217
1501
  }
1218
1502
  else if (block.type === 'tool_result') {
@@ -0,0 +1,22 @@
1
+ export interface PidSessionEntry {
2
+ pid: number;
3
+ agent: string;
4
+ /** The launch session id. Present for agents launched with a known id (Claude). */
5
+ sessionId?: string;
6
+ cwd?: string;
7
+ /**
8
+ * `$TMUX_PANE` at launch — stored for diagnostics and possible future
9
+ * disambiguation. NOT currently consulted on read: the listing path keys
10
+ * purely on pid (stale entries are pruned when the pid dies), so this is
11
+ * metadata, not an anti-collision key.
12
+ */
13
+ tmuxPane?: string;
14
+ startedAtMs: number;
15
+ }
16
+ export declare function extractSessionIdArg(args: string[]): string | undefined;
17
+ /** Record a launched agent process. Never throws — the registry is an optimization. */
18
+ export declare function writePidSessionEntry(entry: PidSessionEntry): void;
19
+ /** Look up a live pid's recorded session. Returns undefined if absent/corrupt. */
20
+ export declare function readPidSessionEntry(pid: number): PidSessionEntry | undefined;
21
+ /** Remove entries whose pid is no longer alive. Best-effort housekeeping. */
22
+ export declare function prunePidSessionRegistry(isAlive: (pid: number) => boolean): void;
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Per-PID session registry — the headless equivalent of the swarmify VS Code
3
+ * extension's `live-terminals.json`.
4
+ *
5
+ * On a machine with no terminal extension (a bare SSH/tmux host), the only way
6
+ * `ag sessions --active` can attribute a `ps`-discovered agent process to a
7
+ * session is to guess "newest .jsonl in the cwd" — which collapses N agents
8
+ * sharing one repo onto a single session (all rows show the same topic/id).
9
+ *
10
+ * `ag run` closes that gap by recording, at spawn time, one file per launched
11
+ * agent process keyed by its OS pid: the exact session id it was launched with
12
+ * (Claude is started with `--session-id <uuid>`, so the launcher knows it),
13
+ * plus agent/cwd/tmux pane. The active-sessions headless path reads it back for
14
+ * an exact pid -> session match instead of a heuristic.
15
+ *
16
+ * Best-effort throughout: a failed write or a corrupt file degrades to the old
17
+ * heuristic, never throws into the launch or the listing path.
18
+ */
19
+ import fs from 'fs';
20
+ import path from 'path';
21
+ import { getTerminalsDir } from '../state.js';
22
+ /**
23
+ * Pull an explicit `--session-id <uuid>` (or `--session-id=<uuid>`) out of a
24
+ * raw agent arg vector. The transparent shim forwards args untouched, but when
25
+ * a launcher (Claude Code background jobs, IDE harnesses) already names the
26
+ * session, recording it gives the same exact pid -> session mapping `ag run`
27
+ * gets from generating the id itself.
28
+ */
29
+ const SESSION_ID_VALUE_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
30
+ export function extractSessionIdArg(args) {
31
+ for (let i = 0; i < args.length; i++) {
32
+ const a = args[i];
33
+ if (a === '--session-id') {
34
+ const v = args[i + 1];
35
+ if (v && SESSION_ID_VALUE_RE.test(v))
36
+ return v;
37
+ }
38
+ else if (a.startsWith('--session-id=')) {
39
+ const v = a.slice('--session-id='.length);
40
+ if (SESSION_ID_VALUE_RE.test(v))
41
+ return v;
42
+ }
43
+ }
44
+ return undefined;
45
+ }
46
+ function pidRegistryDir() {
47
+ return path.join(getTerminalsDir(), 'by-pid');
48
+ }
49
+ function entryPath(pid) {
50
+ return path.join(pidRegistryDir(), `${pid}.json`);
51
+ }
52
+ /** Record a launched agent process. Never throws — the registry is an optimization. */
53
+ export function writePidSessionEntry(entry) {
54
+ if (!entry.pid || entry.pid < 1)
55
+ return;
56
+ try {
57
+ fs.mkdirSync(pidRegistryDir(), { recursive: true });
58
+ fs.writeFileSync(entryPath(entry.pid), JSON.stringify(entry), 'utf8');
59
+ }
60
+ catch {
61
+ /* degrade to the newest-jsonl heuristic */
62
+ }
63
+ }
64
+ /** Look up a live pid's recorded session. Returns undefined if absent/corrupt. */
65
+ export function readPidSessionEntry(pid) {
66
+ let raw;
67
+ try {
68
+ raw = fs.readFileSync(entryPath(pid), 'utf8');
69
+ }
70
+ catch {
71
+ return undefined;
72
+ }
73
+ try {
74
+ const parsed = JSON.parse(raw);
75
+ if (parsed && typeof parsed === 'object' && typeof parsed.pid === 'number') {
76
+ return parsed;
77
+ }
78
+ }
79
+ catch {
80
+ /* unparseable */
81
+ }
82
+ return undefined;
83
+ }
84
+ /** Remove entries whose pid is no longer alive. Best-effort housekeeping. */
85
+ export function prunePidSessionRegistry(isAlive) {
86
+ let files;
87
+ try {
88
+ files = fs.readdirSync(pidRegistryDir()).filter(f => f.endsWith('.json'));
89
+ }
90
+ catch {
91
+ return;
92
+ }
93
+ for (const f of files) {
94
+ const pid = Number(f.slice(0, -'.json'.length));
95
+ if (!Number.isInteger(pid) || isAlive(pid))
96
+ continue;
97
+ try {
98
+ fs.unlinkSync(path.join(pidRegistryDir(), f));
99
+ }
100
+ catch {
101
+ /* raced with another writer/pruner */
102
+ }
103
+ }
104
+ }
@@ -13,11 +13,16 @@ export interface MuxLocation {
13
13
  /** screen session name from $STY, e.g. '12345.pts-0.host'. */
14
14
  session?: string;
15
15
  }
16
- /** How the feed can type back into a session, derived from rails that exist today. */
16
+ /** How the feed can type back into a session, derived from env rails that exist today. */
17
17
  export type ReplyRail = {
18
18
  rail: 'tmux';
19
19
  target: string;
20
20
  socket?: string;
21
+ }
22
+ /** iTerm2 split addressed by its session UUID (the part of $ITERM_SESSION_ID after ':'). */
23
+ | {
24
+ rail: 'iterm';
25
+ session: string;
21
26
  } | null;
22
27
  export interface SessionProvenance {
23
28
  /** Machine the process runs on — os.hostname(). Drives HOSTS grouping. */
@@ -34,7 +39,14 @@ export interface SessionProvenance {
34
39
  reply: ReplyRail;
35
40
  }
36
41
  /** Env vars that carry provenance. Kept small so the macOS `ps` scan stays cheap. */
37
- export declare const PROVENANCE_ENV_KEYS: readonly ["SSH_CONNECTION", "SSH_TTY", "TMUX", "TMUX_PANE", "TERM_PROGRAM", "STY"];
42
+ export declare const PROVENANCE_ENV_KEYS: readonly ["SSH_CONNECTION", "SSH_TTY", "TMUX", "TMUX_PANE", "TERM_PROGRAM", "STY", "ITERM_SESSION_ID"];
43
+ /**
44
+ * Extract the iTerm2 session UUID from `$ITERM_SESSION_ID`, whose value is
45
+ * `w<window>t<tab>p<pane>:<UUID>` — the `:`-suffix is exactly what iTerm2's
46
+ * `id of session` returns, so it's the address for `tell session id …`. Returns
47
+ * undefined for an empty/absent value; tolerates a bare value with no `:`.
48
+ */
49
+ export declare function parseItermSession(value?: string): string | undefined;
38
50
  /** Parse the NUL-separated body of /proc/<pid>/environ into a plain object. */
39
51
  export declare function parseProcEnviron(buf: string): Record<string, string>;
40
52
  /**
@@ -15,9 +15,22 @@
15
15
  * (gone, or owned by another uid) yields `undefined`, never a guess.
16
16
  *
17
17
  * `reply` is a read-only hint, not a send channel: it reports whether a rail
18
- * that can type back into this session exists today (tmux pane => addressable;
19
- * inherited/ignored stdin => null). The feed uses it to decide whether to show
20
- * a Send box. Actually delivering the keystrokes is Gap 2 (pty/tmux send-keys).
18
+ * that can type back into this session exists today and, if so, the exact
19
+ * address to type into. Two env-derived rails, in precedence order:
20
+ *
21
+ * - tmux — `$TMUX_PANE` (+ `$TMUX` socket): `tmux send-keys -t <pane>`. Works
22
+ * inside ANY host app (a tmux pane can live under iTerm/Ghostty/VS
23
+ * Code), so it wins whenever present.
24
+ * - iterm — `$ITERM_SESSION_ID` (`w<n>t<n>p<n>:<UUID>`): the exact iTerm2
25
+ * split, addressable by its session UUID via `tell session id …`
26
+ * (focus-safe, no `activate`). Used when there's no tmux above it.
27
+ *
28
+ * Both are inherited env vars, so the split the agent lives in is recovered with
29
+ * zero cooperation from the agent. Inherited/ignored stdin with neither rail =>
30
+ * null (not env-addressable — resolution may still find an IDE rail off disk,
31
+ * see src/lib/terminal/resolve.ts). The feed uses `reply` to decide whether to
32
+ * show a Send box; the resolver turns it into a concrete inject target (Gap 2,
33
+ * src/lib/terminal/inject.ts).
21
34
  */
22
35
  import * as os from 'os';
23
36
  import { execFile } from 'child_process';
@@ -32,7 +45,20 @@ export const PROVENANCE_ENV_KEYS = [
32
45
  'TMUX_PANE',
33
46
  'TERM_PROGRAM',
34
47
  'STY',
48
+ 'ITERM_SESSION_ID',
35
49
  ];
50
+ /**
51
+ * Extract the iTerm2 session UUID from `$ITERM_SESSION_ID`, whose value is
52
+ * `w<window>t<tab>p<pane>:<UUID>` — the `:`-suffix is exactly what iTerm2's
53
+ * `id of session` returns, so it's the address for `tell session id …`. Returns
54
+ * undefined for an empty/absent value; tolerates a bare value with no `:`.
55
+ */
56
+ export function parseItermSession(value) {
57
+ if (!value)
58
+ return undefined;
59
+ const uuid = value.includes(':') ? value.slice(value.lastIndexOf(':') + 1) : value;
60
+ return uuid.trim() || undefined;
61
+ }
36
62
  /** Parse the NUL-separated body of /proc/<pid>/environ into a plain object. */
37
63
  export function parseProcEnviron(buf) {
38
64
  const env = {};
@@ -99,13 +125,18 @@ export function deriveProvenance(env, hostname) {
99
125
  else if (env.STY) {
100
126
  mux = { kind: 'screen', session: env.STY };
101
127
  }
102
- // A tmux pane is the one rail that lets an external process type into an
103
- // already-running interactive agent (`tmux send-keys -t <pane>`). Everything
104
- // else (inherited stdin from `agents run`, ignored stdin from teams) is not
105
- // externally addressable without relaunching under a pty/tmux rail.
128
+ // Env-addressable rails, in precedence order. A tmux pane wins because it can
129
+ // live inside any host app (`tmux send-keys -t <pane>` reaches it regardless of
130
+ // whether iTerm/Ghostty/VS Code is above it). Absent tmux, an iTerm2 split is
131
+ // addressable by its session UUID. Everything else (inherited stdin from
132
+ // `agents run`, ignored stdin from teams, a plain VS Code integrated terminal)
133
+ // is not env-addressable — the resolver may still find an IDE rail off disk.
134
+ const itermSession = parseItermSession(env.ITERM_SESSION_ID);
106
135
  const reply = mux?.kind === 'tmux' && mux.pane
107
136
  ? { rail: 'tmux', target: mux.pane, socket: mux.socket }
108
- : null;
137
+ : itermSession
138
+ ? { rail: 'iterm', session: itermSession }
139
+ : null;
109
140
  return {
110
141
  host: hostname,
111
142
  transport: ssh ? 'ssh' : 'local',
@@ -0,0 +1,26 @@
1
+ import type { ActiveSession } from './active.js';
2
+ /**
3
+ * Recursion guard, passed as an env var (not a CLI flag) so an OLDER remote
4
+ * `agents` that predates this feature ignores it harmlessly instead of erroring
5
+ * on an unknown option. A remote new enough to fan out reads it and stays local.
6
+ */
7
+ export declare const NO_FANOUT_ENV = "AGENTS_SESSIONS_LOCAL";
8
+ /**
9
+ * Parse a peer's `--active --json` stdout into active sessions, tagging each
10
+ * with `machine`. Defensive against version skew / partial output: non-JSON or
11
+ * a non-array yields `[]`, and non-object entries are dropped rather than
12
+ * throwing. Exported for unit testing without a live tailnet.
13
+ */
14
+ export declare function parseRemoteActive(stdout: string, machine: string): ActiveSession[];
15
+ export interface RemoteActiveResult {
16
+ sessions: ActiveSession[];
17
+ /** How many peer machines we attempted to reach (drives the empty-fleet tip). */
18
+ deviceCount: number;
19
+ }
20
+ /**
21
+ * Gather active sessions from other machines. With an explicit `hosts` list
22
+ * (from `--host`), fan out to exactly those. Otherwise sweep the registered,
23
+ * online devices from `ag devices`, excluding this machine and any without an
24
+ * address. Results from all peers run in parallel and are flattened.
25
+ */
26
+ export declare function gatherRemoteActive(hosts?: string[]): Promise<RemoteActiveResult>;