@phnx-labs/agents-cli 1.22.21 → 1.22.23

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 (105) hide show
  1. package/CHANGELOG.md +256 -0
  2. package/README.md +8 -2
  3. package/dist/commands/doctor.js +15 -7
  4. package/dist/commands/exec.js +20 -6
  5. package/dist/commands/focus.d.ts +76 -4
  6. package/dist/commands/focus.js +219 -40
  7. package/dist/commands/fork.d.ts +20 -2
  8. package/dist/commands/fork.js +91 -64
  9. package/dist/commands/go.d.ts +25 -0
  10. package/dist/commands/go.js +63 -2
  11. package/dist/commands/harness-wizard.d.ts +206 -0
  12. package/dist/commands/harness-wizard.js +403 -0
  13. package/dist/commands/harness.d.ts +12 -0
  14. package/dist/commands/harness.js +97 -101
  15. package/dist/commands/insights.d.ts +32 -0
  16. package/dist/commands/insights.js +478 -0
  17. package/dist/commands/resume.js +10 -3
  18. package/dist/commands/secrets.js +25 -30
  19. package/dist/commands/sessions-picker.d.ts +2 -0
  20. package/dist/commands/sessions-picker.js +1 -0
  21. package/dist/commands/sessions-resume.d.ts +21 -3
  22. package/dist/commands/sessions-resume.js +50 -11
  23. package/dist/commands/sessions.d.ts +81 -5
  24. package/dist/commands/sessions.js +331 -66
  25. package/dist/commands/watchdog.js +13 -2
  26. package/dist/index.js +3 -1
  27. package/dist/lib/agents.d.ts +1 -1
  28. package/dist/lib/agents.js +132 -0
  29. package/dist/lib/codex-policy.d.ts +17 -0
  30. package/dist/lib/codex-policy.js +48 -0
  31. package/dist/lib/crabbox/lease.d.ts +25 -0
  32. package/dist/lib/crabbox/lease.js +62 -0
  33. package/dist/lib/daemon.js +70 -0
  34. package/dist/lib/exec.d.ts +4 -0
  35. package/dist/lib/exec.js +88 -54
  36. package/dist/lib/feed-broadcast.d.ts +1 -20
  37. package/dist/lib/feed-broadcast.js +31 -1
  38. package/dist/lib/hooks.js +12 -2
  39. package/dist/lib/hosts/passthrough.js +1 -0
  40. package/dist/lib/mcp.js +44 -0
  41. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +1 -5
  42. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  43. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +2 -15
  44. package/dist/lib/models.d.ts +0 -5
  45. package/dist/lib/models.js +48 -0
  46. package/dist/lib/picker.d.ts +45 -0
  47. package/dist/lib/picker.js +75 -6
  48. package/dist/lib/plugin-marketplace.js +9 -0
  49. package/dist/lib/pricing/prices.json +119 -92
  50. package/dist/lib/pricing/table.js +13 -0
  51. package/dist/lib/remote-agents-json.d.ts +29 -1
  52. package/dist/lib/remote-agents-json.js +47 -10
  53. package/dist/lib/resources/mcp.js +2 -0
  54. package/dist/lib/resources/permissions.js +3 -0
  55. package/dist/lib/resources/types.d.ts +2 -1
  56. package/dist/lib/runner.js +28 -19
  57. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  58. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -2
  59. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  60. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -13
  61. package/dist/lib/secrets/index.d.ts +15 -1
  62. package/dist/lib/secrets/index.js +118 -49
  63. package/dist/lib/secrets/reaper.d.ts +87 -0
  64. package/dist/lib/secrets/reaper.js +184 -0
  65. package/dist/lib/secrets/remote.d.ts +29 -0
  66. package/dist/lib/secrets/remote.js +37 -1
  67. package/dist/lib/session/active.d.ts +36 -1
  68. package/dist/lib/session/active.js +60 -19
  69. package/dist/lib/session/actor-sidecar.d.ts +14 -0
  70. package/dist/lib/session/actor-sidecar.js +67 -4
  71. package/dist/lib/session/db.d.ts +35 -1
  72. package/dist/lib/session/db.js +132 -1
  73. package/dist/lib/session/digest.d.ts +3 -0
  74. package/dist/lib/session/digest.js +3 -3
  75. package/dist/lib/session/discover.js +168 -0
  76. package/dist/lib/session/insights.d.ts +126 -0
  77. package/dist/lib/session/insights.js +330 -0
  78. package/dist/lib/session/parse.d.ts +29 -2
  79. package/dist/lib/session/parse.js +111 -5
  80. package/dist/lib/session/remote-list.d.ts +10 -1
  81. package/dist/lib/session/remote-list.js +2 -8
  82. package/dist/lib/session/remote.d.ts +57 -3
  83. package/dist/lib/session/remote.js +90 -26
  84. package/dist/lib/session/resume-command.d.ts +6 -0
  85. package/dist/lib/session/resume-command.js +8 -0
  86. package/dist/lib/session/session-cache.d.ts +173 -0
  87. package/dist/lib/session/session-cache.js +399 -0
  88. package/dist/lib/session/types.d.ts +2 -2
  89. package/dist/lib/session/types.js +1 -1
  90. package/dist/lib/session/width.d.ts +1 -1
  91. package/dist/lib/session/width.js +12 -2
  92. package/dist/lib/shims.d.ts +2 -2
  93. package/dist/lib/shims.js +40 -5
  94. package/dist/lib/staleness/writers/hooks.js +1 -1
  95. package/dist/lib/startup/command-registry.d.ts +1 -0
  96. package/dist/lib/startup/command-registry.js +2 -0
  97. package/dist/lib/types.d.ts +1 -1
  98. package/dist/lib/usage.d.ts +13 -0
  99. package/dist/lib/usage.js +254 -3
  100. package/dist/lib/versions.js +13 -2
  101. package/package.json +1 -1
  102. package/dist/bin/agents +0 -0
  103. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  104. package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
  105. package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
@@ -1,6 +1,8 @@
1
1
  import type { Command } from 'commander';
2
- import { type Packing } from '../lib/terminal/index.js';
3
- interface ResumeOptions {
2
+ import { type Backend, type EngineContext, type Packing } from '../lib/terminal/index.js';
3
+ /** Opening more than this many live sessions at once asks for confirmation first. */
4
+ export declare const CONFIRM_THRESHOLD = 5;
5
+ export interface ResumeOptions {
4
6
  agent?: string;
5
7
  all?: boolean;
6
8
  teams?: boolean;
@@ -16,5 +18,21 @@ interface ResumeOptions {
16
18
  splits?: boolean;
17
19
  }
18
20
  export declare function registerSessionsResumeCommand(sessionsCmd: Command): void;
21
+ /** IDs and tmux aliases are actions, not picker search text. Human phrases keep
22
+ * the existing pre-filtered picker, while an explicit identity resumes directly. */
23
+ export declare function isDirectResumeSelector(query: string): boolean;
24
+ /** Re-enter through the top-level command so fleet routing and harness policy
25
+ * stay centralized. The child inherits this terminal for a real interactive resume. */
26
+ export declare function resumeSelectorInPlace(selector: string): Promise<void>;
27
+ /** Direct identities use focus as the lifecycle dispatcher: it rechecks the
28
+ * live fleet, attaches a healthy pane, and falls through to `agents resume`
29
+ * only when the process is no longer attachable. */
30
+ export declare function dispatchSessionLifecycleInPlace(selector: string, hosts?: string[]): Promise<void>;
31
+ export declare function buildSessionLifecycleArgs(selector: string, hosts?: string[]): string[];
19
32
  export declare function resolveResumePacking(options: Pick<ResumeOptions, 'splits'>): Packing;
20
- export {};
33
+ /**
34
+ * Decide which backend to launch into. Returns a concrete backend, `'inplace'`
35
+ * (resume in the current process — no GUI/tmux available), or `'cancel'` (the
36
+ * user dismissed the chooser).
37
+ */
38
+ export declare function resolveBackend(options: ResumeOptions, ctx: EngineContext, count: number): Promise<Backend | 'inplace' | 'cancel'>;
@@ -14,18 +14,21 @@ import { discoverSessions } from '../lib/session/discover.js';
14
14
  import { filterTeamSessions } from '../lib/session/team-filter.js';
15
15
  import { multiItemPicker, itemPicker } from '../lib/picker.js';
16
16
  import { buildPreview } from './sessions-picker.js';
17
- import { filterSessionsByQuery, formatPickerLabel, pickerColumnsFor, buildResumeCommand, resumeSessionInPlace, parseAgentFilter, } from './sessions.js';
17
+ import { filterSessionsByQuery, formatPickerLabel, pickerColumnsFor, resumeSessionInPlace, parseAgentFilter, } from './sessions.js';
18
18
  import { openSurfaces, availableBackends, detectCurrentBackend, currentContext, } from '../lib/terminal/index.js';
19
19
  import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
20
20
  import { setHelpSections } from '../lib/help.js';
21
21
  import { confirm } from '@inquirer/prompts';
22
+ import { spawn } from 'node:child_process';
23
+ import { buildCanonicalResumeCommand } from '../lib/session/resume-command.js';
24
+ import { looksLikeSessionId } from '../lib/session/discover.js';
22
25
  /** Opening more than this many live sessions at once asks for confirmation first. */
23
- const CONFIRM_THRESHOLD = 5;
26
+ export const CONFIRM_THRESHOLD = 5;
24
27
  export function registerSessionsResumeCommand(sessionsCmd) {
25
28
  const cmd = sessionsCmd
26
29
  .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).')
30
+ .argument('[query]', 'Session id/tmux alias to reopen directly, or text that filters the picker')
31
+ .description('Reopen one session by canonical identity, or multi-select history into terminal tabs/splits.')
29
32
  .option('-a, --agent <agent>', 'Filter by agent type and version (e.g., claude, codex@0.116.0)')
30
33
  .option('--all', 'Include sessions from every directory (not just current project)')
31
34
  .option('--teams', 'Include team-spawned sessions (hidden by default)')
@@ -46,6 +49,10 @@ export function registerSessionsResumeCommand(sessionsCmd) {
46
49
  # Pre-filter the pool before selecting (space in the filter → use [query])
47
50
  agents sessions resume "auth middleware"
48
51
 
52
+ # Reopen one session from any device by UUID prefix or tmux alias
53
+ agents sessions resume 019fd114
54
+ agents sessions resume ag-codex-c1f3d813
55
+
49
56
  # Force a backend / side-by-side splits / a remote host
50
57
  agents sessions resume --ghostty
51
58
  agents sessions resume --vscodium
@@ -53,7 +60,8 @@ export function registerSessionsResumeCommand(sessionsCmd) {
53
60
  agents sessions resume --host zion --tmux
54
61
  `,
55
62
  notes: `
56
- - space toggles a session, enter confirms; tab toggles the preview pane.
63
+ - A UUID/prefix or ag-<agent>-<suffix> alias bypasses the picker: a live pane is attached; an inactive session resumes on its owning device.
64
+ - With no identity selector, space toggles a session, enter confirms, and tab toggles the preview pane.
57
65
  - Layout: one tab per session by default. --splits packs session pairs side by side in each tab.
58
66
  - Backend: auto-detected from the terminal you're in (iTerm / Ghostty / tmux); override with --iterm/--ghostty/--tmux/--vscodium.
59
67
  - --vscodium opens each session as an agent terminal tab in VSCodium via the swarm-ext extension (works with --host too).
@@ -71,6 +79,10 @@ async function sessionsResumeAction(query, options) {
71
79
  process.exitCode = 1;
72
80
  return;
73
81
  }
82
+ if (query && isDirectResumeSelector(query)) {
83
+ await dispatchSessionLifecycleInPlace(query.trim(), options.host ? [options.host] : []);
84
+ return;
85
+ }
74
86
  const { agent, version } = parseAgentFilter(options.agent);
75
87
  const limit = parseInt(options.limit || '200', 10);
76
88
  const since = options.since ?? (options.all ? undefined : '30d');
@@ -118,11 +130,7 @@ async function sessionsResumeAction(query, options) {
118
130
  // 2. Split the selection into resumable surfaces and skipped agents (no silent drop).
119
131
  const items = [];
120
132
  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
- }
133
+ const command = buildCanonicalResumeCommand(s.id);
126
134
  const cwd = s.cwd && fs.existsSync(s.cwd) ? s.cwd : process.cwd();
127
135
  items.push({ session: s, cwd, command });
128
136
  }
@@ -179,6 +187,37 @@ async function sessionsResumeAction(query, options) {
179
187
  });
180
188
  console.log(chalk.gray(`\nOpened ${opened}/${items.length} in ${where}.`));
181
189
  }
190
+ /** IDs and tmux aliases are actions, not picker search text. Human phrases keep
191
+ * the existing pre-filtered picker, while an explicit identity resumes directly. */
192
+ export function isDirectResumeSelector(query) {
193
+ const selector = query.trim();
194
+ return looksLikeSessionId(selector) || /^ag-[a-z][a-z0-9-]*-[0-9a-f]{8}$/i.test(selector);
195
+ }
196
+ /** Re-enter through the top-level command so fleet routing and harness policy
197
+ * stay centralized. The child inherits this terminal for a real interactive resume. */
198
+ export async function resumeSelectorInPlace(selector) {
199
+ await spawnCliInPlace(['resume', selector]);
200
+ }
201
+ /** Direct identities use focus as the lifecycle dispatcher: it rechecks the
202
+ * live fleet, attaches a healthy pane, and falls through to `agents resume`
203
+ * only when the process is no longer attachable. */
204
+ export async function dispatchSessionLifecycleInPlace(selector, hosts = []) {
205
+ await spawnCliInPlace(buildSessionLifecycleArgs(selector, hosts));
206
+ }
207
+ export function buildSessionLifecycleArgs(selector, hosts = []) {
208
+ return ['sessions', 'focus', selector, ...hosts.flatMap(host => ['--host', host])];
209
+ }
210
+ function asyncExitCode(child) {
211
+ return new Promise((resolve) => {
212
+ child.once('error', () => resolve(127));
213
+ child.once('exit', (code, signal) => resolve(code ?? (signal ? 1 : 0)));
214
+ });
215
+ }
216
+ async function spawnCliInPlace(args) {
217
+ const child = spawn(process.execPath, [process.argv[1], ...args], { stdio: 'inherit' });
218
+ const exitCode = await asyncExitCode(child);
219
+ process.exitCode = exitCode;
220
+ }
182
221
  export function resolveResumePacking(options) {
183
222
  return options.splits ? 'two-per-tab' : 'tabs';
184
223
  }
@@ -187,7 +226,7 @@ export function resolveResumePacking(options) {
187
226
  * (resume in the current process — no GUI/tmux available), or `'cancel'` (the
188
227
  * user dismissed the chooser).
189
228
  */
190
- async function resolveBackend(options, ctx, count) {
229
+ export async function resolveBackend(options, ctx, count) {
191
230
  const forced = options.iterm ? 'iterm'
192
231
  : options.ghostty ? 'ghostty'
193
232
  : options.tmux ? 'tmux'
@@ -66,6 +66,9 @@ interface SessionsOptions extends SessionFilterOptions {
66
66
  local?: boolean;
67
67
  /** --device <target...> — alias for --host; resolves against the device registry. */
68
68
  device?: string[];
69
+ /** --devices <target...> — plural alias for --device; a bare `all`/`fleet` value
70
+ * means "search the whole fleet" (already the default), so it never errors. */
71
+ devices?: string[];
69
72
  /** Query every registered online compute device and merge tool evidence. */
70
73
  fleet?: boolean;
71
74
  /** Aggregate static program sites instead of returning matching call evidence. */
@@ -127,6 +130,17 @@ export declare function formatActiveRowDescription(s: ActiveSession): string;
127
130
  * can't be correlated back to a transcript on disk.
128
131
  */
129
132
  export declare function indexActiveBySessionId(active: ActiveSession[]): Map<string, ActiveSession>;
133
+ /** The SessionMeta fields the live-row backfill reads — the enrichment a running process cannot report. */
134
+ type BackfillMeta = Pick<SessionMeta, 'version' | 'timestamp' | 'label' | 'ticketId' | 'prUrl' | 'prNumber'>;
135
+ /**
136
+ * Backfill display-only fields onto live rows from the indexed SessionMeta, by
137
+ * full session id (RUSH-2205). A running process reports no agent version, and a
138
+ * live orphan row usually carries no ticket/PR/label/start-time; the historical
139
+ * index does. Only a field the live row LACKS is filled — the live signal always
140
+ * wins when present. Pure (no I/O) so the join is unit-tested against fixtures;
141
+ * the DB read that builds `metaById` is the caller's concern.
142
+ */
143
+ export declare function backfillActiveRowsFromMeta(sessions: ActiveSession[], metaById: Map<string, BackfillMeta>): void;
130
144
  /**
131
145
  * The live decoration for a listing row: a status glyph and the latest-turn
132
146
  * preview, when the session is still running. `●` running / `◐` waiting on the
@@ -198,6 +212,22 @@ export declare function serializeActiveSessionsForJson(sessions: ActiveSession[]
198
212
  prLink: string | null;
199
213
  viewingIn: string | null;
200
214
  }>;
215
+ /**
216
+ * Build the (one or two) rendered lines for a single active-session row.
217
+ * Indent is the leading whitespace (2 spaces for flat groups, 4 inside a window
218
+ * sub-group). Sized to `termW` so no line ever wraps under tmux/SSH (RUSH-2205):
219
+ *
220
+ * line 1: id · agent version · status · owner · created X · idle Y · ticket/PR
221
+ * line 2: └ label/topic (+ checklist) · jump locator (ssh/tmux/detached)
222
+ *
223
+ * The label/topic gets its own line so it is no longer buried in a truncated grey
224
+ * snippet, and the actionable ticket/PR badges ride line 1. Version and the
225
+ * ticket/PR/label are backfilled onto the {@link ActiveSession} from the indexed
226
+ * SessionMeta before this renders (a live process reports none of them). Pure +
227
+ * exported so the row layout is unit-tested for content and width without a
228
+ * captured stdout.
229
+ */
230
+ export declare function renderActiveRowLines(s: ActiveSession, indent: string, termW: number): string[];
201
231
  /**
202
232
  * Compact owner display for the `--active` owner column: the local-part of a
203
233
  * resolved actor email/login (`muqsit@getrush.ai` -> `muqsit`), the id as-is
@@ -313,10 +343,17 @@ export declare function remoteHostsToDial(hosts: string[] | undefined, self: str
313
343
  * call it, so the browser can never disagree with `--active --json` about which
314
344
  * sessions are live (it used to call the local-only `getActiveSessions()` directly
315
345
  * and silently hid every remote session).
346
+ *
347
+ * RUSH-2062: default path is cache-first against the daemon-warmed shared
348
+ * snapshot (`session-cache.ts`). Menubar / Factory / watchdog / CLI share one
349
+ * warm result instead of each re-running the full SSH fan-out. `forceRefresh`
350
+ * (or `AGENTS_SESSIONS_FORCE_REFRESH=1`) re-gathers live; scoped `--host` lists
351
+ * always gather live so a filter never returns a wrong unscoped snapshot.
316
352
  */
317
353
  export declare function gatherActiveSessions(opts?: {
318
354
  local?: boolean;
319
355
  hosts?: string[];
356
+ forceRefresh?: boolean;
320
357
  }): Promise<{
321
358
  sessions: ActiveSession[];
322
359
  remoteDeviceCount: number;
@@ -582,6 +619,10 @@ export interface SessionQueryResolution {
582
619
  export declare function resolveSessionQuery(pool: SessionMeta[], query: string, options?: {
583
620
  indexFallback?: boolean;
584
621
  }): SessionQueryResolution;
622
+ /** Honest result of a fleet sweep that found nothing: it says what the sweep
623
+ * actually did — how many peers answered, which were unreachable — instead of
624
+ * dead-ending on "search with --device <host>" after the search already ran. */
625
+ export declare function fleetNotFoundMessage(query: string, deviceCount: number, unreachable: string[]): string[];
585
626
  /** Filter and rank sessions by a multi-term search query across metadata and content. */
586
627
  export declare function filterSessionsByQuery(sessions: SessionMeta[], query: string | undefined): SessionMeta[];
587
628
  /**
@@ -626,11 +667,32 @@ export type MetadataResolveOutcome = {
626
667
  kind: 'partial';
627
668
  failedPeers: string[];
628
669
  };
670
+ /**
671
+ * A match unique enough to resolve on the FIRST peer that returns it and cancel
672
+ * the rest of the fleet sweep: a full session UUID, which is globally unique.
673
+ *
674
+ * Only a full UUID qualifies. A session **label** is deliberately NOT definitive
675
+ * here even on an exact match: labels are free-form and can collide, so a
676
+ * distinct session may carry the same label on a peer that has not answered yet
677
+ * — early-exiting on the first would silently, and nondeterministically, resume
678
+ * the wrong session (whichever peer replied first). Labels therefore stay
679
+ * all-settle so a cross-machine label conflict surfaces as an ambiguity, and a
680
+ * short-id PREFIX stays all-settle for the same reason (RUSH-2203).
681
+ */
682
+ export declare function isDefinitiveMatch(session: SessionMeta, selector: string): boolean;
683
+ /**
684
+ * Whether a selector may enable early-exit on the cancellable fan-out — only a
685
+ * full UUID, which is globally unique so the first hit is the only hit. Labels,
686
+ * keywords, and short-id prefixes stay all-settle: their uniqueness (or
687
+ * conflict) is only knowable once every peer has answered. See
688
+ * {@link isDefinitiveMatch}.
689
+ */
690
+ export declare function selectorAllowsEarlyExit(selector: string): boolean;
629
691
  /** Resolve a fleet sweep through the same canonical full-id / prefix resolver as
630
692
  * local lookups, then group copies by logical session id. Synced mirrors of one
631
693
  * session therefore stay one candidate even when several machines report them;
632
694
  * distinct ids sharing a prefix remain distinct ambiguity candidates. */
633
- export declare function fleetCandidatesByQuery(rows: SessionMeta[], query: string): FleetSessionCandidate[];
695
+ export declare function fleetCandidatesByQuery(rows: SessionMeta[], query: string, trustResolvedRows?: boolean): FleetSessionCandidate[];
634
696
  /** Fixed peer argv for the metadata resolver. Scope flags compose identically on
635
697
  * every host; `--all` removes the SSH login cwd/time window, not agent/project filters. */
636
698
  export declare function metadataResolveForwardedArgs(selector: string, scope: Pick<SessionFilterOptions, 'agent' | 'project'>): string[];
@@ -661,14 +723,28 @@ export declare function resolveSessionMetadataValue(selector: string, scope?: {
661
723
  * (its transcript and agent binary live there — a local `--host` hop would
662
724
  * re-discover locally and dead-end), returning `'rendered'`.
663
725
  * - more than one logical session → print every full-id candidate with its
664
- * machine labels, returning `'conflict'`.
665
- * - none → `'not-found'`, letting the caller print the local
666
- * "no session on this machine" message.
726
+ * machine labels, returning `{ kind: 'conflict' }`.
727
+ * - none → `{ kind: 'not-found', deviceCount, unreachable }`,
728
+ * letting the caller print an honest sweep-aware message.
729
+ *
730
+ * A full-UUID query opts into early-exit: the first peer holding it resolves the
731
+ * sweep and cancels the rest, so a fast peer is not bounded by the slowest one.
732
+ * A short-id prefix stays all-settle — its ambiguity is only known once every
733
+ * peer has answered, so it must wait to surface a conflict.
667
734
  *
668
735
  * No fuzzy/content fallback: the sweep forwards the id selector and every result
669
736
  * is resolved through `resolveSessionQuery`, the same id-only resolver used locally.
670
737
  */
671
- export declare function resolveSessionAcrossFleet(query: string, mode: ViewMode, hosts?: string[], deps?: FleetResolveDeps): Promise<'rendered' | 'conflict' | 'not-found'>;
738
+ export type FleetResolveResult = {
739
+ kind: 'rendered';
740
+ } | {
741
+ kind: 'conflict';
742
+ } | {
743
+ kind: 'not-found';
744
+ deviceCount: number;
745
+ unreachable: string[];
746
+ };
747
+ export declare function resolveSessionAcrossFleet(query: string, mode: ViewMode, hosts?: string[], deps?: FleetResolveDeps): Promise<FleetResolveResult>;
672
748
  /** Register the `agents sessions` command with all its options and help text. */
673
749
  export declare function registerSessionsCommands(program: Command): void;
674
750
  export {};