@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,111 @@
1
+ /**
2
+ * `agents sessions inject <sessionId> <text>` — deliver text into the terminal a
3
+ * running session lives in. The CLI face of the Terminal Engine's Gap 2 primitive
4
+ * (`injectIntoTerminal`, src/lib/terminal/inject.ts), so a native watchdog
5
+ * (RUSH-1415) can shell out to nudge a stalled agent with "continue".
6
+ *
7
+ * Resolution: find the active session by id, map its `provenance.reply` rail
8
+ * (provenance.ts:47) to an engine `InjectTarget` and inject. Today only tmux rails
9
+ * are externally addressable; `--pane`/`--pty` target a backend directly when the
10
+ * handle is already known (skipping the session lookup).
11
+ */
12
+ import chalk from 'chalk';
13
+ import { getActiveSessions } from '../lib/session/active.js';
14
+ import { injectTargetFromReplyRail } from '../lib/session/inject.js';
15
+ import { injectIntoTerminal } from '../lib/terminal/index.js';
16
+ import { setHelpSections } from '../lib/help.js';
17
+ /** Resolve a session id (short or full) to an addressable terminal target. */
18
+ async function resolveTarget(sessionId) {
19
+ const sessions = await getActiveSessions();
20
+ const match = sessions.find((s) => s.sessionId === sessionId || (s.sessionId != null && s.sessionId.startsWith(sessionId)));
21
+ if (!match)
22
+ return { target: null, reason: `No active session matches "${sessionId}".` };
23
+ const rail = match.provenance?.reply;
24
+ if (!rail) {
25
+ return {
26
+ target: null,
27
+ reason: `Session "${sessionId}" has no addressable reply rail (not running under tmux). Relaunch it under a tmux/pty rail to inject.`,
28
+ };
29
+ }
30
+ const target = injectTargetFromReplyRail(rail);
31
+ if (!target)
32
+ return { target: null, reason: `Session "${sessionId}" reply rail is not injectable.` };
33
+ return { target };
34
+ }
35
+ async function runInject(sessionId, text, options) {
36
+ // Direct-target shortcuts skip the session lookup — the watchdog often already
37
+ // holds the pane id or pty session it wants to type into.
38
+ let target = null;
39
+ if (options.pty) {
40
+ target = { backend: 'pty', id: options.pty };
41
+ }
42
+ else if (options.pane) {
43
+ target = { backend: 'tmux', pane: options.pane, socket: options.socket };
44
+ }
45
+ else {
46
+ const resolved = await resolveTarget(sessionId);
47
+ if (!resolved.target) {
48
+ if (options.json)
49
+ console.log(JSON.stringify({ ok: false, error: resolved.reason }));
50
+ else
51
+ console.error(chalk.red(resolved.reason));
52
+ process.exit(1);
53
+ }
54
+ target = resolved.target;
55
+ }
56
+ const res = await injectIntoTerminal(target, text, {
57
+ enter: options.enter !== false,
58
+ combined: options.combined,
59
+ socket: options.socket,
60
+ host: options.host,
61
+ });
62
+ if (options.json) {
63
+ console.log(JSON.stringify(res));
64
+ }
65
+ else if (res.ok) {
66
+ console.log(chalk.green(`Injected into ${res.backend} (${res.writes} write${res.writes === 1 ? '' : 's'}).`));
67
+ }
68
+ else {
69
+ console.error(chalk.red(res.error ?? 'injection failed'));
70
+ }
71
+ if (!res.ok)
72
+ process.exit(1);
73
+ }
74
+ /** Attach the `inject` subcommand to an existing `sessions` command. */
75
+ export function registerSessionsInjectCommand(sessionsCmd) {
76
+ const injectCmd = sessionsCmd
77
+ .command('inject <sessionId> <text>')
78
+ .description('Deliver text (+ Enter) into the terminal a running session lives in — nudge a stalled agent.')
79
+ .option('--pane <id>', 'Target a tmux pane id directly (e.g. %3), skipping session lookup')
80
+ .option('--pty <id>', 'Target an agents-pty session id directly, skipping session lookup')
81
+ .option('--socket <path>', 'tmux socket path (defaults to the session/shared socket)')
82
+ .option('--host <target>', 'Deliver on a remote host over SSH (tmux/AppleScript backends)')
83
+ .option('--no-enter', 'Send only the text, without a trailing Enter')
84
+ .option('--combined', 'Fuse text + Enter into ONE write (default: two writes, Ink-TUI safe)')
85
+ .option('--json', 'Output the InjectResult as JSON');
86
+ setHelpSections(injectCmd, {
87
+ examples: `
88
+ # Nudge a stalled agent by session id (resolves its tmux pane)
89
+ agents sessions inject a1b2c3d4 "continue"
90
+
91
+ # Target a tmux pane directly (what a watchdog already holds)
92
+ agents sessions inject _ "continue" --pane %3 --socket /tmp/agents/tmux.sock
93
+
94
+ # Type into an agents-pty session without submitting
95
+ agents sessions inject _ "ls" --pty $SID --no-enter
96
+ `,
97
+ notes: `
98
+ - Ink-TUI Enter semantics: by default the text and Enter are two separate
99
+ writes, which is what Claude's Ink TUI needs. --combined fuses them.
100
+ - Only sessions running under a tmux pane are addressable by id today
101
+ (provenance.ts reply rails). Use --pane/--pty for direct targeting.
102
+ - Built on the Terminal Engine (src/lib/terminal): --host runs the tmux /
103
+ AppleScript spec over the same SSH transport the launch engine uses.
104
+ `,
105
+ });
106
+ // The parent `sessions` command also defines --json, so it binds there;
107
+ // optsWithGlobals() merges parent + subcommand options so --json is honored.
108
+ injectCmd.action(async (sessionId, text, _options, command) => {
109
+ await runInject(sessionId, text, command.optsWithGlobals());
110
+ });
111
+ }
@@ -5,6 +5,8 @@ export interface PickedSession {
5
5
  }
6
6
  export interface SessionPickerConfig {
7
7
  message: string;
8
+ /** Dim hint line shown under the header (filters/flags tip). */
9
+ subtitle?: string;
8
10
  sessions: SessionMeta[];
9
11
  filter: (query: string) => SessionMeta[];
10
12
  labelFor: (s: SessionMeta, query: string) => string;
@@ -12,6 +12,14 @@ import { linkPath, relativeToCwd } from '../lib/session/render.js';
12
12
  import { renderMarkdown } from '../lib/markdown.js';
13
13
  import { itemPicker } from '../lib/picker.js';
14
14
  import { classifyFileChanges, changeCounts, toolHistogram, detectTestResult } from '../lib/session/digest.js';
15
+ /** A session whose transcript lives on another machine (folded in over the live
16
+ * cross-machine fan-out): its `filePath` is on that peer's disk, so the preview
17
+ * can't parse it locally — it shows metadata + a "resume there" note instead.
18
+ * Keys off `_remote`, not the machine tag, so locally-readable synced mirrors
19
+ * still parse their file normally. */
20
+ function remoteMachineOf(session) {
21
+ return session._remote ? session.machine : undefined;
22
+ }
15
23
  /**
16
24
  * SessionMeta originates in discover.ts (gitBranch, cwd, label, etc. read from
17
25
  * untrusted session files). parseSession sanitizes event payloads at its
@@ -37,9 +45,22 @@ function sanitizeMeta(s) {
37
45
  const previewCache = new Map();
38
46
  /** Build a cached multi-line preview string for display in the session picker. */
39
47
  export function buildPreview(session) {
40
- const cached = previewCache.get(session.id);
48
+ const remote = remoteMachineOf(session);
49
+ const cacheKey = remote ? `${remote}:${session.id}` : session.id;
50
+ const cached = previewCache.get(cacheKey);
41
51
  if (cached)
42
52
  return cached;
53
+ const safe = sanitizeMeta(session);
54
+ // Remote session: the transcript is on the peer's disk, so there is nothing to
55
+ // parse here. Show the metadata header (agent, cwd, msgs, tokens — all carried
56
+ // over in the fan-out) plus where it lives and how to open it.
57
+ if (remote) {
58
+ const note = ' ' + chalk.gray(`on `) + chalk.bold.white(remote)
59
+ + chalk.gray(` — enter to resume there, or space then enter to read it over SSH`);
60
+ const output = [formatHeader(safe, []), '', note].join('\n');
61
+ previewCache.set(cacheKey, output);
62
+ return output;
63
+ }
43
64
  let events = [];
44
65
  let parseError;
45
66
  try {
@@ -48,13 +69,12 @@ export function buildPreview(session) {
48
69
  catch (err) {
49
70
  parseError = sanitizeForTerminal(err?.message ?? String(err));
50
71
  }
51
- const safe = sanitizeMeta(session);
52
72
  const header = formatHeader(safe, events);
53
73
  const body = parseError
54
74
  ? ' ' + chalk.red(`Failed to parse session: ${parseError}`)
55
75
  : formatCompactPreview(events, safe);
56
76
  const output = [header, '', body].filter(Boolean).join('\n');
57
- previewCache.set(session.id, output);
77
+ previewCache.set(cacheKey, output);
58
78
  return output;
59
79
  }
60
80
  function displayAgent(agent) {
@@ -342,6 +362,7 @@ function truncate(s, max) {
342
362
  export async function sessionPicker(config) {
343
363
  const picked = await itemPicker({
344
364
  message: config.message,
365
+ subtitle: config.subtitle,
345
366
  items: config.sessions,
346
367
  filter: config.filter,
347
368
  labelFor: config.labelFor,
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerSessionsResumeCommand(sessionsCmd: Command): void;
@@ -0,0 +1,221 @@
1
+ /**
2
+ * `agents sessions resume` — multi-select sessions and fan each one out into a
3
+ * terminal surface via the terminal launch engine (src/lib/terminal).
4
+ *
5
+ * Unlike the single-select picker behind bare `agents sessions` (which resumes
6
+ * one session in place), this opens a checkbox picker, then asks where the
7
+ * chosen sessions should resume. By default they pack two-per-tab (session 1 in
8
+ * a new tab, session 2 split beside it, session 3 a new tab, …) in the terminal
9
+ * you're in — iTerm / Ghostty / tmux, locally or on a remote host via --host.
10
+ */
11
+ import * as fs from 'fs';
12
+ import chalk from 'chalk';
13
+ import { discoverSessions } from '../lib/session/discover.js';
14
+ import { filterTeamSessions } from '../lib/session/team-filter.js';
15
+ import { multiItemPicker, itemPicker } from '../lib/picker.js';
16
+ import { buildPreview } from './sessions-picker.js';
17
+ import { filterSessionsByQuery, formatPickerLabel, pickerColumnsFor, buildResumeCommand, resumeSessionInPlace, parseAgentFilter, } from './sessions.js';
18
+ import { openSurfaces, availableBackends, detectCurrentBackend, currentContext, } from '../lib/terminal/index.js';
19
+ import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
20
+ import { setHelpSections } from '../lib/help.js';
21
+ import { confirm } from '@inquirer/prompts';
22
+ /** Opening more than this many live sessions at once asks for confirmation first. */
23
+ const CONFIRM_THRESHOLD = 5;
24
+ export function registerSessionsResumeCommand(sessionsCmd) {
25
+ const cmd = sessionsCmd
26
+ .command('resume')
27
+ .argument('[query]', 'Filter sessions before selecting (topic, path, or id fragment)')
28
+ .description('Multi-select sessions and resume each in a terminal tab/split (this terminal, iTerm, Ghostty, tmux, VSCodium; local or --host).')
29
+ .option('-a, --agent <agent>', 'Filter by agent type and version (e.g., claude, codex@0.116.0)')
30
+ .option('--all', 'Include sessions from every directory (not just current project)')
31
+ .option('--teams', 'Include team-spawned sessions (hidden by default)')
32
+ .option('--since <time>', 'Only sessions newer than this (e.g., 2h, 7d, 4w, or ISO date)')
33
+ .option('-n, --limit <n>', 'Maximum number of sessions to load into the picker', '200')
34
+ .option('--host <alias>', 'Resume on a remote host over SSH (defaults to tmux there)')
35
+ .option('--iterm', 'Force the iTerm backend')
36
+ .option('--ghostty', 'Force the Ghostty backend')
37
+ .option('--tmux', 'Force the tmux backend')
38
+ .option('--vscodium', 'Force the VSCodium agent-terminal backend (swarm-ext)')
39
+ .option('--tabs', 'One tab per session (default: pack two panes per tab)');
40
+ setHelpSections(cmd, {
41
+ examples: `
42
+ # Pick several sessions; they pack two-per-tab in your current terminal
43
+ agents sessions resume
44
+
45
+ # Pre-filter the pool before selecting (space in the filter → use [query])
46
+ agents sessions resume "auth middleware"
47
+
48
+ # Force a backend / one tab each / a remote host
49
+ agents sessions resume --ghostty
50
+ agents sessions resume --vscodium
51
+ agents sessions resume --tabs
52
+ agents sessions resume --host zion --tmux
53
+ `,
54
+ notes: `
55
+ - space toggles a session, enter confirms; tab toggles the preview pane.
56
+ - Layout: two-per-tab by default (session 1 → new tab, session 2 → split beside it, …). --tabs disables splitting.
57
+ - VSCodium defaults to one editor tab per session (matches swarm-ext's native agent-terminal UX); the others default to two-per-tab.
58
+ - Backend: auto-detected from the terminal you're in (iTerm / Ghostty / tmux); override with --iterm/--ghostty/--tmux/--vscodium.
59
+ - --vscodium opens each session as an agent terminal tab in VSCodium via the swarm-ext extension (works with --host too).
60
+ - --host <alias> resumes on a remote machine over the same SSH transport as 'sessions --host' (defaults to tmux).
61
+ - Each session opens version-pinned, in its own cwd. Non-resumable agents are skipped with a note.
62
+ `,
63
+ });
64
+ cmd.action(async (query, options) => {
65
+ await sessionsResumeAction(query, options);
66
+ });
67
+ }
68
+ async function sessionsResumeAction(query, options) {
69
+ if (!isInteractiveTerminal()) {
70
+ console.error(chalk.red('sessions resume needs an interactive terminal.'));
71
+ process.exitCode = 1;
72
+ return;
73
+ }
74
+ const { agent, version } = parseAgentFilter(options.agent);
75
+ const limit = parseInt(options.limit || '200', 10);
76
+ const since = options.since ?? (options.all ? undefined : '30d');
77
+ let sessions = await discoverSessions({
78
+ agent,
79
+ version,
80
+ all: options.all,
81
+ cwd: process.cwd(),
82
+ since,
83
+ sortBy: 'timestamp',
84
+ limit,
85
+ excludeTeamOrigin: !options.teams,
86
+ });
87
+ const { visible } = filterTeamSessions(sessions, !!options.teams);
88
+ sessions = visible;
89
+ if (sessions.length === 0) {
90
+ console.log(chalk.gray('No sessions found. Try --all or a different --since window.'));
91
+ return;
92
+ }
93
+ // 1. Multi-select the sessions. gutter: 6 = the multi-select cursor + checkbox
94
+ // ('> [x] ') that multiItemPicker prepends, so rows size to fit without wrapping.
95
+ const cols = { ...pickerColumnsFor(sessions), gutter: 6 };
96
+ let chosen;
97
+ try {
98
+ chosen = await multiItemPicker({
99
+ message: 'Select sessions to resume:',
100
+ items: sessions,
101
+ filter: (q) => (q.trim() ? filterSessionsByQuery(sessions, q) : sessions),
102
+ labelFor: (s, q) => formatPickerLabel(s, q, cols),
103
+ keyFor: (s) => s.id,
104
+ buildPreview,
105
+ pageSize: 15,
106
+ initialSearch: query,
107
+ emptyMessage: 'No sessions match.',
108
+ enterHint: 'resume',
109
+ });
110
+ }
111
+ catch (err) {
112
+ if (isPromptCancelled(err))
113
+ return;
114
+ throw err;
115
+ }
116
+ if (!chosen || chosen.length === 0)
117
+ return;
118
+ // 2. Split the selection into resumable surfaces and skipped agents (no silent drop).
119
+ const items = [];
120
+ for (const s of chosen) {
121
+ const command = buildResumeCommand(s);
122
+ if (!command) {
123
+ console.log(chalk.yellow(` skip ${s.shortId} — resume is not supported for ${s.agent} sessions yet`));
124
+ continue;
125
+ }
126
+ const cwd = s.cwd && fs.existsSync(s.cwd) ? s.cwd : process.cwd();
127
+ items.push({ session: s, cwd, command });
128
+ }
129
+ if (items.length === 0) {
130
+ console.log(chalk.gray('Nothing resumable in the selection.'));
131
+ return;
132
+ }
133
+ // 3. Resolve the backend (and host).
134
+ const ctx = currentContext();
135
+ const backend = await resolveBackend(options, ctx, items.length);
136
+ if (backend === 'cancel')
137
+ return;
138
+ // 4. Guard against opening a flood of live agents.
139
+ if (items.length > CONFIRM_THRESHOLD) {
140
+ const proceed = await confirm({
141
+ message: `Open ${items.length} live sessions at once?`,
142
+ default: false,
143
+ }).catch(() => false);
144
+ if (!proceed)
145
+ return;
146
+ }
147
+ // 5a. No tab-capable backend (off-macOS, not in tmux, local) — resume in place, sequentially.
148
+ if (backend === 'inplace') {
149
+ if (items.length > 1) {
150
+ console.log(chalk.gray(`Resuming ${items.length} sessions one at a time (no tab-capable terminal detected).`));
151
+ }
152
+ for (const it of items)
153
+ await resumeSessionInPlace(it.session);
154
+ return;
155
+ }
156
+ // 5b. Fan out through the engine. Split-packing (two-per-tab) suits the
157
+ // terminal apps; the VSCodium backend defaults to one editor tab per session,
158
+ // matching swarm-ext's native agent-terminal UX. --tabs forces tabs anywhere.
159
+ const packing = options.tabs || backend === 'vscodium-agent' ? 'tabs' : 'two-per-tab';
160
+ const where = options.host ? `${backend} on ${options.host}` : backend;
161
+ console.log(chalk.gray(`Opening ${items.length} session${items.length === 1 ? '' : 's'} in ${where} (${packing})…`));
162
+ const results = await openSurfaces(items.map((it) => ({ cwd: it.cwd, command: it.command })), { backend, host: options.host, packing });
163
+ let opened = 0;
164
+ results.forEach((r, i) => {
165
+ const s = items[i].session;
166
+ if (r.ok) {
167
+ opened++;
168
+ const shape = r.request.layout === 'tab' ? 'tab' : 'split';
169
+ console.log(chalk.green(` opened ${s.shortId}`) + chalk.gray(` — ${shape} — ${items[i].command.join(' ')}`));
170
+ }
171
+ else {
172
+ console.log(chalk.red(` failed ${s.shortId} — ${r.error}`));
173
+ }
174
+ });
175
+ console.log(chalk.gray(`\nOpened ${opened}/${items.length} in ${where}.`));
176
+ }
177
+ /**
178
+ * Decide which backend to launch into. Returns a concrete backend, `'inplace'`
179
+ * (resume in the current process — no GUI/tmux available), or `'cancel'` (the
180
+ * user dismissed the chooser).
181
+ */
182
+ async function resolveBackend(options, ctx, count) {
183
+ const forced = options.iterm ? 'iterm'
184
+ : options.ghostty ? 'ghostty'
185
+ : options.tmux ? 'tmux'
186
+ : options.vscodium ? 'vscodium-agent'
187
+ : undefined;
188
+ if (forced)
189
+ return forced;
190
+ // Remote defaults to tmux (headless, no GUI session assumptions); override with a backend flag.
191
+ if (options.host)
192
+ return 'tmux';
193
+ const available = availableBackends(ctx);
194
+ if (available.length === 0)
195
+ return 'inplace';
196
+ const detected = detectCurrentBackend(ctx);
197
+ // Only one option and it's where we already are → no need to ask.
198
+ if (available.length === 1 && (!detected || detected === available[0].id))
199
+ return available[0].id;
200
+ const choices = available.map((b) => ({
201
+ id: b.id,
202
+ label: b.label,
203
+ detail: b.id === detected ? "the terminal you're in now" : `open in ${b.label}`,
204
+ }));
205
+ try {
206
+ const picked = await itemPicker({
207
+ message: `Resume ${count} session${count === 1 ? '' : 's'} where?`,
208
+ items: choices,
209
+ filter: () => choices,
210
+ labelFor: (c) => `${chalk.bold(c.label.padEnd(10))}${chalk.gray(c.detail)}`,
211
+ shortIdFor: (c) => c.label,
212
+ enterHint: 'open',
213
+ });
214
+ return picked ? picked.item.id : 'cancel';
215
+ }
216
+ catch (err) {
217
+ if (isPromptCancelled(err))
218
+ return 'cancel';
219
+ throw err;
220
+ }
221
+ }
@@ -1,4 +1,7 @@
1
1
  import type { Command } from 'commander';
2
+ import type { SessionMeta, SessionAgentId } from '../lib/session/types.js';
3
+ /** Whether a session's transcript can be live-tailed (append-only JSONL agents). */
4
+ export declare function isTailable(agent: SessionAgentId): boolean;
2
5
  export interface TailFileOptions {
3
6
  /** If true, emit every line from byte 0 first, then follow. Default false (EOF). */
4
7
  fromStart?: boolean;
@@ -15,5 +18,12 @@ export interface TailFileOptions {
15
18
  * parent dir until the file appears).
16
19
  */
17
20
  export declare function tailFile(filePath: string, onLine: (line: string) => void, ac: AbortController, opts?: TailFileOptions): Promise<void>;
21
+ /**
22
+ * Live-tail one already-resolved session's transcript to stdout until Ctrl+C.
23
+ * Shared by `agents sessions tail` and `agents logs -f`.
24
+ */
25
+ export declare function streamSessionTail(session: SessionMeta, options?: {
26
+ fromStart?: boolean;
27
+ }): Promise<void>;
18
28
  /** Attach the `tail` subcommand to an existing `sessions` command. */
19
29
  export declare function registerSessionsTailCommand(sessionsCmd: Command): void;
@@ -12,6 +12,10 @@ import chalk from 'chalk';
12
12
  import { discoverSessions, resolveSessionById } from '../lib/session/discover.js';
13
13
  import { setHelpSections } from '../lib/help.js';
14
14
  const TAIL_SUPPORTED = ['claude', 'codex'];
15
+ /** Whether a session's transcript can be live-tailed (append-only JSONL agents). */
16
+ export function isTailable(agent) {
17
+ return TAIL_SUPPORTED.includes(agent);
18
+ }
15
19
  /**
16
20
  * Tail a file: emit each newline-terminated line via onLine as it's written.
17
21
  *
@@ -182,6 +186,13 @@ async function runTail(sessionId, options) {
182
186
  }
183
187
  session = resolved;
184
188
  }
189
+ await streamSessionTail(session, { fromStart: options.fromStart });
190
+ }
191
+ /**
192
+ * Live-tail one already-resolved session's transcript to stdout until Ctrl+C.
193
+ * Shared by `agents sessions tail` and `agents logs -f`.
194
+ */
195
+ export async function streamSessionTail(session, options = {}) {
185
196
  const filePath = session.filePath.split('#')[0];
186
197
  if (process.stderr.isTTY) {
187
198
  process.stderr.write(chalk.gray(`Tailing ${session.agent} ${session.shortId} — ${filePath}\n`) +
@@ -1,7 +1,31 @@
1
1
  import type { Command } from 'commander';
2
- import type { SessionMeta } from '../lib/session/types.js';
2
+ import type { SessionAgentId, SessionMeta } from '../lib/session/types.js';
3
3
  import { type ActiveSession } from '../lib/session/active.js';
4
4
  import { type PickedSession } from './sessions-picker.js';
5
+ /**
6
+ * Index live sessions by their full session UUID so a historical `SessionMeta`
7
+ * row (`meta.id`) can be matched to the session that is still running now.
8
+ * Rows without a sessionId (some cloud/headless probes) are skipped — they
9
+ * can't be correlated back to a transcript on disk.
10
+ */
11
+ export declare function indexActiveBySessionId(active: ActiveSession[]): Map<string, ActiveSession>;
12
+ /**
13
+ * The live decoration for a listing row: a status glyph and the latest-turn
14
+ * preview, when the session is still running. `●` running / `◐` waiting on the
15
+ * user / `○` idle, colored by the same `statusColor` the --active view uses.
16
+ * Returns empty strings when there is no live match, so callers render the
17
+ * plain historical row unchanged.
18
+ */
19
+ export declare function liveGlyphAndPreview(a: ActiveSession | undefined): {
20
+ glyph: string;
21
+ preview: string;
22
+ };
23
+ /**
24
+ * The tracker/PR ref for a session's dedicated column: the ticket id when known,
25
+ * else `PR#<n>`, else empty. Pulled out of the trailing badge blob so refs align
26
+ * into a scannable column instead of jamming against a truncated topic.
27
+ */
28
+ export declare function ticketLabel(s: Pick<SessionMeta, 'ticketId' | 'prNumber'>): string;
5
29
  /** Grouped + sorted view of active sessions for the --active renderer. */
6
30
  export interface ActiveSessionsLayout {
7
31
  workspaces: Array<{
@@ -29,7 +53,119 @@ export interface ActiveSessionsLayout {
29
53
  * - sessions within a window/flat bucket: input order preserved
30
54
  */
31
55
  export declare function groupActiveSessions(sessions: ActiveSession[]): ActiveSessionsLayout;
56
+ /** One machine's active sessions, keeping the within-machine workspace layout. */
57
+ export interface MachineGroup {
58
+ /** Normalized device id (machineId() form). */
59
+ machine: string;
60
+ /** The machine this command is running on — pinned first and marked. */
61
+ isLocal: boolean;
62
+ total: number;
63
+ layout: ActiveSessionsLayout;
64
+ }
65
+ /** Active sessions grouped by the machine they run on. */
66
+ export interface MachineGroupedLayout {
67
+ machines: MachineGroup[];
68
+ }
69
+ /**
70
+ * Group active sessions by machine, then delegate each machine's sessions to the
71
+ * existing workspace/window grouping. Local machine is pinned first and flagged;
72
+ * the rest sort by session count descending, then name. Pure — `localMachine` is
73
+ * injected so the function stays testable without reading os.hostname().
74
+ */
75
+ export declare function groupSessionsByMachine(sessions: ActiveSession[], localMachine: string): MachineGroupedLayout;
76
+ /**
77
+ * Collapse duplicate sessions after a cross-machine merge. Two rows collapse
78
+ * only when they share both machine and session UUID (the same host listed
79
+ * twice, or a local/remote overlap); rows without a sessionId can't be
80
+ * correlated, so they're all kept.
81
+ */
82
+ export declare function dedupeByMachineSession(sessions: ActiveSession[]): ActiveSession[];
83
+ /**
84
+ * Order a merged listing so the local machine's sessions come first, then each
85
+ * remote machine as a contiguous block (more sessions first, then name), with
86
+ * every machine keeping its incoming order (timestamp) within the block. Also
87
+ * dedupes: a session present both locally (a synced mirror copy) and via live
88
+ * fan-out collapses to one, keyed by machine + session id. Rows are keyed by
89
+ * `machine` (discover tags local rows with the local id; fan-out tags remote
90
+ * rows with the peer id) falling back to `localMachine` when untagged. Pure —
91
+ * `localMachine` is injected so the ordering is testable without os.hostname().
92
+ */
93
+ export declare function mergeLocalFirst(sessions: SessionMeta[], localMachine: string): SessionMeta[];
94
+ /**
95
+ * Group key for the overview: prefer the indexed project name; else fold the cwd
96
+ * to its repo — a worktree (`.../<repo>/.agents/worktrees/<slug>`) folds to the
97
+ * repo, and a monorepo subdir falls back to its leaf dir basename. Pure.
98
+ */
99
+ export declare function overviewProjectKey(s: Pick<SessionMeta, 'project' | 'cwd'>): string;
100
+ export interface OverviewGroup {
101
+ key: string;
102
+ total: number;
103
+ shown: SessionMeta[];
104
+ more: number;
105
+ maxTs: string;
106
+ }
107
+ /**
108
+ * Turn a recency-descending pool into project groups: each group shows its
109
+ * `perProjectCap` most-recent sessions (the rest become `· N more`), and groups
110
+ * are ordered by their most-recent session so the newest-active project leads.
111
+ * `perProjectCap = Infinity` expands every group. Pure — unit-tested.
112
+ */
113
+ export declare function buildOverviewGroups(pool: SessionMeta[], perProjectCap: number): {
114
+ groups: OverviewGroup[];
115
+ projectCount: number;
116
+ };
117
+ /**
118
+ * Render a session's full transcript to stdout — the non-follow view behind
119
+ * `agents logs <sessionId>`. Reuses the same markdown renderer as
120
+ * `agents sessions <id> --markdown`.
121
+ */
122
+ export declare function renderSessionLog(session: SessionMeta): Promise<void>;
123
+ /** Column-visibility flags for the picker row, computed once over the whole pool. */
124
+ export interface PickerColumns {
125
+ /** Render the machine column (only when the pool spans more than one machine). */
126
+ showMachine?: boolean;
127
+ /** Map a full machine id to its compact display form (shared prefix stripped). */
128
+ machineLabel?: (m: string) => string;
129
+ /** Total width of the machine column, sized to the widest compacted hostname
130
+ * in the pool (capped). Falls back to PICKER_MACHINE_W when absent. */
131
+ machineWidth?: number;
132
+ /** Render the ticket/PR column (only when at least one row carries a ref). */
133
+ showTicket?: boolean;
134
+ /**
135
+ * Cells the picker prepends before each row: 2 for the single-select cursor
136
+ * ('> '), 6 for the multi-select cursor + checkbox ('> [x] '). Reserved from
137
+ * the topic width so rows never wrap. Defaults to 2.
138
+ */
139
+ gutter?: number;
140
+ }
141
+ /**
142
+ * Compact display form for machine ids: strip the longest shared dash-delimited
143
+ * prefix so "yosemite-s0"/"yosemite-s1" read as "s0"/"s1" while unrelated ids
144
+ * ("zion", "mac-mini") stay whole. Stripping a *common* prefix can't collide,
145
+ * and at least one segment is always kept.
146
+ */
147
+ export declare function machineLabeler(machines: string[]): (m: string) => string;
148
+ /**
149
+ * Column flags for a picker, computed once over the whole pool so every row
150
+ * aligns: the machine column only earns its width when the listing spans more
151
+ * than one box, the ticket column only when some row carries a PR/ticket ref.
152
+ */
153
+ export declare function pickerColumnsFor(sessions: SessionMeta[]): PickerColumns;
154
+ export declare function formatPickerLabel(s: SessionMeta, query: string, cols?: PickerColumns): string;
155
+ /**
156
+ * Pick a hint to show above the picker. Deterministic (keys off the pool size)
157
+ * so it stays fixed across the picker's re-renders within a single run.
158
+ */
159
+ export declare function formatPickerTip(sessions: SessionMeta[]): string;
32
160
  export declare function pickSessionInteractive(sessions: SessionMeta[], message?: string, initialSearch?: string, hiddenCount?: number): Promise<PickedSession | null>;
161
+ /**
162
+ * Resume a session in the current terminal — a foreground takeover of this
163
+ * process. Used by the single-select picker and by `sessions resume` when the
164
+ * chosen destination is "in place" (unknown emulator / off-macOS, single pick).
165
+ * Falls back to the current version via `/continue` when the version-pinned
166
+ * binary is missing (ENOENT).
167
+ */
168
+ export declare function resumeSessionInPlace(session: SessionMeta): Promise<void>;
33
169
  /**
34
170
  * Build the shell command that resumes a picked session.
35
171
  *
@@ -42,7 +178,13 @@ export declare function pickSessionInteractive(sessions: SessionMeta[], message?
42
178
  * handler in handlePickedSession retries via buildFallbackCommand.
43
179
  */
44
180
  export declare function buildResumeCommand(session: SessionMeta): string[] | null;
181
+ interface AgentFilter {
182
+ agent?: SessionAgentId;
183
+ version?: string;
184
+ }
185
+ export declare function parseAgentFilter(agentName?: string): AgentFilter;
45
186
  /** Filter and rank sessions by a multi-term search query across metadata and content. */
46
187
  export declare function filterSessionsByQuery(sessions: SessionMeta[], query: string | undefined): SessionMeta[];
47
188
  /** Register the `agents sessions` command with all its options and help text. */
48
189
  export declare function registerSessionsCommands(program: Command): void;
190
+ export {};