@phnx-labs/agents-cli 1.20.57 → 1.20.58

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 (43) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +34 -3
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/defaults.js +24 -0
  5. package/dist/commands/exec.js +28 -4
  6. package/dist/commands/secrets.js +28 -19
  7. package/dist/commands/versions.js +11 -3
  8. package/dist/commands/view.js +19 -4
  9. package/dist/lib/agents.d.ts +21 -0
  10. package/dist/lib/agents.js +28 -4
  11. package/dist/lib/daemon.d.ts +5 -5
  12. package/dist/lib/daemon.js +65 -17
  13. package/dist/lib/git.d.ts +9 -0
  14. package/dist/lib/git.js +12 -0
  15. package/dist/lib/hosts/dispatch.d.ts +21 -0
  16. package/dist/lib/hosts/dispatch.js +88 -5
  17. package/dist/lib/permissions.d.ts +19 -1
  18. package/dist/lib/permissions.js +137 -0
  19. package/dist/lib/project-root.d.ts +65 -0
  20. package/dist/lib/project-root.js +133 -0
  21. package/dist/lib/resources/permissions.js +2 -0
  22. package/dist/lib/resources/types.d.ts +1 -1
  23. package/dist/lib/secrets/agent.d.ts +26 -23
  24. package/dist/lib/secrets/agent.js +196 -216
  25. package/dist/lib/secrets/remote.js +1 -0
  26. package/dist/lib/session/active.d.ts +3 -0
  27. package/dist/lib/session/active.js +1 -0
  28. package/dist/lib/session/parse.js +38 -15
  29. package/dist/lib/session/state.d.ts +4 -1
  30. package/dist/lib/session/state.js +18 -1
  31. package/dist/lib/session/types.d.ts +8 -0
  32. package/dist/lib/staleness/detectors/permissions.js +42 -0
  33. package/dist/lib/staleness/detectors/subagents.js +30 -0
  34. package/dist/lib/staleness/writers/subagents.js +13 -1
  35. package/dist/lib/subagents.d.ts +22 -0
  36. package/dist/lib/subagents.js +146 -0
  37. package/dist/lib/teams/agents.d.ts +14 -0
  38. package/dist/lib/teams/agents.js +158 -22
  39. package/dist/lib/types.d.ts +13 -0
  40. package/dist/lib/versions.d.ts +39 -0
  41. package/dist/lib/versions.js +199 -12
  42. package/package.json +1 -1
  43. package/scripts/postinstall.js +26 -11
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 1.20.58
6
+
7
+ - **Self-updating agent CLIs are represented as one live installation.** `agents view` no longer invents version-home rows for single-binary installers such as Droid, Grok, Cursor, Kiro, Goose, and Hermes; it reports the version returned by the installed binary and folds away stale per-version directories. `agents add <agent>@<version>` now installs or keeps that agent's current release instead of rejecting an unsupported pinned install. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/versions.ts`, `apps/cli/src/commands/{versions,view}.ts`. (RUSH-1321)
8
+ - **Stopped teammate resumes are transactional from launch through persistence.** If a local or remote resume fails, the existing teammate record, directory, runtime metadata, stdout mirror, and log cursor are restored; any replacement wrapper and its descendants are terminated as one process group. A successful resume whose log was truncated restarts parsing at byte zero, and a secondary restore-write failure retains the original launch error as its cause. Source: `apps/cli/src/lib/teams/agents.ts`, `apps/cli/src/lib/hosts/dispatch.ts`. (#1104, #1108)
9
+ - **Wire allowlist support for Cursor CLI.** Cursor agent CLI stores allow/deny in `~/.cursor/cli-config.json` (`permissions.allow`/`deny` with Shell/Read/Write/WebFetch/Mcp). Flip `allowlist: true`, add `convertToCursorFormat` (Bash→Shell), and write via `applyPermissionsToVersion` + detector. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/permissions.ts`. (RUSH-1387)
10
+ - **GitHub Copilot CLI subagents now sync (RUSH-1390).** Installed subagents flatten into GitHub Copilot custom-agent profiles at `~/.copilot/agents/<name>.agent.md` (the Droid custom-droid format), gated to Copilot CLI ≥ 0.0.353. `agents subagents list/view` now surfaces synced Copilot agents and `agents subagents remove` soft-deletes their `.agent.md` files to trash — both previously skipped `copilot` entirely. Source: `apps/cli/src/lib/subagents.ts` (`listSubagentsForAgent`, `removeSubagentFromVersion`, `transformSubagentForCopilot`), `apps/cli/src/lib/staleness/writers/subagents.ts`, `apps/cli/src/lib/staleness/detectors/subagents.ts`, `apps/cli/src/lib/agents.ts`.
11
+ - **Menu-bar Quick Dispatch preserves typed drafts when focus is stolen (RUSH-1592).** If another app activates while the `Cmd-Shift-O` capture panel is open, the panel can hide without destroying the note; the next summon restores the draft text plus selected screenshots, action, and agents. Return submits and clears the draft; Escape clears without dispatching. Source: `apps/cli/menubar/Sources/MenubarHelper/PromptPanel.swift`, `apps/cli/docs/menubar.md`.
12
+ - **Wire subagents support for Kiro CLI.** Kiro custom agents are JSON files under `~/.kiro/agents/*.json` (introduced in kiro-cli v1.23.0). Flip Kiro's `subagents: { since: '1.23.0' }`, add `transformSubagentForKiro`, and wire the subagents writer, detector, install/remove, and orphan-detection paths. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/subagents.ts`, `apps/cli/src/lib/staleness/{writers,detectors}/subagents.ts`. (RUSH-1393)
13
+ - **Menu-bar ticket agents now carry every selected screenshot into the Linear issue (RUSH-1668).** `Cmd-Shift-O` already passed selected file paths to the ticket agent, but the prompt only asked it to inspect them, so agents could create text-only issues and stop. The brief now identifies every selected path as user-provided ticket material, requires each file to be uploaded, supplies the existing `linear update <id> --proof <path>` path as a reliable default, and leaves description/comment/other placement to the agent's judgment. Source: `apps/cli/menubar/Sources/MenubarHelper/{AgentsCLI,IssueSelfTest}.swift`, `apps/cli/docs/menubar.md`.
14
+ - **`agents sessions --active --json` now carries session attachment metadata for Factory previews (RUSH-1524).** Claude and Droid prompt image/document blocks that reference local files are preserved as `{ path, name, mediaType, sizeBytes }`, and the active-session state dedupes them into `attachments` so consumers can render screenshot thumbnails and open the original files instead of only seeing an attachment count. Source: `apps/cli/src/lib/session/parse.ts`, `apps/cli/src/lib/session/state.ts`, `apps/cli/src/lib/session/active.ts`.
15
+ - **Retired the standalone `com.phnx-labs.agents-secrets-agent` launchd service — the always-on daemon is now the sole broker host (#416, step 2).** `ensureAgentRunning()` no longer installs a separate launchd service: it retires any leftover plist via the new `retireLegacySecretsAgentService()` and relies on the daemon (Path 0), with a one-off detached broker as the only fallback. The upgrade migration (`scripts/postinstall.js` → `healLongRunningProcesses`) now `launchctl bootout`s the legacy service **first**, then (re)starts the daemon so it takes over the broker socket, instead of kickstarting the old service onto new code. `agents secrets start` is now a thin alias that brings the daemon up (and waits for the broker to answer); `agents secrets stop` locks all bundles and retires any leftover legacy service while leaving the always-on daemon running; `agents secrets status` reports broker reachability (daemon-hosted vs standalone) rather than "service installed". The stale broker teardown (version-skew self-heal) retires the legacy service instead of kickstarting it. Source: `apps/cli/src/lib/secrets/agent.ts` (`retireLegacySecretsAgentService`, `ensureAgentRunning`, `teardownStaleBroker`, `uninstallSecretsAgentService`; removed `installSecretsAgentService`/`kickstartSecretsAgentService`/`generateServicePlist`), `apps/cli/scripts/postinstall.js` (`healLongRunningProcesses`), `apps/cli/src/commands/secrets.ts` (`start`/`stop`/`status`).
16
+
17
+ - **Clarify the native escape hatch behind `--mode skip`.** The README and bundled `run` skill now discourage `skip`, list its exact direct-exec per-harness flag mappings and ACP `allow_always` behavior, replace an older recommendation of unsafe `full` for ordinary writes, and distinguish Codex `auto` (sandboxed `edit`, which can still prompt) from Codex `skip` (`--dangerously-bypass-approvals-and-sandbox`, equivalent to unsandboxed `--yolo`). Documentation only; runtime behavior is unchanged. Source: `README.md`, `skills/run/SKILL.md`.
18
+ - **Wire allowlist support for Kiro CLI.** Kiro 2.8.0+ permission groups now sync into `~/.kiro/settings/permissions.yaml` as v3 capability rules for shell, filesystem, and web access; existing user-authored rules are preserved and duplicate generated rules are removed. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/permissions.ts`, `apps/cli/src/lib/staleness/detectors/permissions.ts`. (RUSH-1392)
19
+ - **Fix: remote `agents secrets view <bundle>@host --reveal` no longer leaves a 60-second SSH control master behind.** The interactive `-tt` reveal path now opts out of default SSH multiplexing (`multiplex: false`), matching the transport guidance for one-shot commands that must not keep a `ControlPersist` socket open after a Touch ID/passphrase reveal. Source: `apps/cli/src/lib/secrets/remote.ts` (`remoteSecretsRaw`).
20
+ - **`agents run --host <host> --cwd <dir>` now sets the working directory ON the host (and a new `--project` shorthand jumps to a project by name).** Previously `--cwd` was silently dropped for `--host` runs — only the separate `--remote-cwd` flag worked — so `agents run claude --host s1 --cwd ~/src/foo` landed in the remote login-shell's default directory with no warning. `--cwd` is now forwarded as the host working directory, and a home-anchored path (`~/…`, `$HOME/…`, or a local-home absolute the shell already expanded like `/Users/me/…`) is re-rooted at the *remote* `$HOME` so it resolves correctly across machines with different home paths (`/Users/me` → `/home/me`). `--remote-cwd` remains as the explicit override. New `-P, --project <slug>[@worktree]` resolves a bare project name against your projects root (e.g. `~/src/github.com/<user>`) — auto-inferred from the repo you launch inside and cached in `agents.yaml`, or set/shown with `agents defaults project-root [path]`; `--project foo@fix` targets the `fix` git worktree. Works for local and `--host` runs. Verified end-to-end: `agents run claude --host yosemite-s1 --project agents-cli` runs the remote agent with `pwd` = `/home/muqsit/src/github.com/muqsitnawaz/agents-cli`. Source: `apps/cli/src/lib/project-root.ts` (new), `apps/cli/src/lib/hosts/dispatch.ts` (`remoteCdPrefix`), `apps/cli/src/commands/exec.ts` (`--project`/`--cwd` host wiring), `apps/cli/src/commands/defaults.ts` (`project-root`).
21
+ - **Fix daemon crash-looping when its pinned Node version is pruned (fleet-wide).** The routine daemon's launchd/systemd manifest hardcoded `~/.nvm/versions/node/v24.0.0/bin` on PATH, and launched the CLI entry bare when it was an extension-less shim or a `bin/agents → dist/index.js` symlink (an extension check on the link name missed it). The moment that exact nvm patch was upgraded away, the shim's `#!/usr/bin/env node` shebang fell through to an ancient system node (Node 18 → `SyntaxError: node:util has no export 'styleText'` from `@inquirer/core`), and the service crash-looped at import — observed at 100k+ restarts on Linux workers, silently killing all scheduled routines. `getDaemonLaunch` now detects Node-script entries by resolving symlinks and sniffing the shebang (not just the `.js`/`.cjs`/`.mjs` extension), so it pins them to `process.execPath`; and the generated PATH now leads with `path.dirname(process.execPath)` — the Node that installed the service — instead of a hardcoded nvm version, so both the shim and child routine processes always resolve a working runtime. Source: `apps/cli/src/lib/daemon.ts` (`getDaemonLaunch`, `isNodeScriptEntry`, `daemonNodeBinDir`, `generateSystemdUnit`, `generateLaunchdPlist`).
22
+ - **Fix global npm upgrades restarting the routines daemon through `scripts/postinstall.js`.** The postinstall process is itself `process.argv[1]`, so its daemon self-heal could stamp `node scripts/postinstall.js daemon _run` into launchd. Daemon startup now accepts an explicit CLI entry and postinstall passes the resolved signed native binary (or JavaScript entrypoint), with the same value threaded through launchd, systemd, and detached startup. Source: `apps/cli/scripts/postinstall.js`, `apps/cli/src/lib/daemon.ts`.
23
+ - **Fix a standalone secrets service stealing the daemon-hosted broker socket during postinstall.** The standalone and hosted brokers now bind through one race-safe owner arbitration path: an existing reachable broker wins without its socket being unlinked, a persistent losing service stays quiescent instead of triggering launchd restart churn, takes over if the owner stops, and releases its standby PID on service shutdown; only an unreachable stale socket is reclaimed. This covers the release ordering where postinstall restarts the daemon first and then kickstarts an installed standalone service. Source: `apps/cli/src/lib/secrets/agent.ts` (`bindBrokerSocket`, `runSecretsAgent`, `startHostedBroker`).
24
+
5
25
  ## 1.20.57
6
26
 
7
27
  - **`agents teams resume` / `agents teams message` — resume a stopped teammate with a follow-up message.** A teammate that ended its turn with more to do (PR open awaiting review, headless turn cap, a redirect after the fact) could not be reached: `agents message` resolves only *live* sessions, so a completed/stopped/failed teammate had no path back short of finishing the work by hand or spawning a fresh, context-less teammate. `teams resume <team> <teammate> <message>` re-enters the teammate's **own** session with the message as the next user turn, re-launching through the same backend (local process or remote host) in its original worktree and flipping it back to `running` so `teams status` tracks it live. `teams message` is the same command with automatic routing by reconciled status: a **running** teammate is steered via its mailbox (delivered at its next tool call, no re-launch); a **stopped** one is resumed; a **pending** one is refused with a pointer to `teams start`. Works for every harness — the resume delegates to `agents run --resume`, inheriting native resume for Claude/Codex and the universal `/continue` replay for the rest (OpenCode, Grok, Kimi, …); the resume target is the teammate's captured underlying session id (`remoteSessionId ?? agentId`), and a non-Claude teammate that died before emitting a session id is refused with a clear error rather than resumed into a fresh run. This also makes good on `teams stop`'s long-standing "can be restarted later" promise, which no code implemented. Source: `apps/cli/src/commands/teams.ts` (`message`/`resume` subcommands, `decideTeamMessageRoute`), `apps/cli/src/lib/teams/agents.ts` (`AgentManager.resumeTeammate`, resume-aware `buildRunArgv`/`buildCommand`/`launchProcess`/`launchRemoteProcess`).
package/README.md CHANGED
@@ -155,7 +155,37 @@ agents run claude "Review PRs merged this week, summarize risks" \
155
155
  | agents run codex "Write regression tests for the top 3 risks"
156
156
  ```
157
157
 
158
- Supports plan (read-only) and edit modes, effort levels, JSON output for scripting, and timeout limits.
158
+ Supports plan (read-only), edit, auto, and skip modes, effort levels, JSON output for scripting, and timeout limits.
159
+
160
+ ### What does `--mode skip` actually do?
161
+
162
+ Treat `skip` as a last-resort escape hatch. In direct-exec runs (without `--acp`),
163
+ agents-cli forwards the harness's native no-prompt flag; it does not add another
164
+ safety layer. Prefer `auto` where the harness has a smart classifier (Claude Code and
165
+ GitHub Copilot), or `edit` everywhere else. Harnesses without a native bypass flag
166
+ reject direct-exec `skip`.
167
+
168
+ | Harness | Direct-exec `--mode skip` becomes |
169
+ |---|---|
170
+ | Claude Code | `--dangerously-skip-permissions` |
171
+ | Codex | `--dangerously-bypass-approvals-and-sandbox` (equivalent to `--yolo`) |
172
+ | Gemini | `--yolo` |
173
+ | Cursor | `-f` |
174
+ | OpenClaw | `--mode full` |
175
+ | GitHub Copilot | `--allow-all` (alias: `--yolo`) |
176
+ | Antigravity | `--dangerously-skip-permissions` |
177
+ | Grok | `--always-approve` |
178
+ | Kimi | `--yolo` interactively; no extra flag in headless `-p` runs, which already auto-approve |
179
+ | Droid | `--skip-permissions-unsafe` |
180
+
181
+ With `--acp`, these native flags are not used. agents-cli instead grants `skip`
182
+ permission requests at the ACP protocol layer with `allow_always`; the same
183
+ last-resort warning applies.
184
+
185
+ Codex has no native smart-classifier mode, so `agents run codex --mode auto` resolves
186
+ to sandboxed `edit` and can still prompt. `agents run codex --mode skip` is different:
187
+ it bypasses approvals **and** removes the sandbox. `full` remains an alias for `skip`,
188
+ but new scripts should use the explicit `skip` name.
159
189
 
160
190
  ### One protocol, every harness
161
191
 
@@ -432,7 +462,7 @@ tools:
432
462
  ---
433
463
  ```
434
464
 
435
- Workflows that need to write — post PR comments, edit files, send Slack — should run with `--mode edit` or `--mode full`. `agents run` defaults to `--mode plan` (read-only), which deadlocks at `ExitPlanMode` in headless runs.
465
+ Workflows that need to write — post PR comments, edit files, send Slack — should run with `--mode edit`, or `--mode auto` on Claude Code and GitHub Copilot. Reserve `--mode skip` (legacy alias: `full`) for last-resort bypasses. `agents run` defaults to `--mode plan` (read-only), which deadlocks at `ExitPlanMode` in headless runs.
436
466
 
437
467
  Resolution is project > user > system: a `<repo>/.agents/workflows/<name>/` overrides a same-named workflow in `~/.agents/workflows/`. Commit project workflows with your repo so teammates get the same pipeline.
438
468
 
@@ -825,7 +855,7 @@ Which DotAgents resources each agent CLI can load. Source of truth: [src/lib/age
825
855
  | OpenCode | yes | no | yes | no | yes | yes | no | no | `AGENTS.md` | no |
826
856
  | Copilot | yes | no | yes | no | yes | yes | no | no | `AGENTS.md` | no |
827
857
  | Amp | yes | no | yes | no | yes | yes | no | no | `AGENTS.md` | no |
828
- | Kiro | yes | no | yes | no | yes | yes | no | no | `AGENTS.md` | no |
858
+ | Kiro | yes | no | yes | >= 2.8.0 | yes | yes | no | no | `AGENTS.md` | no |
829
859
  | Goose | yes | no | yes | no | no | no | no | no | `AGENTS.md` | no |
830
860
  | Roo Code | yes | no | yes | no | yes | yes | no | no | `AGENTS.md` | no |
831
861
 
@@ -853,6 +883,7 @@ Which DotAgents resources each agent CLI can load. Source of truth: [src/lib/age
853
883
  |------------|-------|------|
854
884
  | Hooks | Codex | >= 0.116.0 |
855
885
  | Hooks | Gemini | >= 0.26.0 |
886
+ | Permissions | Kiro | >= 2.8.0 |
856
887
  | File-based commands | Codex | < 0.117.0 (0.117+ uses command-as-skill) |
857
888
  | Plugins | Codex | >= 0.128.0 |
858
889
 
package/dist/bin/agents CHANGED
Binary file
@@ -6,6 +6,7 @@
6
6
  import chalk from 'chalk';
7
7
  import { setHelpSections } from '../lib/help.js';
8
8
  import { listRunDefaults, setRunDefault, unsetRunDefault, } from '../lib/run-defaults.js';
9
+ import { getProjectRoot, setProjectRoot } from '../lib/project-root.js';
9
10
  function formatRunDefault(entry) {
10
11
  const parts = [];
11
12
  if (entry.defaults.mode)
@@ -86,4 +87,27 @@ export function registerDefaultsCommands(program) {
86
87
  process.exit(1);
87
88
  }
88
89
  });
90
+ defaults
91
+ .command('project-root [path]')
92
+ .description('Show or set the projects root for `agents run --project` (auto-inferred when unset)')
93
+ .action((rootPath) => {
94
+ try {
95
+ if (!rootPath) {
96
+ const current = getProjectRoot();
97
+ if (current) {
98
+ console.log(`Projects root: ${chalk.white(current)}`);
99
+ }
100
+ else {
101
+ console.log(chalk.gray('Projects root not set — auto-inferred and cached on first `--project` use.'));
102
+ }
103
+ return;
104
+ }
105
+ const stored = setProjectRoot(rootPath);
106
+ console.log(chalk.green(`Set projects root: ${stored}`));
107
+ }
108
+ catch (err) {
109
+ console.error(chalk.red(err.message));
110
+ process.exit(1);
111
+ }
112
+ });
89
113
  }
@@ -211,7 +211,8 @@ export function registerRunCommand(program) {
211
211
  .option('--no-auto-secrets', 'Skip auto-injection of secrets declared by a workflow\'s frontmatter `secrets:` field. Has no effect on bare-agent runs.')
212
212
  .option('--secrets-keys <keys>', 'Inject only this comma-separated subset of keys from --secrets bundles (e.g. KEY1,KEY2). Missing keys are an error. Applies to all --secrets bundles on this run.')
213
213
  .option('--allow-expired', 'Inject secrets even if their expiry date has passed (overrides the pre-run expiry abort).')
214
- .option('--cwd <dir>', 'Working directory for the agent (defaults to current directory)')
214
+ .option('--cwd <dir>', 'Working directory for the agent (defaults to current directory). With --host, the directory ON the host.')
215
+ .option('-P, --project <ref>', 'Project shorthand <slug>[@worktree], resolved against your projects root (auto-inferred, cached). Sets the cwd locally or on --host.')
215
216
  .option('--add-dir <dir>', 'Grant access to an additional directory outside the project (Claude and Codex, repeatable)', (val, prev) => [...prev, val], [])
216
217
  .option('--json', 'Stream events as JSON lines (for parsing by other tools)')
217
218
  .option('--quiet', 'Suppress preamble (rotation banner, "Running:" line). Useful when piping JSON events to a parser.', false)
@@ -238,7 +239,7 @@ export function registerRunCommand(program) {
238
239
  .option('--interval <dur>', 'Loop delay between iterations ("0" back-to-back, "30m" paces). Loop only.')
239
240
  .option('--host <name>', 'Offload this run onto another machine over SSH instead of running locally — a device, a registered agent host, or user@host. See `agents devices` / `agents hosts`.')
240
241
  .option('--device <name>', 'Alias of --host: offload this run onto a registered device (from `agents devices`).')
241
- .option('--remote-cwd <dir>', 'Working directory on the host for --host runs.')
242
+ .option('--remote-cwd <dir>', 'Explicit host working directory for --host runs (overrides --cwd; usually --cwd suffices).')
242
243
  .option('--no-follow', 'With --host, dispatch detached and return immediately (track via `agents hosts ps/logs`).')
243
244
  .option('--any', 'With --host <cap> (a capability tag), pick any matching host instead of erroring when several match.')
244
245
  .option('--lease [backend]', 'Invent a disposable cloud box for this run and tear it down after (via crabbox). Optional backend selects the cloud (hetzner/aws/do). Unlike --host, no machine is registered.')
@@ -454,6 +455,24 @@ export function registerRunCommand(program) {
454
455
  // --host/--on/--computer: offload this run onto a registered agent host
455
456
  // over SSH instead of running locally. The three flags are aliases.
456
457
  const hostGiven = [options.host, options.device, options.on, options.computer].filter((v) => !!v);
458
+ // --project <slug>[@worktree]: resolve the projects-root shorthand into a
459
+ // cwd. On a host run it resolves home-relative (`~/…`, so the host expands
460
+ // it); locally it becomes an absolute path. It owns the working directory,
461
+ // so it is mutually exclusive with both --cwd and --remote-cwd.
462
+ if (options.project) {
463
+ if (options.cwd || options.remoteCwd) {
464
+ console.error(chalk.red('Pass --project alone — not with --cwd or --remote-cwd.'));
465
+ process.exit(1);
466
+ }
467
+ const { resolveProjectRef } = await import('../lib/project-root.js');
468
+ try {
469
+ options.cwd = await resolveProjectRef(options.project, { forRemote: hostGiven.length > 0 });
470
+ }
471
+ catch (err) {
472
+ console.error(chalk.red(err.message));
473
+ process.exit(1);
474
+ }
475
+ }
457
476
  if (hostGiven.length > 0) {
458
477
  if (new Set(hostGiven).size > 1) {
459
478
  console.error(chalk.red('Conflicting --host/--device values — pass just one.'));
@@ -488,6 +507,11 @@ export function registerRunCommand(program) {
488
507
  }
489
508
  try {
490
509
  const runAgent = agentSpec.split('@')[0];
510
+ // Working directory on the host: an explicit --remote-cwd is used
511
+ // verbatim; --cwd/--project are made portable (a local-home absolute
512
+ // becomes `~/…` so the remote shell re-roots it at ITS home).
513
+ const { toRemotePortable } = await import('../lib/project-root.js');
514
+ const hostCwd = options.remoteCwd ?? (options.cwd ? toRemotePortable(options.cwd) : undefined);
491
515
  // `--resume [id]`: commander yields the string id, or `true` when the
492
516
  // flag is passed bare. A bare resume needs the interactive picker,
493
517
  // which can't run over a detached remote dispatch — only forward a
@@ -528,7 +552,7 @@ export function registerRunCommand(program) {
528
552
  prompt,
529
553
  mode: options.mode,
530
554
  model: options.model,
531
- remoteCwd: options.remoteCwd,
555
+ remoteCwd: hostCwd,
532
556
  sessionId: hostSessionId,
533
557
  name: options.name,
534
558
  resume: resumeId,
@@ -550,7 +574,7 @@ export function registerRunCommand(program) {
550
574
  prompt,
551
575
  mode: options.mode,
552
576
  model: options.model,
553
- remoteCwd: options.remoteCwd,
577
+ remoteCwd: hostCwd,
554
578
  sessionId: hostSessionId,
555
579
  name: options.name,
556
580
  resume: resumeId,
@@ -12,14 +12,14 @@ import { terminalWidth, truncateToWidth, stringWidth } from '../lib/session/widt
12
12
  import * as fs from 'fs';
13
13
  import { SSH_TARGET_RE, assertValidSshTarget, sshExec } from '../lib/ssh-exec.js';
14
14
  import { quoteWin32ExecArg, composeWin32CommandLine } from '../lib/platform/index.js';
15
- import { ensureDaemonStarted } from '../lib/daemon.js';
15
+ import { ensureDaemonStarted, isDaemonRunning } from '../lib/daemon.js';
16
16
  import { parseHostsOption, remoteResolveEnv, remoteSecretsRaw, remoteSecretsStream, resolveSshTarget, } from '../lib/secrets/remote.js';
17
17
  import { remoteShellFor, buildWindowsStdinImportCommand } from '../lib/hosts/remote-cmd.js';
18
18
  import { resolveRemoteOsSync } from '../lib/hosts/remote-os.js';
19
19
  import { bundleExists, bundleItemStore, bundlePolicy, deleteBundle, describeBundle, keychainItemsForBundle, keychainRef, listBundles, migrateLegacyBundles, parseDotenv, readAndResolveBundleEnv, readBundle, renameBundle, rotateBundleSecret, sanitizeProcessEnv, validateBundleName, validateEnvKey, validateExpiresFutureDated, validateSecretType, writeBundle, } from '../lib/secrets/bundles.js';
20
20
  import { getKeychainToken, getKeychainTokens, hasKeychainToken, secretsKeychainItem, setKeychainToken, } from '../lib/secrets/index.js';
21
21
  import { assertOpAvailable, createPasswordItem, deleteItemByTitle, extractSecrets, itemExistsByTitle, listItems, listVaults, } from '../lib/onepassword.js';
22
- import { DEFAULT_TTL_MS, agentLoad, agentLock, agentStatus, ensureAgentRunning, installSecretsAgentService, runAgentLoadFromStdin, runSecretsAgent, secretsAgentServiceInstalled, uninstallSecretsAgentService, } from '../lib/secrets/agent.js';
22
+ import { DEFAULT_TTL_MS, agentLoad, agentLock, agentPing, agentStatus, ensureAgentRunning, runAgentLoadFromStdin, runSecretsAgent, uninstallSecretsAgentService, } from '../lib/secrets/agent.js';
23
23
  import { parseDuration } from '../lib/hooks/cache.js';
24
24
  import { emit } from '../lib/events.js';
25
25
  import { registerCommandGroups, setHelpSections } from '../lib/help.js';
@@ -1770,13 +1770,13 @@ Examples:
1770
1770
  ttlMs = secs * 1000;
1771
1771
  }
1772
1772
  if (!(await ensureAgentRunning())) {
1773
- console.error(chalk.red('Could not start the secrets-agent.'));
1773
+ console.error(chalk.red('Could not start the secrets broker.'));
1774
1774
  process.exit(1);
1775
1775
  }
1776
1776
  // #415: the daemon should be always-on for any background need, not only
1777
- // after `routines add`. A user who only ever unlocks secrets still gets
1778
- // the daemon installed + running here. `ensureAgentRunning` above only
1779
- // brings up the standalone secrets broker, not the daemon. Idempotent
1777
+ // after `routines add`. `ensureAgentRunning` prefers the daemon (it hosts
1778
+ // the broker, #416), but can fall back to a one-off broker spawn when the
1779
+ // daemon can't come up so ensure the daemon is up regardless. Idempotent
1780
1780
  // (single-instance start lock, #414) and best-effort — never blocks unlock.
1781
1781
  ensureDaemonStarted();
1782
1782
  let loaded = 0;
@@ -1830,13 +1830,14 @@ Examples:
1830
1830
  console.log(chalk.gray('secrets-agent is macOS-only.'));
1831
1831
  return;
1832
1832
  }
1833
- console.log(chalk.gray('service: ') +
1834
- (secretsAgentServiceInstalled()
1835
- ? chalk.green('installed (persistent)')
1836
- : chalk.yellow('not installed run `agents secrets start` for a persistent broker')));
1833
+ const brokerUp = (await agentPing()).reachable;
1834
+ console.log(chalk.gray('broker: ') +
1835
+ (brokerUp
1836
+ ? chalk.green('running') + chalk.gray(isDaemonRunning() ? ' (hosted by the daemon)' : ' (standalone)')
1837
+ : chalk.yellow('not running — starts on demand, or run `agents secrets start` to bring the daemon up now')));
1837
1838
  const entries = await agentStatus();
1838
1839
  if (entries.length === 0) {
1839
- console.log(chalk.gray('No bundles unlocked. The secrets-agent is idle or not running.'));
1840
+ console.log(chalk.gray('No bundles unlocked. The secrets broker is idle or not running.'));
1840
1841
  console.log(chalk.gray('Try: agents secrets unlock <bundle>'));
1841
1842
  return;
1842
1843
  }
@@ -1887,29 +1888,37 @@ Examples:
1887
1888
  });
1888
1889
  cmd
1889
1890
  .command('start')
1890
- .description('Install + start the secrets-agent as a persistent background service (macOS). Survives heavy load; reads connect instantly.')
1891
+ .description('Bring up the always-on daemon that hosts the secrets broker (macOS). Survives heavy load; reads connect instantly.')
1891
1892
  .action(async () => {
1892
1893
  if (process.platform !== 'darwin') {
1893
- console.error(chalk.red('secrets-agent service is macOS-only.'));
1894
+ console.error(chalk.red('The secrets broker is macOS-only.'));
1894
1895
  process.exit(1);
1895
1896
  }
1896
- process.stdout.write(chalk.gray('Installing launchd service…\n'));
1897
- if (await installSecretsAgentService()) {
1898
- console.log(chalk.green('secrets-agent service running.') + chalk.gray(' It stays up across your macOS login session; unlock/auto-cache now connect instantly.'));
1897
+ process.stdout.write(chalk.gray('Starting the daemon…\n'));
1898
+ ensureDaemonStarted();
1899
+ // The daemon hosts the broker socket-first; wait briefly for it to answer.
1900
+ const deadline = Date.now() + 10000;
1901
+ let reachable = (await agentPing()).reachable;
1902
+ while (!reachable && Date.now() < deadline) {
1903
+ await new Promise((r) => setTimeout(r, 200));
1904
+ reachable = (await agentPing()).reachable;
1905
+ }
1906
+ if (reachable) {
1907
+ console.log(chalk.green('secrets broker running.') + chalk.gray(' Hosted by the always-on daemon; unlock/auto-cache now connect instantly.'));
1899
1908
  }
1900
1909
  else {
1901
- console.error(chalk.red('Service installed but did not become reachable in time (machine may be heavily loaded — launchd will keep retrying).'));
1910
+ console.error(chalk.red('Daemon started but the broker did not become reachable in time (machine may be heavily loaded — it will keep retrying).'));
1902
1911
  process.exit(1);
1903
1912
  }
1904
1913
  });
1905
1914
  cmd
1906
1915
  .command('stop')
1907
- .description('Stop + remove the persistent secrets-agent service and wipe what it held.')
1916
+ .description('Lock all bundles and retire any legacy standalone service. The always-on daemon (which hosts the broker) is left running.')
1908
1917
  .action(async () => {
1909
1918
  if (process.platform !== 'darwin')
1910
1919
  return;
1911
1920
  await uninstallSecretsAgentService();
1912
- console.log(chalk.green('secrets-agent service stopped and removed.'));
1921
+ console.log(chalk.green('Locked all bundles.') + chalk.gray(' The broker stays hosted by the always-on daemon; a legacy standalone service, if any, was retired.'));
1913
1922
  });
1914
1923
  cmd
1915
1924
  .command('_agent-run', { hidden: true })
@@ -4,7 +4,7 @@ import ora from 'ora';
4
4
  import * as fs from 'fs';
5
5
  import * as path from 'path';
6
6
  import { select, confirm, checkbox } from '@inquirer/prompts';
7
- import { AGENTS, ALL_AGENT_IDS, getAccountEmail, getAccountInfo, agentLabel, warnAgentDeprecated, } from '../lib/agents.js';
7
+ import { AGENTS, ALL_AGENT_IDS, getAccountEmail, getAccountInfo, agentLabel, warnAgentDeprecated, isSelfUpdatingAgent, } from '../lib/agents.js';
8
8
  import { formatUsageSummary, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
9
9
  import { viewAction } from './view.js';
10
10
  import { readManifest, writeManifest, createDefaultManifest } from '../lib/manifest.js';
@@ -67,7 +67,7 @@ async function setDefaultVersion(agent, installedVersion) {
67
67
  createVersionedAlias(agent, installedVersion);
68
68
  const symlinkResult = await switchConfigSymlink(agent, installedVersion);
69
69
  if (symlinkResult.success) {
70
- console.log(chalk.green(' Set as default'));
70
+ console.log(chalk.green(isSelfUpdatingAgent(agent) ? ' Set as active config profile' : ' Set as default'));
71
71
  if (symlinkResult.backupPath) {
72
72
  console.log(chalk.gray(` Backed up existing config to: ${symlinkResult.backupPath}`));
73
73
  }
@@ -752,7 +752,15 @@ export function registerVersionsCommands(program) {
752
752
  warnIfShimShadowed(agentId);
753
753
  const useEmail = await getAccountEmail(agentId, getVersionHomePath(agentId, finalVersion));
754
754
  const useEmailStr = useEmail ? chalk.cyan(` (${useEmail})`) : '';
755
- console.log(chalk.green(`Set ${agentLabel(agentConfig.id)}@${finalVersion} as global default`) + useEmailStr);
755
+ // Self-updating agents are one binary; `use` only swaps the config
756
+ // symlink (the profile), it does not change which binary runs — so say
757
+ // "profile", not "version", to match what actually happened.
758
+ if (isSelfUpdatingAgent(agentId)) {
759
+ console.log(chalk.green(`Switched ${agentLabel(agentConfig.id)} to config profile ${finalVersion}`) + useEmailStr);
760
+ }
761
+ else {
762
+ console.log(chalk.green(`Set ${agentLabel(agentConfig.id)}@${finalVersion} as global default`) + useEmailStr);
763
+ }
756
764
  }
757
765
  }
758
766
  catch (err) {
@@ -7,7 +7,7 @@ import * as path from 'path';
7
7
  import { AGENTS, ALL_AGENT_IDS, getAllCliStates, getAccountInfo, resolveAgentName, formatAgentError, agentLabel, colorAgent, } from '../lib/agents.js';
8
8
  import { deriveUsageStatusFromSnapshot, formatUsageSection, formatUsageSummary, formatUsageStatusBadge, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
9
9
  import { readManifest } from '../lib/manifest.js';
10
- import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, syncResourcesToVersion, removeVersion, printTrashFooter, reconcileStaleLatestForAgent, } from '../lib/versions.js';
10
+ import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, syncResourcesToVersion, removeVersion, printTrashFooter, reconcileStaleLatestForAgent, isGlobalBinaryAgent, getLiveVersion, } from '../lib/versions.js';
11
11
  import { ensureVersionedAliasCurrent, removeShim, } from '../lib/shims.js';
12
12
  import { getAgentResources } from '../lib/resources.js';
13
13
  import { resolveVersionFilter, AgentSpecError } from '../lib/agent-spec/index.js';
@@ -358,6 +358,19 @@ async function showInstalledVersions(filterAgentId) {
358
358
  profileOnly.push(agentId);
359
359
  }
360
360
  }
361
+ // For self-updating global-binary agents (droid) the on-disk version-dir name
362
+ // is a stale label — the real version is whatever `<cli> --version` reports.
363
+ // Resolve it once so every row/width pass shows the live version, while the
364
+ // per-version home + account lookups keep using the real dir name.
365
+ const liveVersionByAgent = new Map();
366
+ await Promise.all(versionManaged
367
+ .filter((agentId) => isGlobalBinaryAgent(agentId))
368
+ .map(async (agentId) => {
369
+ const live = await getLiveVersion(agentId);
370
+ if (live)
371
+ liveVersionByAgent.set(agentId, live);
372
+ }));
373
+ const displayVersion = (agentId, dirVersion) => liveVersionByAgent.get(agentId) ?? dirVersion;
361
374
  // Show version-managed agents
362
375
  if (versionManaged.length > 0) {
363
376
  // Calculate column widths across all agents for alignment
@@ -370,7 +383,8 @@ async function showInstalledVersions(filterAgentId) {
370
383
  const versions = listInstalledVersions(agentId);
371
384
  const globalDefault = getGlobalDefault(agentId);
372
385
  for (const v of versions) {
373
- const label = v === globalDefault ? `${v} (default)` : v;
386
+ const shown = displayVersion(agentId, v);
387
+ const label = v === globalDefault ? `${shown} (default)` : shown;
374
388
  maxVerLabel = Math.max(maxVerLabel, label.length);
375
389
  const rawInfo = infoMap.get(`${agentId}:${v}`);
376
390
  const info = rawInfo ? mergeCanonical(rawInfo) : undefined;
@@ -423,9 +437,10 @@ async function showInstalledVersions(filterAgentId) {
423
437
  });
424
438
  for (const version of sortedVersions) {
425
439
  const isDefault = version === globalDefault;
426
- const base = isDefault ? `${version} (default)` : version;
440
+ const shown = displayVersion(agentId, version);
441
+ const base = isDefault ? `${shown} (default)` : shown;
427
442
  const padded = base.padEnd(maxVerLabel);
428
- const label = isDefault ? `${version}${chalk.green(' (default)')}${' '.repeat(maxVerLabel - base.length)}` : padded;
443
+ const label = isDefault ? `${shown}${chalk.green(' (default)')}${' '.repeat(maxVerLabel - base.length)}` : padded;
429
444
  const rawInfo = infoMap.get(`${agentId}:${version}`);
430
445
  const vInfo = rawInfo ? mergeCanonical(rawInfo) : undefined;
431
446
  const usageKey = getUsageLookupKey(vInfo);
@@ -34,6 +34,27 @@ export declare function findInPath(command: string): string | null;
34
34
  export declare const AGENTS: Record<AgentId, AgentConfig>;
35
35
  /** All registered agent IDs derived from the AGENTS registry. */
36
36
  export declare const ALL_AGENT_IDS: AgentId[];
37
+ /**
38
+ * A self-updating agent is a single global binary installed by an official
39
+ * `curl … | sh` / `brew install` script that carries NO version token — the
40
+ * installer can only ever fetch the *current* release, and the binary then keeps
41
+ * itself up to date in place (droid, grok, antigravity, cursor, hermes, forge,
42
+ * kiro, goose). There is no semver to pin, so agents-cli must not model these as
43
+ * having multiple installable version-homes the way it does for npm-packaged
44
+ * agents (claude, codex, kimi, …).
45
+ *
46
+ * The predicate is `!npmPackage && installScript && !installScript.includes('VERSION')`:
47
+ * - `npmPackage` empty → not installed from npm, so `agents add x@1.2.3`
48
+ * can't resolve a registry version.
49
+ * - `installScript` present → it IS installed by a script (not unmanaged).
50
+ * - no `VERSION` placeholder → the script has no slot for a pinned version
51
+ * (contrast: an installer templated with `VERSION`
52
+ * could pin, and is NOT self-updating).
53
+ *
54
+ * Route every "is this a pinnable, multi-version agent?" decision through here —
55
+ * never a scattered `agent === 'droid'`.
56
+ */
57
+ export declare function isSelfUpdatingAgent(agent: AgentId): boolean;
37
58
  /** Get the chalk color function for an agent. Works for any AgentId or SessionAgentId. */
38
59
  export declare function colorAgent(agentId: string): (s: string) => string;
39
60
  /** Return the agent's display name, colored. */
@@ -290,7 +290,7 @@ export const AGENTS = {
290
290
  format: 'markdown',
291
291
  variableSyntax: '$ARGUMENTS',
292
292
  supportsHooks: true,
293
- capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: true, subagents: false, rules: { file: '.cursorrules' }, workflows: false, memory: false, modes: ['edit', 'skip'] },
293
+ capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: true, subagents: false, rules: { file: '.cursorrules' }, workflows: false, memory: false, modes: ['edit', 'skip'] }, // allowlist: ~/.cursor/cli-config.json
294
294
  },
295
295
  opencode: {
296
296
  id: 'opencode',
@@ -310,7 +310,7 @@ export const AGENTS = {
310
310
  format: 'markdown',
311
311
  variableSyntax: '$ARGUMENTS',
312
312
  supportsHooks: false,
313
- capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '1.1.1' }, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit'] }, // subagents: ~/.config/opencode/agents/*.md
313
+ capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '1.1.1' }, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit'] },
314
314
  },
315
315
  openclaw: {
316
316
  id: 'openclaw',
@@ -353,7 +353,7 @@ export const AGENTS = {
353
353
  format: 'markdown',
354
354
  variableSyntax: '$ARGUMENTS',
355
355
  supportsHooks: true,
356
- capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'auto', 'skip'] },
356
+ capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: true, subagents: { since: '0.0.353' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'auto', 'skip'] },
357
357
  },
358
358
  amp: {
359
359
  id: 'amp',
@@ -393,7 +393,7 @@ export const AGENTS = {
393
393
  format: 'markdown',
394
394
  variableSyntax: '$ARGUMENTS',
395
395
  supportsHooks: true,
396
- capabilities: { hooks: { since: '0.10.0' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['edit'] },
396
+ capabilities: { hooks: { since: '0.10.0' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '2.8.0' }, skills: true, commands: true, plugins: false, subagents: { since: '1.23.0' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['edit'] },
397
397
  },
398
398
  goose: {
399
399
  id: 'goose',
@@ -661,6 +661,30 @@ export const AGENTS = {
661
661
  };
662
662
  /** All registered agent IDs derived from the AGENTS registry. */
663
663
  export const ALL_AGENT_IDS = Object.keys(AGENTS);
664
+ /**
665
+ * A self-updating agent is a single global binary installed by an official
666
+ * `curl … | sh` / `brew install` script that carries NO version token — the
667
+ * installer can only ever fetch the *current* release, and the binary then keeps
668
+ * itself up to date in place (droid, grok, antigravity, cursor, hermes, forge,
669
+ * kiro, goose). There is no semver to pin, so agents-cli must not model these as
670
+ * having multiple installable version-homes the way it does for npm-packaged
671
+ * agents (claude, codex, kimi, …).
672
+ *
673
+ * The predicate is `!npmPackage && installScript && !installScript.includes('VERSION')`:
674
+ * - `npmPackage` empty → not installed from npm, so `agents add x@1.2.3`
675
+ * can't resolve a registry version.
676
+ * - `installScript` present → it IS installed by a script (not unmanaged).
677
+ * - no `VERSION` placeholder → the script has no slot for a pinned version
678
+ * (contrast: an installer templated with `VERSION`
679
+ * could pin, and is NOT self-updating).
680
+ *
681
+ * Route every "is this a pinnable, multi-version agent?" decision through here —
682
+ * never a scattered `agent === 'droid'`.
683
+ */
684
+ export function isSelfUpdatingAgent(agent) {
685
+ const cfg = AGENTS[agent];
686
+ return !cfg.npmPackage && !!cfg.installScript && !cfg.installScript.includes('VERSION');
687
+ }
664
688
  // Capability-filtered agent lists used to live here as `*_CAPABLE_AGENTS`
665
689
  // constants. They were a frequent source of silent-skip bugs (e.g. grok
666
690
  // rules sync gated on `COMMANDS_CAPABLE_AGENTS`). Use `capableAgents(cap)`
@@ -82,12 +82,12 @@ export declare function readDaemonClaudeOAuthToken(opts?: {
82
82
  */
83
83
  export declare function writeOwnerOnlyServiceManifest(filePath: string, content: string): void;
84
84
  /** Generate a macOS launchd plist for auto-starting the daemon. */
85
- export declare function generateLaunchdPlist(oauthToken?: string | null): string;
85
+ export declare function generateLaunchdPlist(oauthToken?: string | null, agentsBin?: string): string;
86
86
  /** Generate a Linux systemd user unit for auto-starting the daemon. */
87
- export declare function generateSystemdUnit(oauthToken?: string | null): string;
87
+ export declare function generateSystemdUnit(oauthToken?: string | null, agentsBin?: string): string;
88
88
  export declare function getAgentsBinPath(argv1?: string | undefined, execPath?: string): string;
89
89
  /** Start the daemon via launchd, systemd, or as a detached process. */
90
- export declare function startDaemon(): {
90
+ export declare function startDaemon(agentsBin?: string): {
91
91
  pid: number | null;
92
92
  method: string;
93
93
  };
@@ -129,8 +129,8 @@ export declare function buildDetachedDaemonEnv(baseEnv?: NodeJS.ProcessEnv, oaut
129
129
  * Going through `process.execPath` means a real PE/binary is spawned with
130
130
  * `detached: true` and no console, so nothing signals the daemon after launch.
131
131
  *
132
- * When the entry isn't a JS file (e.g. a native launcher resolved via
133
- * `which agents`), run it directly — it owns its own runtime resolution.
132
+ * When the entry isn't a Node script (e.g. a native compiled launcher), run it
133
+ * directly — it owns its own runtime resolution.
134
134
  */
135
135
  export declare function getDaemonLaunch(agentsBin?: string): {
136
136
  command: string;