@phnx-labs/agents-cli 1.22.6 → 1.22.8
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.
- package/CHANGELOG.md +131 -1
- package/README.md +7 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/browser.js +61 -0
- package/dist/commands/exec.js +83 -16
- package/dist/commands/feed.d.ts +2 -1
- package/dist/commands/feed.js +47 -20
- package/dist/commands/focus.js +22 -1
- package/dist/commands/harness.d.ts +0 -1
- package/dist/commands/harness.js +60 -4
- package/dist/commands/models.js +2 -2
- package/dist/commands/monitors.js +2 -2
- package/dist/commands/projects.js +122 -107
- package/dist/commands/routines.js +2 -2
- package/dist/commands/run-account-picker.js +2 -0
- package/dist/commands/secrets.js +1 -1
- package/dist/commands/sessions-backfill.d.ts +33 -0
- package/dist/commands/sessions-backfill.js +83 -1
- package/dist/commands/sessions-stats.d.ts +36 -0
- package/dist/commands/sessions-stats.js +263 -0
- package/dist/commands/sessions.d.ts +1 -1
- package/dist/commands/sessions.js +21 -1
- package/dist/commands/snapshot.d.ts +11 -0
- package/dist/commands/snapshot.js +107 -0
- package/dist/commands/teams.js +2 -1
- package/dist/commands/view.d.ts +9 -1
- package/dist/commands/view.js +63 -12
- package/dist/index.js +4 -2
- package/dist/lib/activity.js +2 -2
- package/dist/lib/agents.js +68 -11
- package/dist/lib/analytics/recipes.js +11 -5
- package/dist/lib/browser/ipc.js +2 -0
- package/dist/lib/browser/profiles.d.ts +15 -7
- package/dist/lib/browser/profiles.js +53 -12
- package/dist/lib/browser/remote-control.d.ts +35 -0
- package/dist/lib/browser/remote-control.js +48 -0
- package/dist/lib/browser/service.d.ts +19 -0
- package/dist/lib/browser/service.js +19 -1
- package/dist/lib/browser/types.d.ts +14 -2
- package/dist/lib/byok-usage.d.ts +38 -0
- package/dist/lib/byok-usage.js +117 -0
- package/dist/lib/capabilities.js +1 -1
- package/dist/lib/device-config.js +8 -0
- package/dist/lib/exec.d.ts +20 -0
- package/dist/lib/exec.js +73 -8
- package/dist/lib/feed-outcome.d.ts +1 -0
- package/dist/lib/feed-outcome.js +2 -0
- package/dist/lib/feed-post.js +1 -1
- package/dist/lib/feed-ranking.d.ts +1 -0
- package/dist/lib/feed-ranking.js +4 -0
- package/dist/lib/feed.d.ts +4 -1
- package/dist/lib/feed.js +25 -0
- package/dist/lib/hosts/passthrough.d.ts +10 -1
- package/dist/lib/hosts/passthrough.js +23 -3
- package/dist/lib/hosts/remote-cmd.js +1 -0
- package/dist/lib/mcp.js +6 -1
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/model-tiers.js +4 -1
- package/dist/lib/models.js +63 -0
- package/dist/lib/placement.d.ts +82 -0
- package/dist/lib/placement.js +188 -0
- package/dist/lib/profiles.d.ts +73 -10
- package/dist/lib/profiles.js +133 -15
- package/dist/lib/project-focus.d.ts +9 -0
- package/dist/lib/project-focus.js +23 -0
- package/dist/lib/project-key.d.ts +1 -1
- package/dist/lib/project-key.js +1 -1
- package/dist/lib/project-probe.d.ts +18 -0
- package/dist/lib/project-probe.js +46 -0
- package/dist/lib/project-status.d.ts +56 -0
- package/dist/lib/project-status.js +125 -18
- package/dist/lib/resources/mcp.js +3 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/rotate.d.ts +19 -4
- package/dist/lib/rotate.js +24 -1
- package/dist/lib/routines.d.ts +2 -0
- package/dist/lib/runner.d.ts +3 -0
- package/dist/lib/runner.js +90 -7
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/audit.js +1 -1
- package/dist/lib/secrets/index.d.ts +1 -0
- package/dist/lib/secrets/index.js +29 -15
- package/dist/lib/secrets/remote.d.ts +1 -1
- package/dist/lib/secrets/remote.js +1 -1
- package/dist/lib/session/active.d.ts +2 -0
- package/dist/lib/session/bash-command.d.ts +2 -3
- package/dist/lib/session/db.d.ts +92 -1
- package/dist/lib/session/db.js +230 -1
- package/dist/lib/session/digest.d.ts +1 -1
- package/dist/lib/session/digest.js +1 -1
- package/dist/lib/share/publish.js +24 -0
- package/dist/lib/snapshot.d.ts +103 -0
- package/dist/lib/snapshot.js +99 -0
- package/dist/lib/startup/command-registry.d.ts +1 -1
- package/dist/lib/startup/command-registry.js +2 -2
- package/dist/lib/subagents-registry.js +3 -0
- package/dist/lib/types.d.ts +11 -2
- package/dist/lib/usage.d.ts +5 -0
- package/dist/lib/usage.js +3 -3
- package/package.json +1 -1
- package/dist/commands/activity.d.ts +0 -87
- package/dist/commands/activity.js +0 -346
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,139 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 1.22.
|
|
3
|
+
## 1.22.8
|
|
4
|
+
|
|
5
|
+
- **`agents browser` now gates cross-machine drives behind per-device consent.** `agents browser <cmd> --host <device>` already routes a browser command to another fleet machine over SSH and drives its browser — but nothing asked that machine's permission, so any box you could SSH to, you could drive. A new device-local `browser.remote-control` setting (off by default, never synced) fixes that: a fleet-remote `browser --host <this-machine> start` is refused with an actionable message until the owner runs `agents browser remote-control on` here. Local starts (no `--host`) are never gated. The fleet passthrough marks every `--host` dispatch with `AGENTS_FLEET_REMOTE` so the far side can tell a cross-machine drive from a local one. New command: `agents browser remote-control [on|off]` (no arg prints status; `--json` supported). Source: `apps/cli/src/lib/browser/remote-control.ts`, `apps/cli/src/commands/browser.ts`, `apps/cli/src/lib/hosts/passthrough.ts`, `apps/cli/src/lib/device-config.ts`, `apps/cli/docs/browser.md`.
|
|
6
|
+
|
|
7
|
+
- **`agents browser` tasks are now attributed to the caller that ran `start`, not
|
|
8
|
+
to the browser daemon.** `Task.owner` (RUSH-2020) was resolved with
|
|
9
|
+
`resolveActor()` *inside* the shared, long-lived browser daemon, so every task —
|
|
10
|
+
no matter which agent or person opened it — was stamped with the identity of
|
|
11
|
+
whoever happened to start the daemon. The caller's identity is now forwarded over
|
|
12
|
+
IPC: the CLI (the caller's own process) puts `actor` (`resolveActor().id`) and
|
|
13
|
+
`launchId` (`$AGENT_LAUNCH_ID`, the per-run id `exec.ts` injects for every harness)
|
|
14
|
+
on the `start` request, and the daemon stamps exactly those. Adds `Task.launchId` —
|
|
15
|
+
which run created a task — the scope a later `browser status --mine` and the
|
|
16
|
+
no-flag current-task default will filter on. Source:
|
|
17
|
+
`apps/cli/src/lib/browser/types.ts`, `apps/cli/src/lib/browser/service.ts`,
|
|
18
|
+
`apps/cli/src/lib/browser/ipc.ts`, `apps/cli/src/commands/browser.ts`.
|
|
19
|
+
|
|
20
|
+
- **`agents harness edit` and `agents harness rename` are now real commands.** `editProfile` and `renameProfile` already existed in `lib/profiles.ts` but nothing on the CLI surface reached them, so changing a custom harness meant hand-editing its YAML. `agents harness edit <name>` applies `--model`, `--base-url`, `--version`, and `--description` in place, preserving fork lineage (an edit never marks a harness as forked from itself). `agents harness rename <name> <new-name>` renames the YAML file and its `name` field, and rewrites `forkedFrom` on every harness that pointed at the old name so the fork graph stays accurate. There is deliberately no `--label`: the header `agents view` prints is derived from the harness name, so renaming is how you change it.
|
|
21
|
+
|
|
22
|
+
- **Run-time messages call a custom harness a "custom harness", not a "profile".** When you `agents run <name>` a custom harness (created with `agents harness add`), the CLI now says `Resolved custom harness '<name>'` and, for a discarded cost tier, `cost tiers don't apply to custom harness '<name>'` — instead of the legacy internal noun "profile". The `--strategy` and account-picker notices on a custom-harness run are aligned too. Behavior is unchanged; the legacy `agents profiles` alias still works. Source: `apps/cli/src/commands/exec.ts`.
|
|
23
|
+
|
|
24
|
+
- **Placement model + `agents run --where` (Phase 2 surface consolidation).**
|
|
25
|
+
"Where does the body run?" is one shared object (`local | device | fleet | cloud | lease`)
|
|
26
|
+
in `src/lib/placement.ts`. `agents run --where device:<name>|auto|lease[:backend]|local`
|
|
27
|
+
expands into the existing `--host` / `--lease` paths; mixing doors fails loud. Docs
|
|
28
|
+
(`00-concepts.md` § Placement, `hosts.md`) and help on run / routines / monitors teach
|
|
29
|
+
the matrix — including that monitors `--device` is **owner**, not body placement.
|
|
30
|
+
Old flags remain aliases. Source: `apps/cli/src/lib/placement.ts`, `apps/cli/src/commands/exec.ts`.
|
|
31
|
+
|
|
32
|
+
- **`agents harness fork` no longer accepts `--label` (breaking change).** The `--label` flag was used to set a human-facing display name for a custom harness. Display names are now always derived from the profile's `name` via a curated vendor/brand table (`deepseek-flash` → `DeepSeek Flash`, `spark` → `Spark`), so the flag is superfluous. Any script that passes `--label` to `agents harness fork` will receive a CLI error; remove the flag to migrate.
|
|
33
|
+
|
|
34
|
+
- **`agents run` no longer auto-picks an account whose token the server has already rejected.** Account rotation judged an account "signed in" from a local heuristic — a credential file is present and its email decodes — which cannot tell a good token from a revoked-but-unexpired one, so `balanced`/`available`/`run auto` could route into a `revoked` account and die at spawn ("session expired"). Eligibility now also reads the daemon's live auth-health probe (`auth-health.ts`): a `revoked` (401/403) account is excluded from the pick, reported as `revoked` by the pre-flight readiness check, shown as "needs re-login" in the account picker, and named in the teams throttle warning. Fail-open: a missing probe or any non-revoked verdict never blocks a launch (a cached `revoked` keeps gating until the daemon's next probe clears it). Source: `apps/cli/src/lib/rotate.ts`, `apps/cli/src/commands/run-account-picker.ts`, `apps/cli/src/commands/teams.ts`, `apps/cli/docs/hosts.md`.
|
|
35
|
+
|
|
36
|
+
- **Scheduled routines no longer overlap or outlive their configured timeout (RUSH-2186).** Detached cron, catchup, and monitor launches now take a cross-process per-routine claim and refuse a second fire while the prior run is alive. The configured deadline is persisted in run metadata; both the live runner and the restart-recovery monitor kill the owned process tree and record `timeout` when it expires. Source: `apps/cli/src/lib/runner.ts`, `apps/cli/src/lib/routines.ts`, `apps/cli/docs/03-routines.md`.
|
|
37
|
+
|
|
38
|
+
- **`agents snapshot` — one-process poll for inventory + active sessions (Phase 4 surface consolidation).**
|
|
39
|
+
Consumers (Factory, scripts, menubar) were forking `view --json` × N harnesses plus
|
|
40
|
+
`sessions --active --json` (and sometimes feed) on every tick. `agents snapshot --json`
|
|
41
|
+
returns the same shapes in one invocation: `inventory` (view), `sessions` (active rows),
|
|
42
|
+
optional `--with-feed` / `--with-sync`. Default sessions scope is this machine; `--all-hosts`
|
|
43
|
+
matches full `sessions --active` fan-out. Does **not** redefine `agents status`, which stays
|
|
44
|
+
the UnifiedSyncStatus sync contract. Source: `apps/cli/src/commands/snapshot.ts`,
|
|
45
|
+
`apps/cli/src/lib/snapshot.ts`.
|
|
46
|
+
|
|
47
|
+
## 1.22.7
|
|
48
|
+
|
|
49
|
+
- **`agents feed --project <name>` scopes the whole feed to one project.** Open
|
|
50
|
+
blocks, the updates view (`--filter updates`), and the trailing activity lane
|
|
51
|
+
are all filtered to the requested repo/project using the same worktree-aware
|
|
52
|
+
project key as `agents perf` (`lib/project-key.ts`). The masthead becomes
|
|
53
|
+
`<project> needs you` / `<project> updates`. Filtering is applied locally after
|
|
54
|
+
the fleet fan-out, so older peers that do not recognize `--project` still
|
|
55
|
+
contribute correctly. Source: `apps/cli/src/commands/feed.ts`,
|
|
56
|
+
`apps/cli/src/lib/feed-ranking.ts`.
|
|
57
|
+
|
|
58
|
+
- **Feed blocks are now stamped with their project.** The `feed-publish` hook
|
|
59
|
+
derives project from the session cwd, and `agents feed post --blocked` stamps it
|
|
60
|
+
on the declared block. Live-session enrichment backfills `project` onto older
|
|
61
|
+
blocks that lack it. Source: `apps/cli/src/lib/feed.ts`,
|
|
62
|
+
`apps/cli/src/lib/feed-outcome.ts`, `apps/cli/src/lib/session/active.ts`.
|
|
63
|
+
|
|
64
|
+
- **`agents activity` is removed.** The standalone milestone timeline is gone;
|
|
65
|
+
its stream is now read through `agents feed --filter all` (blocks + updates) or
|
|
66
|
+
`agents feed --filter updates` (updates only). `activity --project <name>` is
|
|
67
|
+
replaced by `feed --project <name>`. Source: `apps/cli/src/index.ts`,
|
|
68
|
+
`apps/cli/src/startup/command-registry.ts`, `apps/cli/src/commands/activity.ts`
|
|
69
|
+
(deleted), `apps/cli/docs/06-observability.md`,
|
|
70
|
+
`apps/cli/docs/11-projects.md`.
|
|
71
|
+
|
|
72
|
+
- **`agents browser start` no longer fails with "Custom binary not found" when the `default` profile came from another OS.** `~/.agents/agents.yaml` syncs across the fleet, so a `default` profile auto-created on macOS carried a `/Applications/Google Chrome.app/...` binary path that doesn't exist on a Linux box — a bare `browser start` there died with `Custom binary not found`, the top browser roadblock (one session burned six commands working around it). `ensureDefaultBrowserProfile` now validates that the resolved default can actually launch on THIS machine and, if its browser/binary is missing, regenerates the `default` from the installed-browser auto-detect instead of handing back the broken profile. A configured default (`profiles set-default`) that can't launch here warns and falls through to auto-detect; remote (`ssh://`) defaults skip the local binary check since their browser lives on the far host. Source: `apps/cli/src/lib/browser/profiles.ts`, `apps/cli/docs/browser.md`.
|
|
73
|
+
|
|
74
|
+
- **The stray "Agents CLI needs to authenticate to continue" Touch ID sheet now actually heals on an already-hashed machine (SEC-13/#1938 follow-up).** 1.22.5 added a one-time no-ACL re-store for a stale-ACL'd `agents-cli.hmackey` item — the internal HMAC key read *before every hashed keychain lookup*, whose damaged copy pops a generic, context-less Touch ID sheet on nearly any command that touches secrets. But it wired the heal only into `maybeAutoRekey`, which is bypassed for the hmackey and hashed-name lookups themselves (`prepareServiceName` returns early for `HMAC_KEY_ITEM` before `maybeAutoRekey` runs). So the exact hot paths that read the key — the `agents devices list` stats probe a SessionStart hook runs, and every background hashed read — never triggered the heal, and an already-migrated machine prompted forever. The documented `agents secrets rekey` remedy is also a no-op on such a machine: with no cleartext names left to re-key, it returns without re-storing the key. The heal now runs on the read path itself (`readHmacKeyRecord`): the first hashed lookup in the first process re-stores the record no-ACL exactly once (guarded by `healedNoAcl`, so it never churns the keychain afterward) — one last prompt on the read that heals it, then silent forever, on every path. Source: `apps/cli/src/lib/secrets/index.ts`.
|
|
75
|
+
|
|
76
|
+
- **Add Pi (Oh My Pi, `omp`) as a native harness.** agents-cli now installs, runs, and
|
|
77
|
+
syncs resources for [Oh My Pi](https://omp.sh) (`@oh-my-pi/pi-coding-agent`, binary
|
|
78
|
+
`omp`) under id `pi`. Pi is a Bun-based, terminal-first, multi-provider coding agent;
|
|
79
|
+
its cross-provider model catalog (OpenRouter, OpenAI, Anthropic, xAI, DeepSeek, …)
|
|
80
|
+
surfaces in `agents view` and `agents models pi` via `omp models --json`. It is
|
|
81
|
+
Claude-compatible: MCP (`.mcp.json`, stdio + http + headers), skills, file commands, and
|
|
82
|
+
Claude-shaped subagents all sync into `~/.omp/agent/`. Hooks, allowlist, and plugins are
|
|
83
|
+
intentionally off (omp's hook/approval/plugin models don't map to agents-cli's).
|
|
84
|
+
Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/exec.ts`, `apps/cli/src/lib/models.ts`.
|
|
85
|
+
|
|
86
|
+
- **`agents run <profile> --model <tier>` now resolves the cost tier against the profile's own harness, not its host's.** A custom-harness profile (e.g. a DeepSeek model routed through the `claude` host binary) resolved a tier token (`cheap|default|best|ultra`) by calling `resolveTier(options.agent, ...)` with `options.agent` already overwritten to the HOST agent's id — so `best` resolved against Claude's own catalog and could push a real Claude model id as the `--model` flag, clobbering the profile's own `ANTHROPIC_MODEL` env value. Profiles can now declare a `models:` block (per-tier model ids for the harness's own catalog); a requested tier resolves against it first — clamping an unset tier down to the next cheaper one that IS set — and the concrete id is substituted into both the env and the forwarded `--model` value before exec.ts's native (host-catalog) tier logic ever runs. A profile with no `models:` configured degrades gracefully to today's behavior — the harness's single pinned model, with an informational note instead of an error. Source: `apps/cli/src/lib/profiles.ts`, `apps/cli/src/commands/exec.ts`.
|
|
87
|
+
|
|
88
|
+
- **`agents projects status` card: host-grouped agents, focus units, and a warnings footer.** Live agents render under `@host` rows so the same harness on two machines is not collapsed into one cell. Focus counts are labeled `file-touches (Nd)` instead of bare integers. Repo drift, dirty trees, missing checkouts, slug mismatch, unmeasurable schedule, and crash piles land at the bottom with 🔴 critical / ⚠️ continue. Local workspace probe always feeds the footer (full fleet table still requires `--fleet`). Source: `apps/cli/src/lib/project-status.ts`, `project-focus.ts`, `project-probe.ts`, `commands/projects.ts`.
|
|
89
|
+
|
|
90
|
+
- **`agents projects status` and `view`/`show` share one body.** Named form is the full card (every milestone + definition); unnamed is the multi-project rollup. No second implementation to drift. Source: `apps/cli/src/commands/projects.ts`.
|
|
91
|
+
|
|
92
|
+
- **`agents sessions --help` and `05-sessions.md` now teach one session-lifecycle
|
|
93
|
+
matrix.** `focus` / `focus --attach-only` / `detach` / `attach` / `resume` are
|
|
94
|
+
listed as distinct intents (not synonyms), so operators stop guessing among
|
|
95
|
+
`go` / `focus` / `attach` / `resume`. Source: `apps/cli/src/commands/sessions.ts`,
|
|
96
|
+
`apps/cli/src/commands/focus.ts`, `apps/cli/docs/05-sessions.md`.
|
|
4
97
|
|
|
5
98
|
- **Cost tiers are ignored (with a clear warning) for profile runs.** A profile's model comes from its endpoint (e.g. Kimi/DeepSeek/GLM via `agents run <profile>`), not the host harness's catalog — so passing `--model cheap|default|best|ultra` to a profile used to resolve against the *host* harness and forward an incompatible model id to the profile's endpoint. Now a tier on a profile run is discarded with a standout warning and the profile's configured model is used. Concrete `--model <id>` on a profile is unchanged. Source: `apps/cli/src/commands/exec.ts`.
|
|
6
99
|
|
|
100
|
+
- **`agents view` harness rows now lead with the version number.** Custom harness rows previously showed `via <host> <version>` — the host CLI name came first, which buried the version in the middle of the line. The format is now `<version> (forked from <host>)` for pinned harnesses and `<version> (forked from <host>, tracks default)` for unpinned ones that follow the host's global default. The `tracks default` label is shown in green so it stands out at a glance.
|
|
101
|
+
|
|
102
|
+
- **Chained fork lineage in harness headers.** When a custom harness is itself a fork of another custom harness (which in turn forks a native host), the block header now shows the full two-hop chain: `custom · forked from <intermediate> -> <native-host>`. Single-hop forks continue to show `custom · forked from <parent>`.
|
|
103
|
+
|
|
104
|
+
- **BYOK budget bar in `agents view`.** Custom harnesses backed by an OpenRouter key now show a live spend bar (amount used, remaining, and limit) inline on the model/auth row. Keys are deduplicated so multiple harnesses sharing the same keychain entry trigger exactly one API call. The bar is rendered only when a budget is available; harnesses without a BYOK key are unaffected. Source: `apps/cli/src/lib/byok-usage.ts`, `apps/cli/src/commands/view.ts`.
|
|
105
|
+
|
|
106
|
+
- **`agents run auto` with no prompt no longer silently attaches a dead pane or leaves an orphan session (RUSH-2185 / EXEC-23a).** Three latent bugs combined to produce this failure when `auto` picked a harness like `cursor-agent` that exits immediately without a prompt: (F1) the auto-picker had no gate for whether a harness can open a bare interactive REPL — `cursor-agent` was a valid candidate even though its CLI requires a prompt and exits on `argv = []`; (F2) `surfacePaneFailure` was guarded by `status !== 0`, so a clean exit-0 death produced only a bare `[detached]` line with no diagnostic; (F3) the "pane still alive → keep session" fall-through relied on `paneExitStatus` returning `{dead:false}`, which it also returns on any query error (a race right after the pane-died hook), leaving the session alive as an orphan. Fixed: (F1) a new `interactiveRepl` capability bit in `AgentConfig.capabilities` marks every harness; `auto` now filters to REPL-capable candidates before picking, and fails loud naming the installed harnesses when none qualify; (F2) `shouldRecapDeadPane(status, interactive)` surfaces the pane tail any time the run is interactive, regardless of exit code; (F3) `isPaneKnownAliveFromQueryResult(code, stdout)` is now required as positive proof before keeping a session — an ambiguous result tears the session down via `killSession` instead. Source: `apps/cli/src/lib/exec.ts`, `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/types.ts`, `apps/cli/src/lib/capabilities.ts`, `apps/cli/src/commands/exec.ts`, `apps/cli/docs/specifications.md` (EXEC-23a).
|
|
107
|
+
|
|
108
|
+
- **`agents run auto` can pick the Pi (`omp`) harness for prompt-less interactive runs again, and `main` builds green.** The `interactiveRepl` capability bit added by the RUSH-2185 / EXEC-23a fix landed at the same time as the new Pi harness, and neither change saw the other — so `pi` was the one agent in `AGENTS` that never declared the bit, and the completeness test that pins the registry to the capability list went red on `main` (`pi missing interactiveRepl`). Pi now declares `interactiveRepl: true`: bare `omp` runs the TUI, and `omp -p` is the one-shot form that answers a prompt and exits.
|
|
109
|
+
|
|
110
|
+
- **`projects status --fleet` no longer labels this box `@local`.** Local sessions from `getActiveSessions()` lacked `machine`; host-grouped agents stamped remotes only. Locals are now filled with `machineId()` before rollup so the agents roster and fleet lines agree. Source: `apps/cli/src/lib/project-status.ts`, `commands/projects.ts`.
|
|
111
|
+
|
|
112
|
+
- **`agents sessions stats` — which skills/commands you actually invoke, and which are dead weight.** A cheap, db-backed rollup of `session_resource_usage` (the skill/`Skill`-tool + slash-command tallies already recorded at index time), joined to `sessions` for attribution so `--agent`/`--project`/`--since`/`--machine` narrow the window and `--kind`/`--plugin` narrow the resources. A both-ends view: the most-invoked resources (`--bottom` flips to least-invoked, `--top <n>` caps), and the installed-but-never-invoked ones (cross-referenced against `listResources`/`discoverPlugins`) — the productized form of a manual transcript-scan audit. `--json` emits a versioned `sessions-stats` envelope. The signal captures EXPLICIT invocations only (slash commands + `Skill` tool calls) — an auto-triggered skill emits no event and reads as 0, and only Claude transcripts expose the signal today; both caveats are surfaced in help and output. A new `agents sessions backfill resources` folds historical sessions (indexed before the signal shipped) into the usage index, re-parsing each transcript from byte 0, gated by a new `resource_scan_ledger` (schema v31) so reruns skip completed transcripts — mirroring `agents sessions backfill tools`. Source: `apps/cli/src/commands/sessions-stats.ts`, `apps/cli/src/commands/sessions-backfill.ts`, `apps/cli/src/lib/session/db.ts`, `apps/cli/docs/05-sessions.md`, `apps/cli/docs/specifications.md` (SES-IF-4b).
|
|
113
|
+
|
|
114
|
+
- **`agents share` serves screenshots and recordings with a real content-type.**
|
|
115
|
+
Publishing a PNG/JPEG/GIF/WebP/AVIF image, an MP4/MOV/WebM video, or a PDF now
|
|
116
|
+
sets the matching `content-type` instead of `application/octet-stream`. GitHub's
|
|
117
|
+
image proxy (camo) only renders an inline `` when the asset is served as
|
|
118
|
+
a real image/video type, so this is what lets an agent drop a screenshot or a
|
|
119
|
+
screen recording straight into a PR body via `agents share <file>`. HTML, SVG,
|
|
120
|
+
CSS, JS, JSON, and text were already typed correctly. Source:
|
|
121
|
+
`apps/cli/src/lib/share/publish.ts`.
|
|
122
|
+
|
|
123
|
+
- **`agents trends tools-per-session` now counts every scanned session, not just `agents teams`
|
|
124
|
+
runs.** The recipe read `sessions.tool_call_count`, a column nothing populates except the
|
|
125
|
+
teams summarizer (`apps/cli/src/lib/teams/summarizer.ts`) — the general session indexer never
|
|
126
|
+
computes it. So every session that did not come from a team was scored 0 or excluded outright
|
|
127
|
+
by `WHERE tool_call_count IS NOT NULL`, pinning the fleet-wide p50 at 0 however many tools ran
|
|
128
|
+
and leaving only `claude` in the table. It now reads `tool_scan_ledger.call_count`, the
|
|
129
|
+
per-session count the tool indexer writes for every session it scans — the same index behind
|
|
130
|
+
`agents sessions --include tools`, so the two surfaces stop disagreeing. Sessions with
|
|
131
|
+
genuinely zero tool calls still count as 0 instead of vanishing. On a real 7-day window this
|
|
132
|
+
took the sample from 400 to 570 sessions and surfaced `grok`, `rush`, `codex`, `kimi`,
|
|
133
|
+
`droid` and `antigravity`, none of which had ever appeared. Run `agents sessions backfill
|
|
134
|
+
tools` once if historical sessions were never indexed. Source:
|
|
135
|
+
`apps/cli/src/lib/analytics/recipes.ts`.
|
|
136
|
+
|
|
7
137
|
## 1.22.5
|
|
8
138
|
|
|
9
139
|
- **`agents events` can now filter by `--session <id>` and `--bundle <name>` — trace which agent/session triggered a secret access.** Every event already carries the provenance `sessionId`, and secrets events carry the `bundle` in their payload, but neither was queryable: you could see *that* the `share` bundle was read, not *which session* read it. `--session` (wired to the engine's existing `sessionId` filter) and `--bundle` (a new payload filter across both the operational log and the activity stream) close that gap. `agents events --module secrets --bundle share --session <id>` answers "which agent read the share bundle" — the attribution the Touch ID storm investigation needed, since the macOS biometric sheet itself emits no event. Source: `apps/cli/src/lib/event-stream.ts`, `apps/cli/src/commands/events.ts`, `apps/cli/docs/06-observability.md`.
|
package/README.md
CHANGED
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
<a href="https://x.ai" title="Grok Build (xAI)"><strong>Grok</strong></a>
|
|
32
32
|
|
|
33
33
|
<a href="https://factory.ai" title="Factory AI Droid"><strong>Droid</strong></a>
|
|
34
|
+
|
|
35
|
+
<a href="https://omp.sh" title="Oh My Pi"><strong>Pi</strong></a>
|
|
34
36
|
</p>
|
|
35
37
|
|
|
36
38
|
https://agents-cli.sh/demo.mp4
|
|
@@ -270,6 +272,11 @@ agents sessions --include tools --query 'program:git' --count --fleet --json
|
|
|
270
272
|
|
|
271
273
|
# Populate historical tool rows once on each device
|
|
272
274
|
agents sessions backfill tools --fleet
|
|
275
|
+
|
|
276
|
+
# Which skills/commands you actually invoke -- and which installed ones are dead weight
|
|
277
|
+
agents sessions stats
|
|
278
|
+
agents sessions stats --zero # only the never-invoked (dead weight)
|
|
279
|
+
agents sessions backfill resources # fold historical sessions into the usage index
|
|
273
280
|
```
|
|
274
281
|
|
|
275
282
|
Interactive picker when you're in a terminal. Structured output (`--json`, `--markdown`, filtered by role or turn count) when piped.
|
package/dist/bin/agents
CHANGED
|
Binary file
|
package/dist/commands/browser.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { listProfiles, getProfile, createProfile, deleteProfile, ensureDefaultBrowserProfile, getConfiguredDefaultProfileName, DEFAULT_BROWSER_PROFILE_NAME, getProfileRuntimeDir, extractConfiguredPort, findFreeProfilePort, getEndpointPresets, } from '../lib/browser/profiles.js';
|
|
4
4
|
import { updateMeta } from '../lib/state.js';
|
|
5
|
+
import { resolveActor } from '../lib/actor.js';
|
|
5
6
|
import { loginsForProfile, profilesLoggedInto, serviceForUrl, loginsWithAccountsForProfile, accountsForProfile, credKeysForService, AUTH_SIGNATURES, } from '../lib/browser/login-detection.js';
|
|
6
7
|
import { parseSecretRef } from '../lib/browser/secret-ref.js';
|
|
7
8
|
import { readAndResolveBundleEnv, isHeadlessSecretsContext, bundleExists, readBundle, describeBundle } from '../lib/secrets/bundles.js';
|
|
@@ -13,6 +14,8 @@ import { discoverBrowserWsUrl, verifyBrowserIdentity } from '../lib/browser/cdp.
|
|
|
13
14
|
import { parseTargetFilter } from '../lib/browser/service.js';
|
|
14
15
|
import { BrowserDaemonNotRunningError, formatBrowserDaemonNotRunningError, sendIPCRequest, } from '../lib/browser/ipc.js';
|
|
15
16
|
import { browserTaskPicker } from './browser-picker.js';
|
|
17
|
+
import { assertRemoteControlAllowed } from '../lib/browser/remote-control.js';
|
|
18
|
+
import { getConfigValue, setConfigValue } from '../lib/device-config.js';
|
|
16
19
|
import { isInteractiveTerminal } from './utils.js';
|
|
17
20
|
import { registerCommandGroups, setHelpSections } from '../lib/help.js';
|
|
18
21
|
import { buildHar } from '../lib/browser/har.js';
|
|
@@ -76,6 +79,12 @@ export function registerBrowserCommand(program) {
|
|
|
76
79
|
agents browser navigate https://example.com
|
|
77
80
|
agents browser screenshot
|
|
78
81
|
|
|
82
|
+
# Drive another machine's browser (needs its consent — see remote-control)
|
|
83
|
+
agents browser start --host zion
|
|
84
|
+
|
|
85
|
+
# Allow / deny other fleet machines driving THIS machine's browser
|
|
86
|
+
agents browser remote-control on
|
|
87
|
+
|
|
79
88
|
# End the session when done
|
|
80
89
|
agents browser done
|
|
81
90
|
`,
|
|
@@ -609,6 +618,42 @@ function registerProfilesCommands(browser) {
|
|
|
609
618
|
});
|
|
610
619
|
}
|
|
611
620
|
function registerTaskCommands(browser) {
|
|
621
|
+
browser
|
|
622
|
+
.command('remote-control [state]')
|
|
623
|
+
.description("Allow or deny other fleet machines driving THIS machine's browser over `browser --host`. " +
|
|
624
|
+
'`on`/`off` to set (device-local, never synced); no argument prints the current value. Default off.')
|
|
625
|
+
.option('--json', 'Output as JSON')
|
|
626
|
+
.action((state, opts) => {
|
|
627
|
+
const KEY = 'browser.remote-control';
|
|
628
|
+
if (state === undefined) {
|
|
629
|
+
const cur = getConfigValue(KEY).value === true;
|
|
630
|
+
if (opts.json) {
|
|
631
|
+
console.log(JSON.stringify({ remoteControl: cur }));
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
console.log(`Remote browser control (this machine): ${cur ? 'on' : 'off'}`);
|
|
635
|
+
if (!cur)
|
|
636
|
+
console.log('Enable with: agents browser remote-control on');
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
const norm = state.toLowerCase();
|
|
640
|
+
const onWords = ['on', 'true', 'yes', 'allow', 'enable'];
|
|
641
|
+
const offWords = ['off', 'false', 'no', 'deny', 'disable'];
|
|
642
|
+
if (!onWords.includes(norm) && !offWords.includes(norm)) {
|
|
643
|
+
console.error(`Expected "on" or "off", got "${state}".`);
|
|
644
|
+
process.exit(1);
|
|
645
|
+
}
|
|
646
|
+
const value = onWords.includes(norm);
|
|
647
|
+
setConfigValue(KEY, value);
|
|
648
|
+
if (opts.json) {
|
|
649
|
+
console.log(JSON.stringify({ remoteControl: value }));
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
console.log(`Remote browser control (this machine) is now ${value ? 'on' : 'off'}.`);
|
|
653
|
+
console.log(value
|
|
654
|
+
? 'Other fleet machines can now drive this browser via `browser --host <this-device>`.'
|
|
655
|
+
: 'Cross-machine `browser --host` drives to this machine are refused.');
|
|
656
|
+
});
|
|
612
657
|
browser
|
|
613
658
|
.command('start')
|
|
614
659
|
.description('Start a browser task. Pass --profile <name>; omit to use your configured default (`agents browser profiles set-default`), else auto-pick an installed Chromium-family browser.')
|
|
@@ -622,6 +667,16 @@ function registerTaskCommands(browser) {
|
|
|
622
667
|
.option('--duration <sec>', 'Recording duration cap in seconds (with --record; default 60)', (v) => parseInt(v, 10))
|
|
623
668
|
.option('--max-mb <mb>', 'Recording size cap in MB (with --record; default 25)', (v) => parseInt(v, 10))
|
|
624
669
|
.action(async (opts) => {
|
|
670
|
+
// Consent gate: a fleet-remote `browser --host <this-machine> start` may
|
|
671
|
+
// only open a browser here if the owner opted in. Refuse before we resolve
|
|
672
|
+
// or auto-create any profile. Local starts are never gated.
|
|
673
|
+
try {
|
|
674
|
+
assertRemoteControlAllowed();
|
|
675
|
+
}
|
|
676
|
+
catch (err) {
|
|
677
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
678
|
+
process.exit(1);
|
|
679
|
+
}
|
|
625
680
|
let profileName = opts.profile;
|
|
626
681
|
if (!profileName) {
|
|
627
682
|
try {
|
|
@@ -698,6 +753,12 @@ function registerTaskCommands(browser) {
|
|
|
698
753
|
url: opts.url,
|
|
699
754
|
endpoint: opts.endpoint,
|
|
700
755
|
skipDomainSkill: opts.skills === false,
|
|
756
|
+
// Forward the caller's identity: the browser daemon is shared, so it
|
|
757
|
+
// cannot resolve who/which-run called `start`. `resolveActor()` runs
|
|
758
|
+
// here in the CLI (the caller's process); `$AGENT_LAUNCH_ID` is the
|
|
759
|
+
// per-run id exec.ts injects for every harness.
|
|
760
|
+
actor: resolveActor().id,
|
|
761
|
+
launchId: process.env.AGENT_LAUNCH_ID,
|
|
701
762
|
});
|
|
702
763
|
if (!response.ok) {
|
|
703
764
|
console.error(response.error);
|
package/dist/commands/exec.js
CHANGED
|
@@ -553,13 +553,14 @@ export function registerRunCommand(program) {
|
|
|
553
553
|
.option('--budget <tokens>', 'Loop token hard-cap: stop once cumulative tokens reach this (stoppedBy: budget), enforced outside the agent. Loop only.')
|
|
554
554
|
.option('--until <signal>', 'Loop stop condition. `signal` reads <runDir>/loop-signal.json {continue,reason} each iteration; absent or continue:false stops (fail-closed). Loop only.')
|
|
555
555
|
.option('--interval <dur>', 'Loop delay between iterations ("0" back-to-back, "30m" paces). Loop only.')
|
|
556
|
-
.option('--
|
|
557
|
-
.option('--
|
|
556
|
+
.option('--where <spec>', 'Where this run\'s body executes (one placement door): local | device:<name> | auto | lease[:backend]. Expands to --host/--lease. Do not combine with those flags. See docs/00-concepts.md#placement.')
|
|
557
|
+
.option('--host <name>', 'Offload this run onto another machine over SSH — a device name, registered host, or user@host. Pass "auto" to pick from 14d usage affinity (most-used online device has highest probability). Same as --where device:<name>. See `agents devices`.')
|
|
558
|
+
.option('--device <name>', 'Alias of --host. Pass "auto" for affinity-based device pick (same as --where auto).')
|
|
558
559
|
.option('--remote-cwd <dir>', "Explicit host working directory for --host runs, used VERBATIM (overrides --cwd; usually --cwd suffices — it re-roots a local-home path onto the remote home). Pass a single-quoted '$HOME/…' or a valid remote absolute path; a local ~ expands here and won't exist there (/Users/you vs /home/you).")
|
|
559
560
|
.option('--no-follow', 'With --host, dispatch detached and return immediately (track via `agents hosts ps/logs`).')
|
|
560
561
|
.option('--any', 'With --host <cap> (a capability tag), pick any matching host instead of erroring when several match.')
|
|
561
562
|
.option('--copy-creds', 'With --host, copy the picked runtime credentials (and Claude OAuth token) to the host, then shred them after the run. Opt-in per run.')
|
|
562
|
-
.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.')
|
|
563
|
+
.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). Same as --where lease[:backend]. Unlike --host, no machine is registered.')
|
|
563
564
|
.option('--box <slug>', 'Reuse an existing warm crabbox box for this run instead of provisioning a disposable --lease box.')
|
|
564
565
|
.option('--keep-box', 'With --lease, keep the box after the run instead of stopping it.')
|
|
565
566
|
.option('--reuse', 'With --lease, reuse the most-recently-used warm box if one exists (else provision fresh). The scriptable form of the interactive reuse picker.')
|
|
@@ -601,6 +602,11 @@ export function registerRunCommand(program) {
|
|
|
601
602
|
agents run auto "fix the flaky test" --mode edit
|
|
602
603
|
agents run auto --host yosemite-s0 "fix the flaky test" # pin the host
|
|
603
604
|
|
|
605
|
+
# Placement (one door — where the body runs). Old flags still work.
|
|
606
|
+
agents run claude "…" --where device:yosemite-s0 # = --host yosemite-s0
|
|
607
|
+
agents run claude "…" --where auto # = --device auto
|
|
608
|
+
agents run claude "fix CI" --where lease --mode edit
|
|
609
|
+
|
|
604
610
|
# Open the session in a terminal tab — detected from where your sessions
|
|
605
611
|
# already run (Ghostty / iTerm / Terminal.app); force one with a value
|
|
606
612
|
agents run claude --terminal
|
|
@@ -688,6 +694,36 @@ export function registerRunCommand(program) {
|
|
|
688
694
|
await handleTerminalHandoff(agentSpec, options, prompt);
|
|
689
695
|
return;
|
|
690
696
|
}
|
|
697
|
+
// Placement: --where expands into --host / --lease before any dispatch.
|
|
698
|
+
// One door for "where does the body run?" — old flags remain aliases.
|
|
699
|
+
// See lib/placement.ts and docs/00-concepts.md#placement.
|
|
700
|
+
{
|
|
701
|
+
const { placementFromRunFlags, expandPlacementToRunFlags, PlacementError } = await import('../lib/placement.js');
|
|
702
|
+
try {
|
|
703
|
+
const placement = placementFromRunFlags(options);
|
|
704
|
+
if (options.where) {
|
|
705
|
+
const expanded = expandPlacementToRunFlags(placement);
|
|
706
|
+
if (expanded.host !== undefined)
|
|
707
|
+
options.host = expanded.host;
|
|
708
|
+
if (expanded.device !== undefined)
|
|
709
|
+
options.device = expanded.device;
|
|
710
|
+
if (expanded.lease !== undefined)
|
|
711
|
+
options.lease = expanded.lease;
|
|
712
|
+
if (expanded.box !== undefined)
|
|
713
|
+
options.box = expanded.box;
|
|
714
|
+
// Clear the where flag so remote re-entry (host dispatch) does not
|
|
715
|
+
// re-expand and conflict with the concrete host we just set.
|
|
716
|
+
options.where = undefined;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
catch (err) {
|
|
720
|
+
if (err instanceof PlacementError) {
|
|
721
|
+
console.error(chalk.red(err.message));
|
|
722
|
+
process.exit(1);
|
|
723
|
+
}
|
|
724
|
+
throw err;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
691
727
|
// --notify: post a desktop notification when this run finishes. Armed on
|
|
692
728
|
// process exit so it covers EVERY dispatch path below (local, --host,
|
|
693
729
|
// --lease, the error path) instead of one branch. Only for headless runs
|
|
@@ -1568,7 +1604,7 @@ export function registerRunCommand(program) {
|
|
|
1568
1604
|
await warnUnpushedWork(resumeExec.cwd ?? process.cwd());
|
|
1569
1605
|
process.exit(resumeExit);
|
|
1570
1606
|
}
|
|
1571
|
-
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, headlessPlanStallCommand, nativeResume, resolveInteractive, inferredInteractiveWithoutTty }, { ALL_AGENT_IDS, ACCOUNT_INSPECTION_AGENT_IDS, agentLabel, supportsAccountInspection }, { profileExists, resolveProfileForRun }, { readAndResolveBundleEnv, describeBundle, assertRemoteBundleFlagsUnsupported }, { splitBundleRef, resolveHostSshTarget, remoteResolveEnv }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, rotationFailoverChain, shouldArmRotationFailover, RUN_STRATEGIES, collectHarnessCandidates, pickHarnessWeighted, classifyHarnessCandidates, formatHarnessPickBanner, formatNoHealthyHarnessError, formatNoHealthyAccountError }, { getGlobalDefault, getVersionHomePath, resolveVersion, resolveVersionAlias, ensureAgentRunnable }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef, resolveAllowedSubagents, pruneStaleWorkflowSubagents, ensureSubagentDispatchTool }, { resolveRunDefaults }, { getMcpServersByName, buildWorkflowMcpConfig }, { supports }, { shareRuntimeEnv },] = await Promise.all([
|
|
1607
|
+
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, headlessPlanStallCommand, nativeResume, resolveInteractive, inferredInteractiveWithoutTty }, { ALL_AGENT_IDS, ACCOUNT_INSPECTION_AGENT_IDS, agentLabel, supportsAccountInspection }, { profileExists, resolveProfileForRun }, { readAndResolveBundleEnv, describeBundle, assertRemoteBundleFlagsUnsupported }, { splitBundleRef, resolveHostSshTarget, remoteResolveEnv }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, rotationFailoverChain, shouldArmRotationFailover, RUN_STRATEGIES, collectHarnessCandidates, pickHarnessWeighted, classifyHarnessCandidates, formatHarnessPickBanner, formatNoHealthyHarnessError, formatNoHealthyAccountError }, { getGlobalDefault, getVersionHomePath, resolveVersion, resolveVersionAlias, ensureAgentRunnable }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef, resolveAllowedSubagents, pruneStaleWorkflowSubagents, ensureSubagentDispatchTool }, { resolveRunDefaults }, { getMcpServersByName, buildWorkflowMcpConfig }, { supports, capableAgents }, { shareRuntimeEnv },] = await Promise.all([
|
|
1572
1608
|
import('../lib/exec.js'),
|
|
1573
1609
|
import('../lib/agents.js'),
|
|
1574
1610
|
import('../lib/profiles.js'),
|
|
@@ -1615,7 +1651,7 @@ export function registerRunCommand(program) {
|
|
|
1615
1651
|
const cwd = options.cwd ?? process.cwd();
|
|
1616
1652
|
if (accountPickerRequested && !isValidAgent(rawAgent)) {
|
|
1617
1653
|
if (profileExists(rawAgent)) {
|
|
1618
|
-
console.error(chalk.red(`Account selection is not available for
|
|
1654
|
+
console.error(chalk.red(`Account selection is not available for custom harness '${rawAgent}'. Run its concrete host agent with @ instead.`));
|
|
1619
1655
|
process.exit(1);
|
|
1620
1656
|
}
|
|
1621
1657
|
if (resolveWorkflowRef(rawAgent, cwd)) {
|
|
@@ -1629,9 +1665,24 @@ export function registerRunCommand(program) {
|
|
|
1629
1665
|
// — launching a default "because it's there" is how a rotate loop
|
|
1630
1666
|
// hammers an exhausted account.
|
|
1631
1667
|
const byHarness = await collectHarnessCandidates();
|
|
1632
|
-
|
|
1668
|
+
// F1 (RUSH-2185 / EXEC-23a): a prompt-less run is interactive; only
|
|
1669
|
+
// harnesses that can open a REPL with no argv are valid candidates.
|
|
1670
|
+
// cursor-agent and similar exit immediately without a prompt, which
|
|
1671
|
+
// leaves a silent [detached] pane and an orphan session.
|
|
1672
|
+
const interactive = prompt === undefined && options.headless !== true;
|
|
1673
|
+
const replCapable = interactive ? new Set(capableAgents('interactiveRepl')) : null;
|
|
1674
|
+
const candidateHarness = replCapable
|
|
1675
|
+
? new Map([...byHarness].filter(([id]) => replCapable.has(id)))
|
|
1676
|
+
: byHarness;
|
|
1677
|
+
const harnessPick = pickHarnessWeighted(candidateHarness);
|
|
1633
1678
|
if (!harnessPick) {
|
|
1634
|
-
|
|
1679
|
+
if (replCapable && byHarness.size > 0 && candidateHarness.size === 0) {
|
|
1680
|
+
const installed = [...byHarness.keys()].join(', ');
|
|
1681
|
+
console.error(chalk.red(`No installed harness supports a prompt-less interactive REPL (installed: ${installed}). Pass a prompt (-p) or install claude, codex, or another REPL-capable harness.`));
|
|
1682
|
+
}
|
|
1683
|
+
else {
|
|
1684
|
+
console.error(chalk.red(formatNoHealthyHarnessError(classifyHarnessCandidates(candidateHarness))));
|
|
1685
|
+
}
|
|
1635
1686
|
process.exit(1);
|
|
1636
1687
|
}
|
|
1637
1688
|
agent = harnessPick.picked.agent;
|
|
@@ -1653,14 +1704,30 @@ export function registerRunCommand(program) {
|
|
|
1653
1704
|
// so Chinese models (Kimi, DeepSeek, Qwen, GLM) can run inside
|
|
1654
1705
|
// Claude Code without a local proxy.
|
|
1655
1706
|
try {
|
|
1656
|
-
const resolved = resolveProfileForRun(rawAgent);
|
|
1707
|
+
const resolved = resolveProfileForRun(rawAgent, options.model);
|
|
1657
1708
|
agent = resolved.agent;
|
|
1658
1709
|
if (!version)
|
|
1659
1710
|
version = resolved.version;
|
|
1660
1711
|
profileEnv = resolved.env;
|
|
1661
1712
|
profileFallbackModel = resolved.fallbackModel;
|
|
1662
1713
|
fromProfile = true;
|
|
1663
|
-
process.stderr.write(chalk.gray(`Resolved
|
|
1714
|
+
process.stderr.write(chalk.gray(`Resolved custom harness '${resolved.profileName}' -> ${agent}${version ? `@${version}` : ''}\n`));
|
|
1715
|
+
if (resolved.tierNote) {
|
|
1716
|
+
process.stderr.write(chalk.gray(`[agents] ${resolved.tierNote}\n`));
|
|
1717
|
+
}
|
|
1718
|
+
// A tier token (cheap/default/best/ultra) already resolved against
|
|
1719
|
+
// this PROFILE's own `models:` map above, when the profile opts in.
|
|
1720
|
+
// Replace the raw --model value here so the tier never reaches the
|
|
1721
|
+
// native, HOST-catalog tier block below. When the profile has no
|
|
1722
|
+
// `models:` opt-in at all, resolvedModel stays undefined and
|
|
1723
|
+
// options.model is left as the raw tier token on purpose — the
|
|
1724
|
+
// "cost tiers don't apply to custom harness ..." discard guard further
|
|
1725
|
+
// down this function is the canonical fallback for that case, and
|
|
1726
|
+
// this block must not race it with a second, differently-worded
|
|
1727
|
+
// message.
|
|
1728
|
+
if (resolved.resolvedModel !== undefined) {
|
|
1729
|
+
options.model = resolved.resolvedModel;
|
|
1730
|
+
}
|
|
1664
1731
|
}
|
|
1665
1732
|
catch (err) {
|
|
1666
1733
|
console.error(chalk.red(err.message));
|
|
@@ -1863,7 +1930,7 @@ export function registerRunCommand(program) {
|
|
|
1863
1930
|
else {
|
|
1864
1931
|
console.error(chalk.red(`Unknown agent: ${rawAgent}`));
|
|
1865
1932
|
console.error(chalk.gray(`Available agents: ${ALL_AGENT_IDS.join(', ')}`));
|
|
1866
|
-
console.error(chalk.gray(`Or add a
|
|
1933
|
+
console.error(chalk.gray(`Or add a custom harness: agents harness add <name>`));
|
|
1867
1934
|
process.exit(1);
|
|
1868
1935
|
}
|
|
1869
1936
|
}
|
|
@@ -2031,7 +2098,7 @@ export function registerRunCommand(program) {
|
|
|
2031
2098
|
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} ignored: version ${version} is pinned\n`));
|
|
2032
2099
|
}
|
|
2033
2100
|
else if (fromProfile) {
|
|
2034
|
-
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} ignored:
|
|
2101
|
+
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} ignored: custom harness pins its own version/auth\n`));
|
|
2035
2102
|
}
|
|
2036
2103
|
else {
|
|
2037
2104
|
try {
|
|
@@ -2281,12 +2348,12 @@ export function registerRunCommand(program) {
|
|
|
2281
2348
|
? (workflowModel ?? (options.fallback ? undefined : runDefaults.model))
|
|
2282
2349
|
: undefined);
|
|
2283
2350
|
// Cost tiers (cheap|default|best|ultra) resolve against a harness's own model
|
|
2284
|
-
// catalog. A
|
|
2285
|
-
// tier here would forward an incompatible host-harness model to a
|
|
2286
|
-
// Discard it loudly and let the
|
|
2351
|
+
// catalog. A custom harness's model comes from its endpoint, not the host
|
|
2352
|
+
// harness, so a tier here would forward an incompatible host-harness model to a
|
|
2353
|
+
// different API. Discard it loudly and let the custom harness's own model stand.
|
|
2287
2354
|
if (fromProfile && model && isTierToken(model)) {
|
|
2288
|
-
process.stderr.write(chalk.yellow(`[agents] --model ${model}: cost tiers don't apply to
|
|
2289
|
-
`(its model comes from the endpoint) — ignoring the tier, using the
|
|
2355
|
+
process.stderr.write(chalk.yellow(`[agents] --model ${model}: cost tiers don't apply to custom harness '${rawAgent}' ` +
|
|
2356
|
+
`(its model comes from the endpoint) — ignoring the tier, using the custom harness's configured model\n`));
|
|
2290
2357
|
model = undefined;
|
|
2291
2358
|
}
|
|
2292
2359
|
const execOptions = {
|
package/dist/commands/feed.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* `agents feed post`: agent-callable free-text progress into the activity
|
|
14
14
|
* stream (milestone `status.posted`). Session/agent/host/runtime/pid identity
|
|
15
15
|
* is auto-stamped from env + the pid registry — no domain-specific flags.
|
|
16
|
-
* Humans watch via the feed activity lane
|
|
16
|
+
* Humans watch via the feed activity lane.
|
|
17
17
|
*/
|
|
18
18
|
import type { Command } from 'commander';
|
|
19
19
|
import { type OpenBlock } from '../lib/feed.js';
|
|
@@ -47,6 +47,7 @@ export declare function formatOutcomeHeader(group: OutcomeGroup): string;
|
|
|
47
47
|
export declare function sessionHintsFromActive(sessions: Array<{
|
|
48
48
|
sessionId?: string;
|
|
49
49
|
agentId?: string;
|
|
50
|
+
cwd?: string;
|
|
50
51
|
ticket?: {
|
|
51
52
|
id?: string;
|
|
52
53
|
};
|