@phnx-labs/agents-cli 1.20.49 → 1.20.51

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 (100) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +3 -0
  3. package/dist/commands/browser-picker.js +1 -18
  4. package/dist/commands/cloud.js +1 -25
  5. package/dist/commands/computer.d.ts +1 -0
  6. package/dist/commands/computer.js +129 -8
  7. package/dist/commands/doctor.js +133 -2
  8. package/dist/commands/exec.js +49 -6
  9. package/dist/commands/factory.js +1 -4
  10. package/dist/commands/inspect.js +1 -11
  11. package/dist/commands/mcp.js +2 -6
  12. package/dist/commands/message.js +1 -4
  13. package/dist/commands/profiles.js +1 -18
  14. package/dist/commands/repo.js +33 -14
  15. package/dist/commands/resource-view.d.ts +1 -0
  16. package/dist/commands/resource-view.js +5 -17
  17. package/dist/commands/secrets.d.ts +1 -0
  18. package/dist/commands/secrets.js +1 -28
  19. package/dist/commands/sessions-picker.js +1 -18
  20. package/dist/commands/sessions.js +6 -8
  21. package/dist/commands/teams-picker.js +1 -32
  22. package/dist/commands/teams.js +218 -97
  23. package/dist/commands/tmux.js +1 -3
  24. package/dist/commands/view.js +1 -9
  25. package/dist/commands/worktree.js +1 -4
  26. package/dist/lib/agents.d.ts +0 -4
  27. package/dist/lib/agents.js +20 -33
  28. package/dist/lib/auto-dispatch-linear.d.ts +18 -0
  29. package/dist/lib/auto-dispatch-linear.js +107 -0
  30. package/dist/lib/auto-dispatch-provider.d.ts +10 -0
  31. package/dist/lib/auto-dispatch-provider.js +25 -0
  32. package/dist/lib/auto-dispatch.d.ts +87 -0
  33. package/dist/lib/auto-dispatch.js +142 -0
  34. package/dist/lib/browser/cdp.js +11 -2
  35. package/dist/lib/browser/drivers/ssh.d.ts +28 -10
  36. package/dist/lib/browser/drivers/ssh.js +57 -18
  37. package/dist/lib/browser/refs.js +1 -5
  38. package/dist/lib/cli-resources.d.ts +0 -2
  39. package/dist/lib/cli-resources.js +30 -13
  40. package/dist/lib/cloud/rush.d.ts +0 -24
  41. package/dist/lib/cloud/rush.js +0 -31
  42. package/dist/lib/crabbox/cli.js +4 -1
  43. package/dist/lib/crabbox/lease.js +29 -1
  44. package/dist/lib/daemon.js +41 -0
  45. package/dist/lib/exec.js +43 -18
  46. package/dist/lib/format.d.ts +38 -0
  47. package/dist/lib/format.js +108 -0
  48. package/dist/lib/git.d.ts +21 -0
  49. package/dist/lib/git.js +92 -0
  50. package/dist/lib/hooks/cache.d.ts +9 -2
  51. package/dist/lib/hooks/cache.js +220 -8
  52. package/dist/lib/hooks.js +17 -8
  53. package/dist/lib/hosts/passthrough.js +30 -1
  54. package/dist/lib/hosts/progress.d.ts +31 -0
  55. package/dist/lib/hosts/progress.js +35 -0
  56. package/dist/lib/hosts/remote-cmd.d.ts +1 -1
  57. package/dist/lib/hosts/remote-cmd.js +13 -3
  58. package/dist/lib/platform/exec.d.ts +4 -1
  59. package/dist/lib/platform/exec.js +8 -2
  60. package/dist/lib/resources.d.ts +0 -8
  61. package/dist/lib/resources.js +0 -10
  62. package/dist/lib/runner.js +10 -2
  63. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  64. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  65. package/dist/lib/session/active.d.ts +11 -1
  66. package/dist/lib/session/active.js +3 -0
  67. package/dist/lib/session/db.d.ts +1 -4
  68. package/dist/lib/session/db.js +20 -25
  69. package/dist/lib/session/discover.d.ts +2 -2
  70. package/dist/lib/session/discover.js +61 -48
  71. package/dist/lib/session/parse.js +35 -34
  72. package/dist/lib/session/render.d.ts +7 -3
  73. package/dist/lib/session/render.js +15 -9
  74. package/dist/lib/session/state.d.ts +55 -0
  75. package/dist/lib/session/state.js +87 -10
  76. package/dist/lib/session/types.d.ts +9 -0
  77. package/dist/lib/shims.d.ts +25 -2
  78. package/dist/lib/shims.js +73 -8
  79. package/dist/lib/ssh-tunnel.d.ts +33 -2
  80. package/dist/lib/ssh-tunnel.js +94 -7
  81. package/dist/lib/staleness/types.d.ts +0 -1
  82. package/dist/lib/teams/agents.d.ts +108 -1
  83. package/dist/lib/teams/agents.js +511 -11
  84. package/dist/lib/teams/api.d.ts +7 -1
  85. package/dist/lib/teams/api.js +5 -2
  86. package/dist/lib/teams/registry.d.ts +17 -0
  87. package/dist/lib/teams/registry.js +2 -0
  88. package/dist/lib/teams/remoteWorktree.d.ts +57 -0
  89. package/dist/lib/teams/remoteWorktree.js +213 -0
  90. package/dist/lib/teams/scheduler.d.ts +29 -0
  91. package/dist/lib/teams/scheduler.js +78 -0
  92. package/dist/lib/teams/supervisor.js +7 -0
  93. package/dist/lib/types.d.ts +14 -1
  94. package/dist/lib/versions.d.ts +7 -26
  95. package/dist/lib/versions.js +44 -146
  96. package/dist/lib/warn-unpushed.d.ts +40 -0
  97. package/dist/lib/warn-unpushed.js +128 -0
  98. package/package.json +3 -1
  99. package/dist/lib/resources/index.d.ts +0 -53
  100. package/dist/lib/resources/index.js +0 -76
@@ -50,10 +50,41 @@ export declare const REMOTE_TASK_NAME = "AgentsComputerHelper";
50
50
  /** Basename of the cross-published exe under native/computer-win/dist. */
51
51
  export declare const WIN_HELPER_EXE = "computer-helper-win.exe";
52
52
  /**
53
- * Locate the cross-published Windows daemon exe. Only the local build output is
54
- * a candidate — `scripts/build-win.sh` writes it to packages/.../dist/.
53
+ * Locate a locally built Windows daemon exe a repo-checkout build output from
54
+ * `scripts/build-win.sh`, or one bundled next to the package. Local paths are
55
+ * only the first candidate; npm-installed CLIs have neither and fall through to
56
+ * the release-asset download (`ensureWinHelperExe`).
55
57
  */
56
58
  export declare function resolveWinHelperExe(): string | null;
59
+ /** GitHub repo whose `v<version>` releases carry the exe as an asset. */
60
+ export declare const WIN_HELPER_RELEASE_REPO = "phnx-labs/agents-cli";
61
+ /** Cache dir for downloaded helper exes, one subdir per release tag. */
62
+ export declare function winHelperCacheDir(version: string): string;
63
+ /** Release-asset URLs for the exe + its checksum at one exact `v<version>` tag. */
64
+ export declare function winHelperAssetUrls(version: string): {
65
+ exe: string;
66
+ sha256: string;
67
+ };
68
+ /**
69
+ * Parse the published `.sha256` asset — `sha256sum` format (`<hex> <name>`) or
70
+ * a bare hex digest. Throws on anything that does not lead with 64 hex chars.
71
+ */
72
+ export declare function parseSha256Asset(text: string): string;
73
+ /** Stream a file through sha256 — the exe is ~157MB, never read it whole. */
74
+ export declare function sha256File(file: string): Promise<string>;
75
+ /**
76
+ * Download the exe release asset for this CLI version, verify its sha256
77
+ * against the published `.sha256` asset, and cache it under the agents cache
78
+ * dir. Only the exact `v<version>` tag is consulted — a missing asset is a
79
+ * hard error naming that tag, never a silent fallback to another release.
80
+ */
81
+ export declare function downloadWinHelperExe(version: string): Promise<string>;
82
+ /**
83
+ * Resolve the helper exe for `setup --host`: local build outputs first (repo
84
+ * checkout / bundled), then the checksum-verified release-asset download for
85
+ * the running CLI version. Throws with the tag it checked when neither exists.
86
+ */
87
+ export declare function ensureWinHelperExe(version?: string): Promise<string>;
57
88
  /** Persisted per-device tunnel state so verbs can reconnect after `start --host`. */
58
89
  export interface RemoteTunnelState {
59
90
  device: string;
@@ -21,7 +21,9 @@ import * as net from 'net';
21
21
  import * as fs from 'fs';
22
22
  import * as path from 'path';
23
23
  import { fileURLToPath } from 'url';
24
- import { randomBytes } from 'crypto';
24
+ import { randomBytes, createHash } from 'crypto';
25
+ import { Readable } from 'stream';
26
+ import { pipeline } from 'stream/promises';
25
27
  import { sshExec, SSH_OPTS } from './ssh-exec.js';
26
28
  import { backgroundSpawnOptions } from './platform/process.js';
27
29
  import { encodePowerShell } from './browser/drivers/ssh.js';
@@ -29,6 +31,7 @@ import { getDevice } from './devices/registry.js';
29
31
  import { sshTargetFor } from './devices/connect.js';
30
32
  import { hostNameFor } from './devices/ssh-config.js';
31
33
  import { getCacheDir } from './state.js';
34
+ import { getCliVersion } from './version.js';
32
35
  import { openComputerClient, resolveTcpEndpoint } from './computer-rpc.js';
33
36
  /** Build the ssh argv (after the `ssh` program name) for an `-L` tunnel. Pure.
34
37
  *
@@ -89,8 +92,10 @@ export const REMOTE_TASK_NAME = 'AgentsComputerHelper';
89
92
  /** Basename of the cross-published exe under native/computer-win/dist. */
90
93
  export const WIN_HELPER_EXE = 'computer-helper-win.exe';
91
94
  /**
92
- * Locate the cross-published Windows daemon exe. Only the local build output is
93
- * a candidate — `scripts/build-win.sh` writes it to packages/.../dist/.
95
+ * Locate a locally built Windows daemon exe a repo-checkout build output from
96
+ * `scripts/build-win.sh`, or one bundled next to the package. Local paths are
97
+ * only the first candidate; npm-installed CLIs have neither and fall through to
98
+ * the release-asset download (`ensureWinHelperExe`).
94
99
  */
95
100
  export function resolveWinHelperExe() {
96
101
  const here = path.dirname(fileURLToPath(import.meta.url));
@@ -106,6 +111,89 @@ export function resolveWinHelperExe() {
106
111
  }
107
112
  return null;
108
113
  }
114
+ /** GitHub repo whose `v<version>` releases carry the exe as an asset. */
115
+ export const WIN_HELPER_RELEASE_REPO = 'phnx-labs/agents-cli';
116
+ /** Cache dir for downloaded helper exes, one subdir per release tag. */
117
+ export function winHelperCacheDir(version) {
118
+ return path.join(getCacheDir(), 'computer', 'win-helper', `v${version}`);
119
+ }
120
+ /** Release-asset URLs for the exe + its checksum at one exact `v<version>` tag. */
121
+ export function winHelperAssetUrls(version) {
122
+ const base = `https://github.com/${WIN_HELPER_RELEASE_REPO}/releases/download/v${version}`;
123
+ return { exe: `${base}/${WIN_HELPER_EXE}`, sha256: `${base}/${WIN_HELPER_EXE}.sha256` };
124
+ }
125
+ /**
126
+ * Parse the published `.sha256` asset — `sha256sum` format (`<hex> <name>`) or
127
+ * a bare hex digest. Throws on anything that does not lead with 64 hex chars.
128
+ */
129
+ export function parseSha256Asset(text) {
130
+ const m = text.trim().match(/^([A-Fa-f0-9]{64})(\s|$)/);
131
+ if (!m)
132
+ throw new Error(`malformed .sha256 release asset: ${JSON.stringify(text.slice(0, 80))}`);
133
+ return m[1].toLowerCase();
134
+ }
135
+ /** Stream a file through sha256 — the exe is ~157MB, never read it whole. */
136
+ export function sha256File(file) {
137
+ return new Promise((resolve, reject) => {
138
+ const hash = createHash('sha256');
139
+ fs.createReadStream(file)
140
+ .on('error', reject)
141
+ .on('data', (d) => hash.update(d))
142
+ .on('end', () => resolve(hash.digest('hex')));
143
+ });
144
+ }
145
+ /**
146
+ * Download the exe release asset for this CLI version, verify its sha256
147
+ * against the published `.sha256` asset, and cache it under the agents cache
148
+ * dir. Only the exact `v<version>` tag is consulted — a missing asset is a
149
+ * hard error naming that tag, never a silent fallback to another release.
150
+ */
151
+ export async function downloadWinHelperExe(version) {
152
+ const cached = path.join(winHelperCacheDir(version), WIN_HELPER_EXE);
153
+ if (fs.existsSync(cached))
154
+ return cached;
155
+ const tag = `v${version}`;
156
+ const { exe: exeUrl, sha256: shaUrl } = winHelperAssetUrls(version);
157
+ const missing = (status, url) => new Error(`no ${WIN_HELPER_EXE} release asset for tag ${tag} (HTTP ${status} on ${url}). ` +
158
+ `The Windows helper ships as a GitHub release asset per tagged CLI version; ` +
159
+ `from a repo checkout you can build it locally instead: bash scripts/build-win.sh`);
160
+ // Checksum first: it is tiny and 404s fast when the tag has no assets.
161
+ const shaRes = await fetch(shaUrl, { signal: AbortSignal.timeout(30_000) });
162
+ if (!shaRes.ok)
163
+ throw missing(shaRes.status, shaUrl);
164
+ const expected = parseSha256Asset(await shaRes.text());
165
+ console.error(`Downloading ${WIN_HELPER_EXE} ${tag} from GitHub releases (~160 MB)...`);
166
+ const exeRes = await fetch(exeUrl, { signal: AbortSignal.timeout(15 * 60_000) });
167
+ if (!exeRes.ok || !exeRes.body)
168
+ throw missing(exeRes.status, exeUrl);
169
+ fs.mkdirSync(path.dirname(cached), { recursive: true });
170
+ // Stream to a partial file and rename only after the checksum passes, so an
171
+ // interrupted download can never be picked up as a valid cache hit.
172
+ const partial = `${cached}.download`;
173
+ try {
174
+ await pipeline(Readable.fromWeb(exeRes.body), fs.createWriteStream(partial));
175
+ const actual = await sha256File(partial);
176
+ if (actual !== expected) {
177
+ throw new Error(`sha256 mismatch for ${exeUrl}: expected ${expected}, got ${actual}`);
178
+ }
179
+ fs.renameSync(partial, cached);
180
+ }
181
+ finally {
182
+ fs.rmSync(partial, { force: true });
183
+ }
184
+ return cached;
185
+ }
186
+ /**
187
+ * Resolve the helper exe for `setup --host`: local build outputs first (repo
188
+ * checkout / bundled), then the checksum-verified release-asset download for
189
+ * the running CLI version. Throws with the tag it checked when neither exists.
190
+ */
191
+ export async function ensureWinHelperExe(version = getCliVersion()) {
192
+ const local = resolveWinHelperExe();
193
+ if (local)
194
+ return local;
195
+ return downloadWinHelperExe(version);
196
+ }
109
197
  function remoteStateDir() {
110
198
  return path.join(getCacheDir(), 'computer', 'remote');
111
199
  }
@@ -249,10 +337,9 @@ function copyFileOverScp(target, remotePath, filePath, timeoutMs = 600_000) {
249
337
  */
250
338
  export async function setupRemoteHelper(name) {
251
339
  const { target } = await resolveRemoteDevice(name);
252
- const exe = resolveWinHelperExe();
253
- if (!exe) {
254
- throw new Error(`Windows helper exe not built. Run: bash scripts/build-win.sh`);
255
- }
340
+ // Local build output, else the checksum-verified GitHub release asset for
341
+ // this CLI version. Throws naming the tag it checked when neither exists.
342
+ const exe = await ensureWinHelperExe();
256
343
  const prep = sshExec(target, encodePowerShell(buildPushScript()), { timeoutMs: 60_000 });
257
344
  if (prep.code !== 0) {
258
345
  throw new Error(`preparing helper exe path on '${name}' failed (exit ${prep.code ?? 'null'}): ${prep.stderr.trim() || prep.stdout.trim()}`);
@@ -53,4 +53,3 @@ export interface SyncManifest {
53
53
  workflows?: Record<string, DirEntry>;
54
54
  plugins?: Record<string, PluginEntry>;
55
55
  }
56
- export type ResourceType = 'commands' | 'skills' | 'hooks' | 'mcp' | 'rules' | 'subagents' | 'workflows' | 'plugins' | 'permissions';
@@ -92,6 +92,21 @@ export interface SignInAdvisory {
92
92
  * probe). Never flips the authoritative installed/ready column.
93
93
  */
94
94
  export declare function resolveSignInAdvisory(installed: boolean, running: boolean, probeSignedIn: boolean): SignInAdvisory;
95
+ /** One row of `agents teams doctor --json` output. */
96
+ export interface TeamsDoctorEntry {
97
+ installed: boolean;
98
+ path: string | null;
99
+ error: string | null;
100
+ signedIn: boolean | null;
101
+ running: boolean;
102
+ }
103
+ /**
104
+ * Collect the same data `agents teams doctor` prints: per-agent install status,
105
+ * launch health, and advisory sign-in state. Kept in one place so `agents doctor
106
+ * --devices` can run it locally or compare it against remote JSON without
107
+ * duplicating the probe logic.
108
+ */
109
+ export declare function collectTeamsDoctorData(): Promise<Record<string, TeamsDoctorEntry>>;
95
110
  /** Resolve and cache the base directory where teammate process data is stored. */
96
111
  export declare function getAgentsDir(): Promise<string>;
97
112
  /**
@@ -131,6 +146,17 @@ export declare class AgentProcess {
131
146
  cloudBranch: string | null;
132
147
  worktreeName: string | null;
133
148
  worktreePath: string | null;
149
+ hostName: string | null;
150
+ hostTarget: string | null;
151
+ repoPath: string | null;
152
+ remotePid: number | null;
153
+ remoteLog: string | null;
154
+ remoteExit: string | null;
155
+ remoteLogOffset: number;
156
+ remotePollSnapshot: {
157
+ alive: boolean;
158
+ exit: string | null;
159
+ } | null;
134
160
  private eventsCache;
135
161
  private lastReadPos;
136
162
  private baseDir;
@@ -175,7 +201,35 @@ export declare class AgentProcess {
175
201
  * Falls back to null when none are available.
176
202
  */
177
203
  private getLatestEventTime;
204
+ /**
205
+ * For a distributed (remote-host) teammate, pull NEW bytes of the host's log
206
+ * into the LOCAL mirror the parser consumes, advance the remote offset, and
207
+ * resolve terminal status from the remote `.exit` sentinel. Runs BEFORE the
208
+ * local read in readNewEvents(), so the existing stream-json parse path then
209
+ * runs unchanged over the freshly-mirrored bytes.
210
+ *
211
+ * Uses a per-wave batched snapshot (remotePollSnapshot) when the supervisor's
212
+ * one-ssh-per-host pre-pass populated it; otherwise falls back to its own
213
+ * round-trips so a bare `teams status`/`teams logs` is still correct.
214
+ */
215
+ private syncRemoteMirror;
178
216
  readNewEvents(): Promise<void>;
217
+ /**
218
+ * Truncate the local mirror to its trailing REMOTE_MIRROR_MAX_BYTES and reset
219
+ * lastReadPos to the new (smaller) size so the parser doesn't re-read the kept
220
+ * tail. Only trims when over the cap — a normal-length log is untouched.
221
+ */
222
+ private capMirrorToTail;
223
+ /** Cap on the in-memory event backlog kept per remote teammate. */
224
+ private static readonly REMOTE_EVENTS_MAX;
225
+ /**
226
+ * Drop the oldest cached events for a remote teammate once past the cap. The
227
+ * status path only needs recent events (last N messages, recentToolCalls,
228
+ * terminal status) and the getDelta cursor filters by timestamp, so a bounded
229
+ * recent window preserves the digest while bounding the heap. Terminal status
230
+ * is already latched onto `this.status`, so trimming can't lose it.
231
+ */
232
+ private capEventsCache;
179
233
  saveMeta(): Promise<void>;
180
234
  static loadFromDisk(agentId: string, baseDir?: string | null): Promise<AgentProcess | null>;
181
235
  isProcessAlive(): boolean;
@@ -246,12 +300,51 @@ export declare class AgentManager {
246
300
  */
247
301
  rescanFromDisk(): Promise<number>;
248
302
  private loadExistingAgents;
249
- spawn(taskName: string, agentType: AgentType, prompt: string, cwd?: string | null, mode?: Mode | null, effort?: EffortLevel, parentSessionId?: string | null, workspaceDir?: string | null, version?: string | null, name?: string | null, after?: string[], model?: string | null, envOverrides?: Record<string, string> | null, taskType?: TaskType | null, cloudProvider?: string | null, cloudSessionId?: string | null, cloudRepo?: string | null, cloudBranch?: string | null, worktreeName?: string | null, worktreePath?: string | null, profileName?: string | null): Promise<AgentProcess>;
303
+ spawn(taskName: string, agentType: AgentType, prompt: string, cwd?: string | null, mode?: Mode | null, effort?: EffortLevel, parentSessionId?: string | null, workspaceDir?: string | null, version?: string | null, name?: string | null, after?: string[], model?: string | null, envOverrides?: Record<string, string> | null, taskType?: TaskType | null, cloudProvider?: string | null, cloudSessionId?: string | null, cloudRepo?: string | null, cloudBranch?: string | null, worktreeName?: string | null, worktreePath?: string | null, profileName?: string | null, hostName?: string | null, hostTarget?: string | null, repoPath?: string | null): Promise<AgentProcess>;
250
304
  /**
251
305
  * Actually spawn the OS process for a teammate. Extracted from spawn() so
252
306
  * staged teammates can be launched later by startReady().
253
307
  */
254
308
  private launchProcess;
309
+ /**
310
+ * Dispatch a distributed teammate onto its host over SSH — the remote-host
311
+ * analog of launchProcess(). Symmetric to the cloud path: no local process; the
312
+ * lifecycle lives on the host and is polled (isProcessAlive/readNewEvents over
313
+ * SSH via the remote `.exit` sentinel + offset-tailed log).
314
+ *
315
+ * When the team uses worktrees (agent.worktreeName set), a git worktree is first
316
+ * created ON THE HOST off the freshly-fetched default branch; the teammate runs
317
+ * there. Otherwise it runs in the host repo path directly.
318
+ */
319
+ private launchRemoteProcess;
320
+ /**
321
+ * Resolve a scheduler-picked device to host placement fields on an unpinned
322
+ * teammate at LAUNCH time (the same resolution `teams add --device` runs, minus
323
+ * the fatal `die()` — a scheduling failure here is per-teammate, not per-add).
324
+ * Sets hostName/hostTarget/repoPath + persists, so the subsequent
325
+ * launchRemoteProcess dispatches over SSH. Mirrors the `add`-time pin path:
326
+ * resolve device → reject Windows (POSIX-only) → ssh target → ensure the repo
327
+ * is present on the host from the team's --repo (ensureRemoteRepo).
328
+ */
329
+ private resolveScheduledPlacement;
330
+ /**
331
+ * Place an UNPINNED, non-cloud teammate onto the team pool via the cascade
332
+ * (least-loaded), if the team declares one. A no-op for a pinned teammate
333
+ * (hostName already set from `--device`), a cloud teammate, or a poolless team —
334
+ * leaving hostName null so the local spawn runs unchanged. Shared by spawn()
335
+ * (immediate add-launch) and startReady() (staged launch) so an unpinned pool
336
+ * teammate schedules identically no matter how it was fired.
337
+ */
338
+ private maybeSchedulePlacement;
339
+ /**
340
+ * One-ssh-per-host batched liveness/exit pre-pass for a team's remote teammates.
341
+ * The supervisor calls this each wave BEFORE listByTask() so the per-teammate
342
+ * isProcessAlive()/readNewEvents() consume a cached snapshot instead of each
343
+ * issuing its own SSH handshake — avoiding N round-trips per wave at 10+ remote
344
+ * teammates. Groups by hostTarget and, for each host, checks every teammate's
345
+ * `.exit` + `kill -0` in a single ssh call over the shared ControlMaster socket.
346
+ */
347
+ prefetchRemoteStatus(taskName: string): Promise<void>;
255
348
  /**
256
349
  * Fire any pending teammates in the given team whose `after` deps have all
257
350
  * completed. Returns the list of teammates just launched. Repeatable:
@@ -266,6 +359,20 @@ export declare class AgentManager {
266
359
  * exec path (src/lib/exec.ts). The team runner just supplies prompt + mode
267
360
  * and reads stream-json events off stdout.
268
361
  */
362
+ /**
363
+ * Build the `agents run …` argv AFTER the `agents` binary — the flags + prompt
364
+ * scaffolding shared by the LOCAL launch (buildCommand, which prefixes
365
+ * process.execPath + the agents CLI path) and the REMOTE launch
366
+ * (launchRemoteProcess, which prefixes `agents` on the host via dispatch). Kept
367
+ * in one place so the PROMPT_SUFFIX / CLAUDE_PLAN_MODE_PREFIX scaffolding and the
368
+ * flag set can never drift between the two backends.
369
+ *
370
+ * `cwd` is intentionally NOT emitted here: the local path passes it as
371
+ * `--cwd`/`--add-dir` (below), while the remote path `cd`s into the host cwd
372
+ * before invoking `agents`. `sessionId` is likewise local-only (the remote run
373
+ * mints its own session on the host).
374
+ */
375
+ private buildRunArgv;
269
376
  private buildCommand;
270
377
  get(agentId: string): Promise<AgentProcess | null>;
271
378
  /**