@phnx-labs/agents-cli 1.22.24 → 1.22.25

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 (85) hide show
  1. package/CHANGELOG.md +167 -1
  2. package/README.md +14 -4
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/cloud.js +9 -5
  5. package/dist/commands/doctor.d.ts +24 -0
  6. package/dist/commands/doctor.js +100 -9
  7. package/dist/commands/exec.js +19 -16
  8. package/dist/commands/feed.d.ts +5 -0
  9. package/dist/commands/feed.js +21 -1
  10. package/dist/commands/focus.js +2 -2
  11. package/dist/commands/menubar.js +8 -0
  12. package/dist/commands/routines.d.ts +3 -0
  13. package/dist/commands/routines.js +70 -47
  14. package/dist/commands/run-cloud.js +1 -1
  15. package/dist/commands/sessions-browser.d.ts +1 -1
  16. package/dist/commands/sessions-browser.js +27 -7
  17. package/dist/commands/sessions-resume.js +3 -2
  18. package/dist/commands/sessions.d.ts +13 -1
  19. package/dist/commands/sessions.js +24 -2
  20. package/dist/commands/setup-watchdog.js +5 -10
  21. package/dist/commands/setup.js +1 -1
  22. package/dist/commands/teams.js +3 -2
  23. package/dist/commands/watchdog.d.ts +3 -4
  24. package/dist/commands/watchdog.js +26 -66
  25. package/dist/index.js +36 -1
  26. package/dist/lib/agents.js +126 -21
  27. package/dist/lib/cloud/cursor.d.ts +79 -0
  28. package/dist/lib/cloud/cursor.js +228 -0
  29. package/dist/lib/cloud/registry.js +2 -0
  30. package/dist/lib/cloud/types.d.ts +7 -2
  31. package/dist/lib/cloud/types.js +14 -0
  32. package/dist/lib/crabbox/cli.d.ts +2 -2
  33. package/dist/lib/crabbox/config.d.ts +7 -8
  34. package/dist/lib/crabbox/config.js +14 -14
  35. package/dist/lib/crabbox/lease.d.ts +11 -4
  36. package/dist/lib/crabbox/lease.js +40 -8
  37. package/dist/lib/crabbox/setup-copy.d.ts +5 -0
  38. package/dist/lib/crabbox/setup-copy.js +17 -1
  39. package/dist/lib/daemon.js +27 -1
  40. package/dist/lib/device-config.js +7 -0
  41. package/dist/lib/devices/doctor-findings.d.ts +7 -1
  42. package/dist/lib/devices/doctor-findings.js +40 -1
  43. package/dist/lib/events.d.ts +9 -0
  44. package/dist/lib/events.js +58 -0
  45. package/dist/lib/exec.d.ts +3 -3
  46. package/dist/lib/exec.js +24 -10
  47. package/dist/lib/feed-outcome.d.ts +3 -0
  48. package/dist/lib/feed-outcome.js +18 -10
  49. package/dist/lib/feed.d.ts +4 -0
  50. package/dist/lib/hosts/passthrough.d.ts +21 -0
  51. package/dist/lib/hosts/passthrough.js +39 -12
  52. package/dist/lib/mcp.js +5 -1
  53. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  54. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  55. package/dist/lib/menubar/snapshot.d.ts +15 -0
  56. package/dist/lib/menubar/snapshot.js +40 -0
  57. package/dist/lib/plugins.js +13 -1
  58. package/dist/lib/resources/mcp.js +3 -0
  59. package/dist/lib/routine-process-cleanup.d.ts +9 -0
  60. package/dist/lib/routine-process-cleanup.js +73 -0
  61. package/dist/lib/runner.js +5 -5
  62. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  63. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  64. package/dist/lib/secrets/bundles.js +1 -20
  65. package/dist/lib/secrets/filestore.d.ts +2 -0
  66. package/dist/lib/secrets/filestore.js +13 -0
  67. package/dist/lib/secrets/rc-hygiene.d.ts +14 -0
  68. package/dist/lib/secrets/rc-hygiene.js +14 -1
  69. package/dist/lib/session/active.d.ts +4 -0
  70. package/dist/lib/session/db.js +8 -2
  71. package/dist/lib/session/remote-list.d.ts +2 -0
  72. package/dist/lib/session/remote-list.js +1 -0
  73. package/dist/lib/session/session-cache.d.ts +4 -4
  74. package/dist/lib/session/session-cache.js +4 -4
  75. package/dist/lib/shims.js +21 -1
  76. package/dist/lib/signin-badge.js +2 -0
  77. package/dist/lib/startup/command-registry.d.ts +15 -0
  78. package/dist/lib/startup/command-registry.js +42 -0
  79. package/dist/lib/teams/agents.js +1 -1
  80. package/dist/lib/teams/parsers.d.ts +1 -1
  81. package/dist/lib/types.d.ts +1 -1
  82. package/dist/lib/versions.js +16 -1
  83. package/dist/lib/watchdog/service.d.ts +17 -0
  84. package/dist/lib/watchdog/service.js +39 -0
  85. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,7 +1,173 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.22.25
4
+
5
+ ---
6
+ issue: 2108
7
+ type: fixed
8
+ ---
9
+
10
+ `agents sessions focus ... --active` now excludes closed and crashed registry rows; explicit lifecycle filters such as `--closed` and `--crashed` still select them. Per-device `latest` / `oldest` selectors also wait for the peer's filtered index result instead of widening to live rows whose version is unknown.
11
+
12
+ - **`agents doctor` now reports the file-store master key when it is live in the
13
+ process environment, not just when it is exported from a shell rc file
14
+ (RUSH-1968).** `rc-hygiene.ts` scans FILES, which leaves a real hole: a value
15
+ inherited by a long-lived process outlives the rc line that set it, so an operator
16
+ who deletes `~/.zshenv:8` gets a clean `rc-secret-export` while every shell, editor
17
+ and agent started before the edit still carries the key and hands it to everything
18
+ they spawn. Confirmed on `yosemite-s1`, which has zero rc exports and still had the
19
+ value in its environment. The new `env-secret-export` warning names that state and
20
+ says the deletion is not sufficient; like every other finding it reports only that
21
+ the variable is set, never its value. Expected inside a release sign context
22
+ (`headless-sign-context.sh` sets it deliberately), which the message says so an
23
+ operator on the home base does not chase it. Source:
24
+ `apps/cli/src/lib/secrets/rc-hygiene.ts` (`masterPassphraseInEnv`),
25
+ `apps/cli/src/lib/devices/doctor-findings.ts`, `apps/cli/src/commands/doctor.ts`.
26
+
27
+ - **`agents doctor --devices` now reports a remote box's secret hygiene, and its
28
+ inventory probe no longer times out on a slow box (RUSH-1968).** The fan-out ran
29
+ each remote's own `doctor --json` but parsed only `.fleet`, discarding the
30
+ `findings` the remote had already computed — so a leaking box read as clean from
31
+ anywhere but itself. The remote's `rc-secret-export` / `env-secret-export` rows
32
+ now ride back, but the remote contributes exactly one thing: the **kind**. Severity,
33
+ message and remediation are generated locally, and `device` is overwritten with the
34
+ name that was dialled. Each of those matters separately — a remediation is a command
35
+ a human copies and runs, a message is the one place a secret value could re-enter a
36
+ readout that otherwise never prints one, a severity decides the CRITICAL section, and
37
+ a device name decides which box gets blamed. The cost is detail: the fleet row names
38
+ the box and the kind, and says to run `agents doctor` there for the file and line.
39
+ Only those two kinds forward — sign-in and divergence rows are recomputed centrally
40
+ and would otherwise double.
41
+
42
+ Separately, the probe's 30s timeout was below the real cost of the command it
43
+ runs — `doctor --json` measures 57s on `yosemite-m0` and 136s on an idle box — so
44
+ every slow device silently contributed nothing at all: no inventory, no sign-in,
45
+ no divergence. Raised to 180s, matching `ChildProcess.doctorTimeout`, which was set
46
+ to 180 for this same command for this same reason. Source:
47
+ `apps/cli/src/commands/doctor.ts` (`asRemoteSecretFindings`, `probeFleetInventory`).
48
+
49
+ - **Feed and menu-bar session counts now use the `agents sessions --active` lifecycle model (RUSH-1993).**
50
+ Feed outcome headers count distinct mailbox agents in every state, so repeated
51
+ open blocks from one agent cannot make `needs you` exceed `agents`. Feed rows
52
+ replace generic `terminal` with a known host app and label routine-spawned
53
+ sessions as `routine:<name>`. The menu-bar snapshot backfills the same routine
54
+ metadata as active-session JSON and renders the full canonical status set
55
+ (`working`, `waiting`, `idle`, `queued`, `orphan`, `crashed`, `closed`,
56
+ `abandoned`, `unknown`) without reclassifying warm rows from attention files.
57
+ Source: `apps/cli/src/commands/feed.ts`, `apps/cli/src/commands/sessions.ts`,
58
+ `apps/cli/src/lib/feed-outcome.ts`, `apps/cli/src/lib/menubar/snapshot.ts`,
59
+ `apps/cli/menubar/Sources/MenubarHelper/{LocalState,StatusItemController}.swift`.
60
+
61
+ - **A typo'd command with `--host` said the opposite of the truth (RUSH-2022).** The
62
+ `--host`/`--device` router runs before commander parses, so `agents session resume
63
+ --host <box>` (one letter off `sessions`, which *does* accept `--host`) reported
64
+ `` `agents session` does not support --host/--device `` — a true statement about a
65
+ command nobody typed. Unknown names now fall through to `unknown command '<name>'`
66
+ with a did-you-mean, and the spellcheck can suggest the lazily-registered groups
67
+ (`sessions`/`teams`/`cloud`/…) it previously could not see. A real command with no
68
+ remote semantics still gets the flag-support error. Source:
69
+ `apps/cli/src/lib/hosts/passthrough.ts`, `apps/cli/src/lib/startup/command-registry.ts`.
70
+
71
+ - **`agents publish` is in the lazy command table.** `commands/packages.ts` registers it
72
+ at top level but the registry did not list it, so it only resolved through the
73
+ unknown-command fallback that loads the whole command tree. Found by the new test
74
+ that pins the command-name set against the real tree.
75
+
76
+ - **A mistyped command keeps its `--host`.** The distance-1 auto-correct now re-checks
77
+ routing after correcting the name, so `agents docto --host <box>` corrects to
78
+ `doctor` **and actually routes to `<box>`** — previously the corrected command
79
+ re-parsed locally with a `--host` it silently dropped (a regression caught in review:
80
+ it used to fail loudly with the wrong message, corrected-but-unrouted made it fail
81
+ silently instead). `apps/cli/src/index.ts`.
82
+
83
+ - **Cursor now supports `--mode plan` headless and interactive (RUSH-2101).** The
84
+ capability registry previously listed only `edit` and `skip`, so `agents run cursor --mode plan`
85
+ and routine jobs silently degraded to writable `edit`. `cursor-agent` has supported `--plan`
86
+ since the 2026-01-16 CLI release; the registry, `AGENT_COMMANDS` flag mapping, and the routine
87
+ runner now forward it correctly. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/exec.ts`,
88
+ `apps/cli/src/lib/runner.ts`.
89
+
90
+ - **`agents sessions resume` picker now filters in-memory on every keystroke instead of re-querying the database (RUSH-2212).** The `filter` callback in the multi-select picker was calling `filterSessionsByQuery`, which runs a full-text search scan against `sessions.db` on every character typed. It now uses the same cheap in-memory substring match (`sessionMatchesQuery`) the session browser already applies, so the picker stays responsive regardless of library size. Source: `apps/cli/src/commands/sessions-resume.ts`.
91
+
92
+ ---
93
+ type: minor
94
+ ---
95
+
96
+ Add Cursor Cloud Agents as a native cloud provider so `agents run cursor --cloud` and `agents cloud run --agent cursor` dispatch through Cursor's v1 REST API, with status, streaming, cancellation, and follow-up runs.
97
+
98
+ - **A starved `coreauthd` can no longer hang `secrets list` forever (RUSH-2233).**
99
+ The keychain helper's `list` runs two passes, and the data-protection pass omits
100
+ `kSecUseAuthenticationUI: …Fail` on purpose (that flag drops every biometry-ACL
101
+ item even when authentication is healthy). Omitting it means the query still
102
+ reaches LocalAuthentication/`coreauthd`, which has no deadline of its own — a
103
+ wedged `coreauthd` left `SecItemCopyMatching` blocked for the life of the
104
+ process. That is *why* helpers accumulated for the bounded-spawn + reaper work
105
+ in RUSH-2231/2232 to clean up. The pass now runs on a background thread behind
106
+ a 3-second wait: on timeout the helper logs one line to stderr, skips the pass,
107
+ and prints whatever the file-keychain pass produced — the same handling the
108
+ screen-locked `errSecInteractionNotAllowed` case already got. Set
109
+ `AGENTS_KEYCHAIN_LIST_TIMEOUT_MS` to override the deadline;
110
+ `AGENTS_KEYCHAIN_BOUNDED_TEST=1` runs the helper's headless self-test for the
111
+ bounded wait. Source: `apps/cli/src/lib/secrets/keychain-helper.swift`.
112
+
113
+ - **Use one three-minute AGI Menu snapshot and one watchdog executor (RUSH-2260).**
114
+ The menu bar now reads routines, 40 indexed recent sessions, the daemon-warmed
115
+ active-session cache, and persisted watchdog state in one subprocess every three
116
+ minutes; `doctor --json` remains independently limited to 15 minutes. The menu no
117
+ longer executes watchdog ticks. The daemon is the sole automatic watchdog executor,
118
+ warms active sessions on the same three-minute cadence, is gated by device-local
119
+ `watchdog.enabled`, and cleans failed/timeout routine process groups that are still
120
+ alive before they can overlap a replacement run. Source:
121
+ `apps/cli/src/lib/menubar/snapshot.ts`, `apps/cli/src/lib/daemon.ts`,
122
+ `apps/cli/src/lib/routine-process-cleanup.ts`.
123
+
124
+ - **Add Warp Agent CLI (Oz) as a harness (RUSH-2261).** `warp` is now a
125
+ first-class harness: `agents add warp`, `agents run warp`, `agents teams add
126
+ <team> warp`, resource sync, and `agents view` all work. The CLI command is
127
+ `oz` (the shared Warp binary); install is self-updating via
128
+ `brew install --cask oz` (macOS) / `oz-stable` apt|yum|pacman (Linux), config
129
+ lives under `~/.warp/`, the rules/context file is `AGENTS.md`, and auth is
130
+ `oz login` (browser OAuth) or a `WARP_API_KEY` token for headless/CI.
131
+ Capabilities are truthful and gated by `supports()`: MCP (Claude
132
+ `.mcp.json` schema at `~/.warp/.mcp.json`, stdio + http with headers) and
133
+ skills are on; hooks, allowlist, commands, plugins, subagents, workflows, and
134
+ memory are off (Oz exposes no matching install surface). Sessions are not
135
+ indexed — Oz stores conversations server-side (retrieved with auth via
136
+ `oz run conversation get <id>`), so there is no local transcript for
137
+ `agents sessions`. No usage bar — Oz exposes no usage/limits endpoint.
138
+ Source: `apps/cli/src/lib/{agents,exec,mcp}.ts`.
139
+
140
+ `scripts/release.sh` now runs orchestration in a fresh release-owned worktree, so a dirty shared checkout or feature branch no longer blocks or contaminates a release.
141
+
142
+ - **Plugin discovery warns instead of silently skipping a directory with no
143
+ `.claude-plugin/plugin.json` (RUSH-2270).** A `plugins/<name>/` directory missing
144
+ (or with a malformed) manifest was invisible to `agents plugins list`/`info`/`sync`,
145
+ `agents doctor`, and the marketplace materialize-into-version-homes step, with zero
146
+ diagnostic anywhere in the chain — no `agents sync`/`agents repo pull` could
147
+ surface it, since discovery dropped it before any of those ran. Found via a real
148
+ case: the `work` plugin merged to `phnx-labs/.agents-system` without its manifest
149
+ and sat invisible for a full merge cycle; the same fix run against this box's own
150
+ `~/.agents/plugins/duck` found it missing one too. `discoverPluginsInDir` now
151
+ writes one `agents-cli:` warning to stderr naming the directory and the missing
152
+ manifest path, matching the existing advisory-not-fatal pattern
153
+ `syncMarketplaceManifest` already uses for a malformed (as opposed to missing)
154
+ manifest. Source: `apps/cli/src/lib/plugins.ts`.
155
+
156
+ - **Muse login is recognized under `providers.meta` (balanced no longer signed_out).**
157
+ Live `muse login` writes `~/.config/muse/auth.json` as
158
+ `{ schema_version, providers: { meta: { access_token, user_email, … } } }`.
159
+ The presence detector only walked one object level, so it never saw the token
160
+ under `providers.meta` and reported signed-out after a successful OAuth —
161
+ `agents run muse` under balanced then failed with "excluded: 0.1.0
162
+ (signed_out)". Detection now recurses into nested provider slots and surfaces
163
+ `user_email` when present. Source: `apps/cli/src/lib/agents.ts`.
164
+
165
+ - **File-backed secret resolution now fails on decryption errors instead of treating unreadable ciphertext as an absent value (RUSH-2264).** A wrong `AGENTS_SECRETS_PASSPHRASE` or tampered encrypted value now reports the affected file-store item and stops before launching a consumer with an empty secret such as `HCLOUD_TOKEN`. Source: `apps/cli/src/lib/secrets/filestore.ts`, `apps/cli/src/lib/secrets/bundles.ts`.
166
+
3
167
  ## 1.22.24
4
168
 
169
+ - **`agents run --lease` now shares one warm pool across repositories by default (RUSH-2225).** Repo sandbox/CI `profile:` labels no longer split lease reuse into one idle box per repo; a dedicated lease pool is explicit with `.crabbox.yaml` `leaseProfile:`. An empty pool keeps its newly warmed box for later callers. Concurrent runs attach with crabbox `--reclaim` and launch with separate working trees, agent homes, and credential files, so callers share compute without clobbering run state. Switching repos re-syncs the checkout, trading cache latency for lower idle-compute cost. Source: `apps/cli/src/lib/crabbox/config.ts`, `apps/cli/src/lib/crabbox/lease.ts`, `apps/cli/src/commands/exec.ts`.
170
+
5
171
  - **`agents sessions focus` recovers dead panes and shares the sessions browser's selectors (GH-2108).** A retained tmux `remain-on-exit` pane is probed through `#{pane_dead}` immediately before attach, so dead or missing panes no longer open a `Pane is dead` screen. `focus` accepts session ids, topic/path searches, `agent@version` selectors (including per-device `latest`/`oldest`), device, project/time, team/routine, skill/plugin, favorites, and the complete live-state union. Focus, resume, attach, and `run --resume` now use one recovery decision on the origin device: a healthy exact origin performs native resume; otherwise balanced selection chooses a healthy version of the same harness and sends `/continue <id>` to read the indexed transcript, including transcripts retained under version trash. Host-dispatched rows persist the dispatch host as their origin, and `attach` routes its detach-record cleanup there before resuming. No usable same-harness version fails with the device, origin version, and account-health reason. Source: `apps/cli/src/commands/focus.ts`, `apps/cli/src/commands/sessions-browser.ts`, `apps/cli/src/lib/session/recovery.ts`.
6
172
 
7
173
  - **`agents secrets setup` no longer tells you to set `AGENTS_SECRETS_PASSPHRASE`, and
@@ -108,7 +274,7 @@
108
274
 
109
275
  - **Fix: the keychain-helper reaper never reaped on macOS.** The daemon reaper shelled `ps -o etimes` — a GNU/Linux procps keyword that macOS `ps` rejects with a non-zero exit — so `execFileSync` threw on every tick and `reapOrphanedKeychainProcesses` returned `reaped: 0` on its only supported platform, leaving orphaned/wedged `Agents CLI` helper processes to pile up. Switched to the portable BSD `etime` keyword (`[[dd-]hh:]mm:ss`) with a parser to seconds. This also un-quarantines the darwin integration test that #2153 had to `it.skip` to unblock releases: that test's symlink fixture was correct all along — it read `reaped: 0` only because the reaper's own `ps etimes` call threw before parsing anything. Verified on macOS 15.4.1: 20/20 reaper tests pass, the integration test reaping a real orphaned sleeper. Source: `apps/cli/src/lib/secrets/reaper.ts`, `reaper.test.ts`.
110
276
 
111
- - **`agents modes [agent[@version]]` lists the permission modes a harness accepts.** The modes analog of `agents models`: for Claude / Codex / Cursor / …, shows which `--mode plan|edit|auto|skip` values work, the native CLI flags, the native default (`*`), any configured `run.defaults` mode, and degrade notes (e.g. plan→edit on Cursor). `agents inspect` also prints the mode list next to capabilities, and `agents run --help` / `agents models` point at the new command. Source: `apps/cli/src/commands/modes.ts`, `apps/cli/src/lib/agent-modes.ts`.
277
+ - **`agents modes [agent[@version]]` lists the permission modes a harness accepts.** The modes analog of `agents models`: for Claude / Codex / Cursor / …, shows which `--mode plan|edit|auto|skip` values work, the native CLI flags, the native default (`*`), any configured `run.defaults` mode, and degrade notes (e.g. plan→edit on Antigravity). `agents inspect` also prints the mode list next to capabilities, and `agents run --help` / `agents models` point at the new command. Source: `apps/cli/src/commands/modes.ts`, `apps/cli/src/lib/agent-modes.ts`.
112
278
 
113
279
  - **`agents insights --all` now works as an alias for `--since all`.** It previously
114
280
  exited with `unknown option '--all'` — a hard failure in the middle of a report the
package/README.md CHANGED
@@ -35,6 +35,8 @@
35
35
  <a href="https://dev.meta.ai/docs/muse-code" title="Meta Muse Code"><strong>Muse</strong></a>
36
36
  &nbsp;&nbsp;&nbsp;&nbsp;
37
37
  <a href="https://omp.sh" title="Oh My Pi"><strong>Pi</strong></a>
38
+ &nbsp;&nbsp;&nbsp;&nbsp;
39
+ <a href="https://docs.warp.dev/reference/cli" title="Warp Agent CLI (Oz)"><strong>Warp</strong></a>
38
40
  </p>
39
41
 
40
42
  https://agents-cli.sh/demo.mp4
@@ -147,6 +149,13 @@ agents run antigravity "Write tests for the fixed code"
147
149
 
148
150
  Each resolves to the project-pinned version with skills, MCP servers, and permissions already synced. Single-typo names auto-correct across every command — `agents view cladue` resolves to `claude`, `agents add codx@latest` to `codex`.
149
151
 
152
+ `agents run claude "task" --lease` reuses one shared warm crabbox pool across
153
+ repositories by default. Concurrent runs share the box but execute in isolated
154
+ `~/workspaces/<repo>-<run>` directories with separate agent homes and credential
155
+ files. Add `leaseProfile: private-hot-box` to
156
+ `.crabbox.yaml` only when a repo intentionally needs a dedicated warm pool;
157
+ cross-repo reuse trades re-sync latency for lower idle-compute cost.
158
+
150
159
  ### Rate-limited? Keep working.
151
160
 
152
161
  ```bash
@@ -405,7 +414,7 @@ agents watchdog --nudge # actually inject "Continue." into the stalled split
405
414
  agents watchdog --watch # daemon loop: a tick every --interval
406
415
  ```
407
416
 
408
- `agents watchdog` detects a stalled session, resolves the *exact* terminal split it lives in (tmux, iTerm, VSCodium, or a raw pty), and injects a nudge -- `Continue.` by default, or set `--text`. It's dry by default; `--nudge` acts on a single tick, and `agents watchdog on|off` enables or disables the built-in routine on this device. `agents setup watchdog` chooses devices. Steer a single run with `agents watchdog policy <id> off | keep | handsoff`.
417
+ `agents watchdog` detects a stalled session, resolves the *exact* terminal split it lives in (tmux, iTerm, VSCodium, or a raw pty), and injects a nudge -- `Continue.` by default, or set `--text`. It's dry by default; `--nudge` acts on a single tick. `agents watchdog on|off` controls the device-local daemon pass, which runs once every three minutes. Steer a single run with `agents watchdog policy <id> off | keep | handsoff`.
409
418
 
410
419
  A stalled session whose tail shows a hard account limit ("You've hit your weekly limit · resets …") is **rotated in place** instead of nudged: the watchdog gates on the same healthy-account selection `agents run auto` makes (zero healthy → one skip event per cooldown window, terminal untouched), injects the harness's exit sequence, relaunches `agents run auto --interactive --session-id <uuid>` in the *same* tab, then replays the old session's resume once the new TUI is live. Default on; `agents watchdog rotate off` disables it (nudging stays on).
411
420
 
@@ -609,10 +618,10 @@ Team state is observable via `agents teams list --json` / `agents teams status -
609
618
 
610
619
  ## Cloud
611
620
 
612
- Some work shouldn't tie up your laptop. `agents cloud run` hands a task to a managed provider that clones the repo, plans, implements, tests, and opens a PR -- while your terminal stays free. A fifth provider, `host`, dispatches the same way onto machines you own: `agents cloud run "…" --host gpu-box` (tasks track in `agents cloud ps` and `agents hosts ps` alike).
621
+ Some work shouldn't tie up your laptop. `agents cloud run` hands a task to a managed provider that clones the repo, plans, implements, tests, and opens a PR -- while your terminal stays free. The `host` provider dispatches the same way onto machines you own: `agents cloud run "…" --host gpu-box` (tasks track in `agents cloud ps` and `agents hosts ps` alike).
613
622
 
614
623
  <p align="center">
615
- <img src="assets/cloud.svg" alt="agents cloud run dispatches one prompt to a managed provider (Rush, Codex, Factory, or Antigravity) that clones, plans, tests, and opens a pull request while you keep working" width="100%" />
624
+ <img src="assets/cloud.svg" alt="agents cloud run dispatches one prompt to a managed provider (Rush, Codex, Cursor, Factory, or Antigravity) that runs while you keep working" width="100%" />
616
625
  </p>
617
626
 
618
627
  ```bash
@@ -626,7 +635,7 @@ agents cloud message <id> "also update the changelog" # steer it mid-run
626
635
  agents cloud cancel <id>
627
636
  ```
628
637
 
629
- Four managed backends behind one interface (`agents cloud providers`):
638
+ Five managed backends behind one interface (`agents cloud providers`):
630
639
 
631
640
  | Provider | What runs | Notes |
632
641
  |---|---|---|
@@ -634,6 +643,7 @@ Four managed backends behind one interface (`agents cloud providers`):
634
643
  | `codex` | A pre-built Codex Cloud environment | Target it with `--env`. |
635
644
  | `factory` | `droid exec` on a cloud VM | Computer-use; pick the box with `--computer`. |
636
645
  | `antigravity` | Gemini managed agents | Antigravity harness in a remote sandbox. |
646
+ | `cursor` | Cursor Cloud Agents | v1 REST API with repo, status, SSE, cancel, and follow-up runs. |
637
647
 
638
648
  Auto-routes each `--agent` to its native cloud, or pin the backend with `--provider`. Instead of dispatching now, register a run as an **event trigger** with `--on pull_request` (also `push`, `issue_comment`, `workflow_run`) -- it persists as a trigger-bound routine that fires on the event. `--json` on every subcommand for scripting.
639
649
 
package/dist/bin/agents CHANGED
Binary file
@@ -25,14 +25,15 @@ export function statusColor(status) {
25
25
  export function registerCloudCommands(program) {
26
26
  const cloud = program
27
27
  .command('cloud', { hidden: true })
28
- .description('Dispatch and manage cloud agent tasks across providers (Rush, Codex, Factory, Antigravity).')
28
+ .description('Dispatch and manage cloud agent tasks across providers (Rush, Codex, Cursor, Factory, Antigravity, Host).')
29
29
  .addHelpText('after', `
30
30
  Each agent runs in its own cloud. Pass --agent and the provider is auto-selected
31
- (claude→rush, codex→codex, droid→factory, antigravity→antigravity); --provider overrides.
31
+ (claude→rush, codex→codex, cursor→cursor, droid→factory, antigravity→antigravity); --provider overrides.
32
32
 
33
33
  Providers:
34
34
  rush Rush Cloud — Claude against a GitHub repo + branch → PR
35
35
  codex Codex Cloud — runs in a pre-built Codex environment (--env)
36
+ cursor Cursor Cloud Agents — v1 REST API, with optional GitHub repos
36
37
  factory Factory Droid Computer — droid exec on a cloud VM (--computer)
37
38
  antigravity Gemini Managed Agents — Antigravity harness in a remote sandbox
38
39
 
@@ -49,6 +50,9 @@ Examples:
49
50
  # Codex Cloud against a saved environment
50
51
  agents cloud run "add pytest fixtures for the new billing module" --provider codex --env env_a1b2c3 --agent codex --timeout 30m
51
52
 
53
+ # Cursor Cloud Agents against a GitHub repository
54
+ agents cloud run "fix the flaky parser test" --agent cursor --repo acme/example
55
+
52
56
  # Factory pod targeting a specific computer (Droid)
53
57
  agents cloud run "QA the new onboarding flow end-to-end" --provider factory --computer linux-vm-1 --agent droid
54
58
 
@@ -74,9 +78,9 @@ Examples:
74
78
  cloud
75
79
  .command('run [prompt]')
76
80
  .description('Dispatch a task to a cloud agent.')
77
- .option('--provider <id>', 'Cloud backend: rush, codex, factory, antigravity, host (overrides agent auto-routing)')
78
- .option('--agent <name>', 'Agent to run: claude, codex, droid, antigravity (auto-routes to its native cloud)')
79
- .option('--repo <owner/repo>', 'GitHub repository. Repeatable for multi-repo dispatch (Rush Cloud only).', (value, previous) => {
81
+ .option('--provider <id>', 'Cloud backend: rush, codex, cursor, factory, antigravity, host (overrides agent auto-routing)')
82
+ .option('--agent <name>', 'Agent to run: claude, codex, cursor, droid, antigravity (auto-routes to its native cloud)')
83
+ .option('--repo <owner/repo>', 'GitHub repository. Repeatable for multi-repo dispatch (Rush or Cursor Cloud).', (value, previous) => {
80
84
  const acc = Array.isArray(previous) ? previous : [];
81
85
  acc.push(value);
82
86
  return acc;
@@ -22,11 +22,35 @@
22
22
  * apply pending sync.
23
23
  */
24
24
  import type { Command } from 'commander';
25
+ import { type DoctorFinding } from '../lib/devices/doctor-findings.js';
25
26
  import { type VersionResourceReport } from '../lib/doctor-diff.js';
26
27
  import { type DuplicateVersionHook } from '../lib/hooks.js';
27
28
  import { type SyncStatusRow, type OrphanRow } from '../lib/drift.js';
28
29
  import { type FetchStatusMarker } from '../lib/auto-pull.js';
29
30
  export declare function wrapLine(prefix: string, text: string, width?: number): string[];
31
+ export declare function asRemoteSecretFindings(raw: unknown, device: string): DoctorFinding[];
32
+ /**
33
+ * Fetch a device's harness inventory by running its top-level `agents doctor
34
+ * --json` (which carries the `fleet` field, RUSH-2027). Separate from
35
+ * {@link probeFleetTarget} — that forwards `teams doctor --json` for per-agent
36
+ * readiness, a different payload. Returns the parsed {@link FleetInventory} or
37
+ * null (unreachable, non-zero exit, unparseable, or an older CLI with no
38
+ * `fleet` field); a null is skipped by the comparator, never a false gap.
39
+ */
40
+ /**
41
+ * How long one remote `doctor --json` probe may take.
42
+ *
43
+ * 180s, matching `ChildProcess.doctorTimeout`, which was set to 180 for this
44
+ * same command for this same reason. The previous 30s sat BELOW the command's
45
+ * real cost — 56s measured on `yosemite-m0`, 136s on an idle box — so every slow
46
+ * device silently contributed nothing at all: no inventory, no sign-in, no
47
+ * divergence, no secret findings. A ceiling under the true cost does not make a
48
+ * probe cheap, it makes it always fail.
49
+ *
50
+ * This does not multiply by device count: `fanOutDevices` is `Promise.all`, so
51
+ * whole-fleet wall time is bounded near the slowest box, not 180s x N.
52
+ */
53
+ export declare const FLEET_INVENTORY_TIMEOUT_MS = 180000;
30
54
  export type IssueSeverity = 'critical' | 'warning' | 'info';
31
55
  /**
32
56
  * One triaged health finding. `severity`/`category`/`subject`/`impact`/`fix` are
@@ -8,7 +8,7 @@ import { enterDoctorOverviewGate, writeDoctorOverviewCache } from '../lib/device
8
8
  import { fleetDialTarget } from '../lib/devices/connect.js';
9
9
  import { compareFleetInventories } from '../lib/devices/fleet-divergence.js';
10
10
  import { collectLocalFleetInventory } from '../lib/devices/fleet-inventory.js';
11
- import { buildLocalFindings, fleetDivergenceToFindings, signInToFindings, renderFindings, } from '../lib/devices/doctor-findings.js';
11
+ import { buildLocalFindings, fleetDivergenceToFindings, signInToFindings, renderFindings, remediationFor, FINDING_SEVERITY, } from '../lib/devices/doctor-findings.js';
12
12
  import { getCliVersion } from '../lib/version.js';
13
13
  import { resolveHost } from '../lib/hosts/registry.js';
14
14
  import { sshExecAsync } from '../lib/ssh-exec.js';
@@ -31,7 +31,7 @@ import { listCliStatus, listCliStatusAsync } from '../lib/cli-resources.js';
31
31
  import { setHelpSections } from '../lib/help.js';
32
32
  import { heal, healChangedAnything } from '../lib/heal.js';
33
33
  import { getEffectiveExecutionPolicy } from '../lib/platform/winpath.js';
34
- import { scanUserRcFiles } from '../lib/secrets/rc-hygiene.js';
34
+ import { scanUserRcFiles, masterPassphraseInEnv } from '../lib/secrets/rc-hygiene.js';
35
35
  import { terminalWidth, truncateToWidth, stringWidth, padToWidth } from '../lib/session/width.js';
36
36
  import { readRepoBehindMarkers } from '../lib/auto-pull.js';
37
37
  import * as fs from 'fs';
@@ -80,6 +80,70 @@ function printWrappedLine(prefix, text) {
80
80
  for (const line of wrapLine(prefix, text))
81
81
  console.log(chalk.gray(line));
82
82
  }
83
+ /**
84
+ * Narrow a remote `findings` array to the secret-hygiene rows, or `[]`.
85
+ *
86
+ * Deliberately NOT "forward every remote finding". The aggregator already
87
+ * rebuilds a remote's sign-in rows from its inventory and its divergence rows
88
+ * from the comparator, so forwarding wholesale would double them; and pulling a
89
+ * remote's orphan/drift rows into a fleet readout is a much larger UX change
90
+ * than this fix. The two secret kinds are the ones that are BOTH unrecomputable
91
+ * centrally and security-relevant, which is exactly why they were being lost.
92
+ *
93
+ * **The remote contributes exactly one thing: the KIND.** Severity, message and
94
+ * remediation are all generated HERE. That is not defensiveness for its own
95
+ * sake — the remote runs its own agents-cli, whose version and integrity we do
96
+ * not control, and each forwarded string is load-bearing in a different way:
97
+ *
98
+ * - `remediation` is a command a human copies and runs. Accepting it from the
99
+ * wire is an injection channel, full stop.
100
+ * - `message` is the one place a secret VALUE could re-enter a readout that
101
+ * otherwise guarantees it never prints one. A local builder cannot leak what
102
+ * it never receives.
103
+ * - `severity` decides the CRITICAL section, so a remote could otherwise
104
+ * promote its own warning and bury real criticals under it.
105
+ * - `device` is overwritten with the name we dialled, so no box can pin a
106
+ * finding on another.
107
+ *
108
+ * The cost is detail: the fleet row says which box and which kind, not which
109
+ * file and line. Run `agents doctor` on that box for the specifics — the
110
+ * message says so.
111
+ */
112
+ const REMOTE_FORWARDED_KINDS = ['rc-secret-export', 'env-secret-export'];
113
+ /** Canonical, locally-authored text for a forwarded kind. Never the remote's. */
114
+ const REMOTE_SECRET_MESSAGE = {
115
+ 'rc-secret-export': 'a credential-shaped export was found in this box\'s shell rc files'
116
+ + ' — run `agents doctor` there for the file and line',
117
+ 'env-secret-export': 'AGENTS_SECRETS_PASSPHRASE is set in this box\'s process environment'
118
+ + ' — run `agents doctor` there for detail',
119
+ };
120
+ export function asRemoteSecretFindings(raw, device) {
121
+ if (!Array.isArray(raw))
122
+ return [];
123
+ const seen = new Set();
124
+ const out = [];
125
+ for (const item of raw) {
126
+ if (!item || typeof item !== 'object' || Array.isArray(item))
127
+ continue;
128
+ const kind = item.kind;
129
+ if (typeof kind !== 'string')
130
+ continue;
131
+ if (!REMOTE_FORWARDED_KINDS.includes(kind))
132
+ continue;
133
+ if (seen.has(kind))
134
+ continue; // one row per kind per box
135
+ seen.add(kind);
136
+ const k = kind;
137
+ const base = {
138
+ severity: FINDING_SEVERITY[k],
139
+ kind: k,
140
+ device,
141
+ message: REMOTE_SECRET_MESSAGE[k],
142
+ };
143
+ out.push({ ...base, remediation: remediationFor({ ...base, remediation: '' }) });
144
+ }
145
+ return out;
146
+ }
83
147
  async function resolveFleetTargets(opts) {
84
148
  const singleName = opts.host || opts.device;
85
149
  if (singleName) {
@@ -155,15 +219,32 @@ async function probeFleetTarget(target) {
155
219
  * null (unreachable, non-zero exit, unparseable, or an older CLI with no
156
220
  * `fleet` field); a null is skipped by the comparator, never a false gap.
157
221
  */
222
+ /**
223
+ * How long one remote `doctor --json` probe may take.
224
+ *
225
+ * 180s, matching `ChildProcess.doctorTimeout`, which was set to 180 for this
226
+ * same command for this same reason. The previous 30s sat BELOW the command's
227
+ * real cost — 56s measured on `yosemite-m0`, 136s on an idle box — so every slow
228
+ * device silently contributed nothing at all: no inventory, no sign-in, no
229
+ * divergence, no secret findings. A ceiling under the true cost does not make a
230
+ * probe cheap, it makes it always fail.
231
+ *
232
+ * This does not multiply by device count: `fanOutDevices` is `Promise.all`, so
233
+ * whole-fleet wall time is bounded near the slowest box, not 180s x N.
234
+ */
235
+ export const FLEET_INVENTORY_TIMEOUT_MS = 180_000;
158
236
  async function probeFleetInventory(target) {
159
237
  const isWin = /^win/i.test((target.os ?? '').trim());
160
238
  const remoteCmd = buildRemoteAgentsInvocation(['doctor', '--json'], undefined, isWin ? 'windows' : undefined, isWin ? undefined : { PATH: '$HOME/.agents/.cache/shims:$HOME/.local/bin:$PATH' });
161
- const res = await sshExecAsync(target.sshTarget, remoteCmd, { timeoutMs: 30000, multiplex: true });
239
+ const res = await sshExecAsync(target.sshTarget, remoteCmd, { timeoutMs: FLEET_INVENTORY_TIMEOUT_MS, multiplex: true });
162
240
  if (res.code !== 0)
163
241
  return null;
164
242
  try {
165
243
  const parsed = JSON.parse(res.stdout);
166
- return asFleetInventory(parsed.fleet);
244
+ return {
245
+ inventory: asFleetInventory(parsed.fleet),
246
+ secretFindings: asRemoteSecretFindings(parsed.findings, target.name),
247
+ };
167
248
  }
168
249
  catch {
169
250
  return null;
@@ -252,19 +333,22 @@ async function runDevicesDoctor(opts) {
252
333
  fanOutDevices(targets, probeFleetTarget),
253
334
  fanOutDevices(targets, probeFleetInventory),
254
335
  ]);
255
- const inventoryByName = new Map();
336
+ const payloadByName = new Map();
256
337
  for (const r of inventoryResults)
257
- inventoryByName.set(r.name, r.status === 'ok' ? (r.value ?? null) : null);
338
+ payloadByName.set(r.name, r.status === 'ok' ? (r.value ?? null) : null);
258
339
  results.push(...remoteResults.map((r) => {
259
- const inventory = inventoryByName.get(r.name) ?? undefined;
340
+ const payload = payloadByName.get(r.name) ?? null;
341
+ const inventory = payload?.inventory ?? undefined;
342
+ const secretFindings = payload?.secretFindings;
260
343
  if (r.status === 'ok' && r.value)
261
- return { ...r.value, inventory: inventory ?? undefined };
344
+ return { ...r.value, inventory, secretFindings };
262
345
  return {
263
346
  name: r.name,
264
347
  online: false,
265
348
  error: r.error ?? String(r.reason ?? 'skipped'),
266
349
  agents: {},
267
- inventory: inventory ?? undefined,
350
+ inventory,
351
+ secretFindings,
268
352
  };
269
353
  }));
270
354
  // Cross-device divergence: compare every device's inventory against the local
@@ -314,6 +398,7 @@ async function runDevicesDoctor(opts) {
314
398
  duplicateHooks: inspectDuplicateVersionHooks(cwd),
315
399
  hostClis: toHostCliInput(listCliStatus(cwd)),
316
400
  rcSecrets: scanUserRcFiles(),
401
+ masterPassphraseInEnv: masterPassphraseInEnv(),
317
402
  execPolicy: process.platform === 'win32'
318
403
  ? { platform: process.platform, policy: getEffectiveExecutionPolicy() }
319
404
  : undefined,
@@ -335,6 +420,11 @@ async function runDevicesDoctor(opts) {
335
420
  });
336
421
  continue;
337
422
  }
423
+ // Secret hygiene the aggregator cannot see for itself — the remote's shell
424
+ // rc files and its process environment. Without this the fleet readout
425
+ // reports a leaking box as clean (RUSH-1968).
426
+ if (r.secretFindings?.length)
427
+ findings.push(...r.secretFindings);
338
428
  if (r.inventory?.signIn) {
339
429
  findings.push(...signInToFindings(r.name, r.inventory.signIn));
340
430
  accounts[r.name] = r.inventory.signIn;
@@ -1389,6 +1479,7 @@ export function registerDoctorCommand(program) {
1389
1479
  duplicateHooks,
1390
1480
  hostClis: toHostCliInput(hostClis),
1391
1481
  rcSecrets: scanUserRcFiles(),
1482
+ masterPassphraseInEnv: masterPassphraseInEnv(),
1392
1483
  // getEffectiveExecutionPolicy spawns powershell — a doomed process on
1393
1484
  // POSIX, where the advisory never applies. Probe only on Windows.
1394
1485
  execPolicy: process.platform === 'win32'
@@ -568,8 +568,8 @@ export function registerRunCommand(program) {
568
568
  .option('--bare', 'With --lease, skip copying your local ~/.agents setup (skills/hooks/commands/MCP) onto the box.')
569
569
  .option('--tailscale', 'Lease the box onto your tailnet (reachable only over Tailscale) rather than a public IP.')
570
570
  .option('--no-tailscale', 'Force a public-IP lease even when a reuse context would default to Tailscale.')
571
- .option('--cloud', 'Vendor cloud placement: dispatch to the agent\'s native cloud (claude→rush, codex→codex, droid→factory, antigravity→antigravity) and stream the result. Same dispatch as `agents cloud run --agent <agent>`; tracked by `agents cloud list/status/logs`. Same as --where cloud. Mutually exclusive with --host/--lease and local-run flags.')
572
- .option('--provider <id>', 'With --cloud: override the agent\'s native cloud provider (rush | codex | factory | antigravity | host).')
571
+ .option('--cloud', 'Vendor cloud placement: dispatch to the agent\'s native cloud (claude→rush, codex→codex, cursor→cursor, droid→factory, antigravity→antigravity) and stream the result. Same dispatch as `agents cloud run --agent <agent>`; tracked by `agents cloud list/status/logs`. Same as --where cloud. Mutually exclusive with --host/--lease and local-run flags.')
572
+ .option('--provider <id>', 'With --cloud: override the agent\'s native cloud provider (rush | codex | cursor | factory | antigravity | host).')
573
573
  .option('--repo <owner/repo>', 'With --cloud: GitHub repository. Repeatable for multi-repo dispatch (Rush Cloud only).', (val, prev) => [...prev, val], [])
574
574
  .option('--branch <name>', 'With --cloud: target git branch.')
575
575
  .option('--cloud-env <id>', 'With --cloud: Codex Cloud environment ID (run\'s --env is the KEY=VAL passthrough, so the cloud env id gets its own flag).');
@@ -657,8 +657,8 @@ export function registerRunCommand(program) {
657
657
  Models (cheap|default|best|ultra): agents models <agent[@version]>
658
658
 
659
659
  Headless plan support (a prompt makes the run headless):
660
- plan works headless on claude, codex, droid, opencode.
661
- kimi, grok, cursor, antigravity have no headless plan mode — a headless
660
+ plan works headless on claude, codex, cursor, droid, opencode.
661
+ kimi, grok, antigravity have no headless plan mode — a headless
662
662
  --mode plan auto-downgrades to --mode auto (with a stderr warning).
663
663
  Interactive plan (omit the prompt) works everywhere it is listed.
664
664
 
@@ -1019,19 +1019,21 @@ export function registerRunCommand(program) {
1019
1019
  const leaseSecretsBundle = process.env.AGENTS_LEASE_SECRETS_BUNDLE;
1020
1020
  const nowSecs = Math.floor(Date.now() / 1000);
1021
1021
  let reuseSlug = options.box;
1022
- // The profile this run's pool/box carries: the repo's .crabbox.yaml
1023
- // `profile:` when declared (what crabbox warmup would label a fresh box
1024
- // with), else crabbox's default. Passing it to leaseAndRun makes the
1025
- // pool-reuse match interchangeable with a fresh warmup.
1022
+ // Lease runs share one default warm pool across repositories. The generic
1023
+ // `.crabbox.yaml profile:` remains available to repo sandbox/CI scripts;
1024
+ // `leaseProfile:` is the explicit opt-in for a dedicated lease hot box.
1026
1025
  const repoRoot = gitToplevel(leaseCwd);
1027
- const { readCrabboxRepoProfile } = await import('../lib/crabbox/config.js');
1028
- const poolProfile = repoRoot ? readCrabboxRepoProfile(repoRoot) : undefined;
1026
+ const { readCrabboxLeaseProfile } = await import('../lib/crabbox/config.js');
1027
+ const poolProfile = repoRoot ? readCrabboxLeaseProfile(repoRoot) : 'default';
1029
1028
  if (options.lease && !reuseSlug && !options.fresh) {
1030
- const { crabboxList } = await import('../lib/crabbox/cli.js');
1031
- const { reusableBoxes, formatBoxRow } = await import('./lease.js');
1029
+ const { crabboxList, poolReusableBoxes } = await import('../lib/crabbox/cli.js');
1030
+ const { formatBoxRow } = await import('./lease.js');
1032
1031
  let warm = [];
1033
1032
  try {
1034
- warm = reusableBoxes(crabboxList({ secretsBundle: leaseSecretsBundle }), nowSecs);
1033
+ warm = poolReusableBoxes(crabboxList({ secretsBundle: leaseSecretsBundle }), {
1034
+ profile: poolProfile,
1035
+ nowSecs,
1036
+ });
1035
1037
  }
1036
1038
  catch {
1037
1039
  warm = []; // crabbox unavailable / no creds → the pool check in leaseAndRun decides
@@ -1102,7 +1104,7 @@ export function registerRunCommand(program) {
1102
1104
  }
1103
1105
  }
1104
1106
  const { detectSignedInRuntimes, resolveClaudeCredentialsBlob, inferLeaseRuntime, profileNeedsBaseRuntimeCredentials } = await import('../lib/crabbox/runtimes.js');
1105
- const { leaseAndRun } = await import('../lib/crabbox/lease.js');
1107
+ const { leaseAndRun, leaseWorkspaceId } = await import('../lib/crabbox/lease.js');
1106
1108
  const { boxAddress } = await import('./lease.js');
1107
1109
  const { getConfiguredRunStrategy, resolveRunVersion } = await import('../lib/rotate.js');
1108
1110
  const { profileExists, readProfile, resolveProfileEnv } = await import('../lib/profiles.js');
@@ -1197,7 +1199,7 @@ export function registerRunCommand(program) {
1197
1199
  ? 'the box is kept after the run'
1198
1200
  : options.fresh
1199
1201
  ? 'the box is destroyed after the run'
1200
- : 'a fresh box is destroyed after the run; a reused pool box is kept';
1202
+ : 'the shared-pool box is kept after the run';
1201
1203
  console.error(chalk.gray(`${boxLifecycle} · shipping ${whatShips}${credentialRuntimes.length > 0 && leaseEmail ? ` (${leaseEmail})` : ''}; ${boxAfterRun}.`));
1202
1204
  // Read the Claude OAuth token from the local Keychain (silent) so it can be
1203
1205
  // written to ~/.claude/.credentials.json on the box — otherwise Claude boots
@@ -1279,6 +1281,7 @@ export function registerRunCommand(program) {
1279
1281
  reuseBox: reuseSlug,
1280
1282
  fresh: options.fresh,
1281
1283
  profile: poolProfile,
1284
+ workspaceId: leaseWorkspaceId(repoRoot ?? leaseCwd),
1282
1285
  copySetup,
1283
1286
  netMode,
1284
1287
  onData: (chunk) => router.push(chunk),
@@ -2455,7 +2458,7 @@ export function registerRunCommand(program) {
2455
2458
  process.exit(1);
2456
2459
  }
2457
2460
  // Default CLI mode is the generic 'plan'. Agents without a read-only
2458
- // mode (antigravity, cursor, kiro, …) degrade via resolveMode to their
2461
+ // mode (antigravity, kiro, …) degrade via resolveMode to their
2459
2462
  // safest native mode (modes[0], typically edit). That covers both the
2460
2463
  // implicit default and an explicit `--mode plan`, so multi-agent
2461
2464
  // scripts can pass a uniform plan flag without per-agent branching.
@@ -42,6 +42,8 @@ export declare function prepareLocalFeedBlocks(localBlocks: OpenBlock[], opts: {
42
42
  dispatch: OpenBlock[];
43
43
  filter: ReturnType<typeof filterBlocksForFeed>;
44
44
  };
45
+ /** Specific host app + routine provenance for one feed row. */
46
+ export declare function formatFeedRuntime(block: Pick<OpenBlock, 'runtime' | 'origin' | 'routineName'>): string;
45
47
  /** Human summary line for one outcome rollup. */
46
48
  export declare function formatOutcomeHeader(group: OutcomeGroup): string;
47
49
  /** Map active sessions into the lightweight hints outcome enrichment needs. */
@@ -59,6 +61,9 @@ export declare function sessionHintsFromActive(sessions: Array<{
59
61
  worktree?: {
60
62
  slug?: string;
61
63
  };
64
+ host?: string;
65
+ origin?: 'cli' | 'routine';
66
+ routineName?: string;
62
67
  }>): SessionOutcomeHint[];
63
68
  /**
64
69
  * True when a SQLite open/query failed because another writer holds the lock.