@phnx-labs/agents-cli 1.20.52 → 1.20.54
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 +63 -2
- package/README.md +12 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/cloud.d.ts +3 -0
- package/dist/commands/cloud.js +2 -1
- package/dist/commands/exec.js +65 -1
- package/dist/commands/feed.d.ts +29 -0
- package/dist/commands/feed.js +237 -32
- package/dist/commands/memory.d.ts +9 -0
- package/dist/commands/memory.js +164 -0
- package/dist/commands/message.d.ts +11 -6
- package/dist/commands/message.js +140 -5
- package/dist/commands/routines.js +12 -0
- package/dist/commands/secrets-migrate.d.ts +2 -1
- package/dist/commands/secrets-migrate.js +88 -13
- package/dist/commands/secrets.js +4 -1
- package/dist/commands/sessions.js +15 -4
- package/dist/commands/ssh.js +18 -6
- package/dist/commands/worktree.js +4 -2
- package/dist/index.js +16 -21
- package/dist/lib/agents.js +249 -17
- package/dist/lib/answer-router.d.ts +75 -0
- package/dist/lib/answer-router.js +149 -0
- package/dist/lib/ask-classifier.d.ts +71 -0
- package/dist/lib/ask-classifier.js +197 -0
- package/dist/lib/cloud/antigravity.d.ts +0 -2
- package/dist/lib/cloud/antigravity.js +2 -17
- package/dist/lib/cloud/codex.js +3 -18
- package/dist/lib/cloud/rush.js +3 -15
- package/dist/lib/cloud/stream.js +2 -0
- package/dist/lib/cloud/types.d.ts +21 -0
- package/dist/lib/cloud/types.js +81 -0
- package/dist/lib/crabbox/cli.d.ts +1 -1
- package/dist/lib/crabbox/cli.js +12 -2
- package/dist/lib/crabbox/lease.d.ts +13 -0
- package/dist/lib/crabbox/lease.js +11 -2
- package/dist/lib/crabbox/progress.d.ts +62 -0
- package/dist/lib/crabbox/progress.js +129 -0
- package/dist/lib/devices/resolve-target.d.ts +21 -4
- package/dist/lib/devices/resolve-target.js +82 -14
- package/dist/lib/devices/sync.d.ts +18 -0
- package/dist/lib/devices/sync.js +34 -1
- package/dist/lib/events.js +4 -1
- package/dist/lib/exec.js +19 -1
- package/dist/lib/feed-outcome.d.ts +101 -0
- package/dist/lib/feed-outcome.js +244 -0
- package/dist/lib/feed-policy.d.ts +30 -0
- package/dist/lib/feed-policy.js +133 -0
- package/dist/lib/feed.d.ts +127 -3
- package/dist/lib/feed.js +416 -40
- package/dist/lib/git.d.ts +17 -1
- package/dist/lib/git.js +20 -1
- package/dist/lib/hooks.js +522 -12
- package/dist/lib/hosts/passthrough.d.ts +3 -3
- package/dist/lib/hosts/passthrough.js +3 -4
- package/dist/lib/mailbox-gc.d.ts +22 -0
- package/dist/lib/mailbox-gc.js +161 -0
- package/dist/lib/mailbox.d.ts +26 -2
- package/dist/lib/mailbox.js +80 -5
- package/dist/lib/mcp.js +82 -0
- package/dist/lib/memory.d.ts +55 -0
- package/dist/lib/memory.js +274 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/notify.d.ts +16 -0
- package/dist/lib/notify.js +61 -0
- package/dist/lib/operator.d.ts +26 -0
- package/dist/lib/operator.js +107 -0
- package/dist/lib/plugins.d.ts +35 -0
- package/dist/lib/plugins.js +217 -0
- package/dist/lib/remote-agents-json.d.ts +14 -0
- package/dist/lib/remote-agents-json.js +94 -0
- package/dist/lib/resources/mcp.js +44 -0
- package/dist/lib/resources/memory.d.ts +15 -0
- package/dist/lib/resources/memory.js +46 -0
- package/dist/lib/resources/types.d.ts +2 -2
- package/dist/lib/runner.d.ts +43 -0
- package/dist/lib/runner.js +323 -74
- package/dist/lib/sandbox.js +6 -0
- package/dist/lib/secrets/bundles.js +38 -13
- package/dist/lib/secrets/icloud-import.d.ts +12 -3
- package/dist/lib/secrets/icloud-import.js +37 -7
- package/dist/lib/secrets/index.d.ts +106 -2
- package/dist/lib/secrets/index.js +603 -28
- package/dist/lib/session/active.d.ts +18 -0
- package/dist/lib/session/active.js +47 -17
- package/dist/lib/session/db.d.ts +9 -1
- package/dist/lib/session/db.js +18 -3
- package/dist/lib/session/discover.d.ts +13 -0
- package/dist/lib/session/discover.js +31 -0
- package/dist/lib/session/parse.d.ts +8 -0
- package/dist/lib/session/parse.js +42 -21
- package/dist/lib/session/remote-active.js +8 -89
- package/dist/lib/session/remote.d.ts +13 -0
- package/dist/lib/session/remote.js +27 -4
- package/dist/lib/session/state.d.ts +11 -0
- package/dist/lib/session/state.js +37 -0
- package/dist/lib/session/tail.d.ts +23 -4
- package/dist/lib/session/tail.js +34 -16
- package/dist/lib/session/throughput.d.ts +30 -0
- package/dist/lib/session/throughput.js +86 -0
- package/dist/lib/shim-heal.d.ts +12 -3
- package/dist/lib/shim-heal.js +12 -6
- package/dist/lib/staleness/detectors/subagents.js +57 -3
- package/dist/lib/staleness/writers/hooks.js +7 -3
- package/dist/lib/staleness/writers/subagents.js +37 -6
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/dist/lib/subagents.d.ts +52 -0
- package/dist/lib/subagents.js +315 -12
- package/dist/lib/teams/worktree.d.ts +8 -0
- package/dist/lib/teams/worktree.js +8 -0
- package/dist/lib/types.d.ts +10 -2
- package/dist/lib/versions.js +38 -48
- package/package.json +4 -3
- package/scripts/postinstall.js +61 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,13 +2,74 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.20.54
|
|
6
|
+
|
|
7
|
+
- **Unified fleet target resolution for `agents ssh` + `sessions --host`.** `agents ssh` now accepts the full target grammar the fan-out already used — a registered `name`, a `user@device` (same device, login user overridden, still dialed via its Tailscale route rather than raw LAN DNS), and an ad-hoc `user@host`/`host` literal — instead of only an exact device name (`agents ssh muqsit@mac-mini` no longer errors "Unknown device"). A bare unregistered alias still reports "Unknown device". `sessions --host user@device` now resolves the host part through the registry too, so it stops silently diverging onto the non-Tailscale route. New `resolveDeviceTarget`; `resolveSshTarget` shares one host-part matcher. Source: `apps/cli/src/lib/devices/resolve-target.ts`, `apps/cli/src/commands/ssh.ts`.
|
|
8
|
+
- **`agents sessions --host` searches the peer's whole index, not its login cwd.** A remote listing runs in the peer's SSH-login home dir and was silently cwd-scoped, so `sessions --host <box>` read as empty (`No sessions found for /home/<user>`) even when the box's index was full. `--host` now defaults to whole-index (`--all`) scope; an explicit path query / `--project` / `--since` / `--agent` filter still narrows on top. It also runs the peer once, for itself (`AGENTS_SESSIONS_LOCAL=1`), so it no longer re-sweeps the fleet and prints a spurious `<this-machine>: unreachable`. Source: `apps/cli/src/lib/session/remote.ts`, `apps/cli/src/commands/sessions.ts`.
|
|
9
|
+
- **`agents devices sync` pins each device's login user.** Tailscale status carries a node's OS + address but not the account you ssh in as, so sync now materializes the local operator's username onto newly-synced devices (never clobbering a user you pinned). This makes `--host <device>` dial the same account no matter which machine launches the fan-out, instead of leaning on ssh's implicit local-username default. Source: `apps/cli/src/lib/devices/sync.ts`.
|
|
10
|
+
|
|
11
|
+
## 1.20.53
|
|
12
|
+
|
|
13
|
+
- **`agents add <agent>@latest` resolves to a concrete version before installing (no install race).** `latest` (like `oldest`) is now resolved via `npm view` up front and installed as a pinned spec directly into `versions/<agent>/<version>/`. Previously `latest` installed into a shared, well-known `versions/<agent>/latest/` scratch dir and was renamed to the real version only after npm finished — so a concurrent `agents view` reconcile (`reconcileStaleLatestForAgent`) or a second `latest` install could rename that dir out from under npm mid-extraction, corrupting the install with `ENOENT` on the seeded `package.json`. A concrete dir per version has no shared name to race on. Source: `apps/cli/src/lib/versions.ts`.
|
|
14
|
+
- **Native memory sync preserves unmanaged Markdown files (RUSH-1621).** Sync tracks managed fact names in `.agents-cli-memory.json` and only deletes those; user-authored `*.md` under the agent memory dir survive. Source: `apps/cli/src/lib/memory.ts`.
|
|
15
|
+
- **Feed high-consequence authz uses the canonical operator registry (RUSH-1618).** `recordAnswer` no longer looks for `operators.yaml` under the feed root; it resolves operators from `~/.agents/` via `loadOperators()`. Source: `apps/cli/src/lib/feed.ts`.
|
|
16
|
+
- **Menu bar groups worktree sessions under the real repo name (RUSH-1635).** Paths under `.agents/worktrees/<slug>` use the enclosing repository directory as the grouping key instead of the worktree slug. Source: `apps/cli/menubar/.../LocalState.swift`.
|
|
17
|
+
|
|
18
|
+
- **PR outcome keys include repository identity (RUSH-1630).** Full GitHub pull URLs normalize to `owner/repo#N` so two repos' PR #10 no longer collide under `pr:#10`. Source: `apps/cli/src/lib/feed-outcome.ts`.
|
|
19
|
+
|
|
20
|
+
- **Urgent OpenClaw notifications use `--target` and `--message` (RUSH-1620).** `openclaw message send` requires a destination and the `--message` flag (not `--text`); without `--target` the send was invalid. Source: `apps/cli/src/lib/notify.ts`.
|
|
21
|
+
- **High-consequence answers require env-proven operator identity (RUSH-1619).** `agents message --as <id>` alone is not verification; `AGENTS_OPERATOR_ID` must match the claimed id (and the id must be in `operators.yaml`). Source: `apps/cli/src/lib/operator.ts`, `apps/cli/src/commands/message.ts`.
|
|
22
|
+
- **Hermes and ForgeCode are first-class install targets (RUSH-559).** `AgentId` now includes `hermes` and `forge`, with resource capability metadata for skills, rules, and MCP. MCP sync writes Hermes `mcp_servers` YAML in `~/.hermes/config.yaml` and ForgeCode `mcpServers` JSON in `~/.forge/.mcp.json`, so registering these targets no longer requires their CLIs to be installed first. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/mcp.ts`, `apps/cli/src/lib/resources/mcp.ts`.
|
|
23
|
+
- **Cloud tasks can now report a resumable `idle` status (RUSH-601).** Provider-side stopped states such as Rush `idle`/`paused`/`needs_review`, Codex `paused`/`needs_review`, and Antigravity `idle`/`paused` normalize to canonical `idle`; stream output, `agents sessions`, and `agents cloud list/status` render idle as an idle state instead of falling through to queued or unknown-status output. Source: `apps/cli/src/lib/cloud/types.ts`, `apps/cli/src/lib/cloud/stream.ts`, `apps/cli/src/lib/session/active.ts`, `apps/cli/src/commands/cloud.ts`.
|
|
24
|
+
- **OpenCode plugin install only writes loader-visible direct `.ts`/`.js` files (RUSH-1617).** Drop nested and `.mjs`/`.cjs` installs that OpenCode never scans; multi-module plugins flatten into `~/.config/opencode/plugins/`. Source: `apps/cli/src/lib/plugins.ts`.
|
|
25
|
+
- **Routine credit-failover scans only the current attempt's log (RUSH-1616).** Each failover spawn writes `stdout.attempt-N.log` and rate-limit detection uses that file alone; prior attempts still append into `stdout.log` for the continuous trail. Source: `apps/cli/src/lib/runner.ts`.
|
|
26
|
+
- **Cursor hook sync drops stale managed entries when matcher/event change (RUSH-1615).** GC keys managed hooks by `event|command|matcher` instead of command path alone, so a matcher or event edit no longer leaves dead entries. Source: `apps/cli/src/lib/hooks.ts`.
|
|
27
|
+
- **Stop advertising Goose SubagentStart/SubagentStop hooks (RUSH-1613).** Goose does not emit those events; drop them from `GOOSE_EVENT_MAP` so sync no longer installs dead entries. Source: `apps/cli/src/lib/hooks.ts`.
|
|
28
|
+
- **Mailbox delivery receipts are monotonic (RUSH-1614).** `recordMessageReceipt` no longer lets a late `queued` write overwrite an already-recorded `consumed`/`continued` when enqueue races the drain. Source: `apps/cli/src/lib/feed.ts`.
|
|
29
|
+
- **Per-session rate-limit detection + feed badge (RUSH-1523).** The session state engine flags rate/usage-limit text in the transcript (`detectRateLimited`); `ActiveSession.rateLimited` flows through remote fan-out into Factory's `FloorAgent.rateLimited`, which renders a **rate limited** pill on the feed card. Source: `apps/cli/src/lib/session/state.ts`, `apps/factory/.../floorAdapter.ts`, `FeedItem.tsx`.
|
|
30
|
+
- **Kiro launches with `--v3` so standalone hooks actually fire (RUSH-1612).** Agents-cli writes Kiro hooks as v3 standalone files under `~/.kiro/hooks/*.json`, but those only load on the v3 engine. `AGENT_COMMANDS.kiro.base` now includes `--v3` so `agents run kiro` opts into the engine that reads them. Source: `apps/cli/src/lib/exec.ts`.
|
|
31
|
+
|
|
32
|
+
- **Ask classifier + stall suppression for the agent feed (RUSH-1477).** Every open block is classified as Decision / Approval / Clarification / Stall / Fyi. Workflow-stalls ("should I…?", "what's next?", "looks good?") are auto-answered and removed so they never render as cards; Decisions and Approvals still surface. `agents feed` reports a digest (`N stalls auto-resolved by policy`); `--all` shows suppressed items; `--json` stamps each block with its `ask` classification. Agent-tagged `blockClass: decision` is never auto-suppressed. Source: `apps/cli/src/lib/ask-classifier.ts`, `apps/cli/src/commands/feed.ts`.
|
|
33
|
+
- **Parked-agent answer router: PTY-select / resume / mailbox by runtime (RUSH-1474).** `agents message` no longer always enqueues to the mailbox. When the target is parked on an open feed question, delivery routes by runtime: tmux/iterm/pty rails get keystrokes that select the matching option label (or free-text via Other); headless parked runs resume via `agents run --resume <id> -- <answer>`; running agents still use the mailbox. Wrong-state delivery is refused with a clear error instead of silently rotting in the spool. Source: `apps/cli/src/lib/answer-router.ts`, `apps/cli/src/commands/message.ts`.
|
|
34
|
+
- **Wire subagents support for OpenCode.** OpenCode loads agent markdown from `~/.config/opencode/agents/` with frontmatter `mode: subagent`. Flip `subagents: true`, add `transformSubagentForOpenCode`, wire writer/detector/list/diff/remove. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/subagents.ts`. (RUSH-1386)
|
|
35
|
+
- **`agents feed` groups by outcome (ticket/PR/worktree), not by agent (RUSH-1479).** The default human view collapses open blocks under the deliverable they serve — Linear ticket first, then PR, then worktree/epic, then a shared Unassigned bucket — so a 1,100-agent fleet reads as dozens of initiatives (`RUSH-1125 · 4 agents · 1 needs you`). Each block is attributed to exactly one outcome; live session meta fills missing ticket/PR/worktree at list time; `--flat` restores the per-agent list; `--json` stamps each block with its `outcome` ref. Factory Floor's Group control gains an Outcome axis (the new default). Source: `apps/cli/src/lib/feed-outcome.ts`, `apps/cli/src/commands/feed.ts`, `apps/factory/ui/settings/components/mission-control/floorModel.ts`.
|
|
36
|
+
- **Menu bar dropdown redesigned around triage: attention floats up, context groups down.** The dropdown used to stack ~11 flat sections at equal weight, so a session waiting on you sat as loud as setup noise. Now: a **⚠ NEEDS YOU strip on top**, sorted by wait-time across all projects (most-stalled first), each row carrying the actual question the session is waiting on plus how long it's waited (`Claude · agents-cli — Claude needs your permission to use Bash · 2h 25m`); **live work grouped by repo** below (`ACTIVE · <repo>` headers, rich rows show the session's own title inline); **ROUTINES expanded** into a glanceable section (next few upcoming + any failing routine inline, `All routines…` for the rest); RECENT TICKETS and RECENT stay dedicated sections; **Setup + Auto-nudge collapse into one System row** (submenu keeps the doctor items and the auto-nudge toggle). A **density toggle** in the footer cycles Auto → Rich → Compact — compact folds rows to one-liners and tucks Recent behind a submenu; Auto (default) is rich while something needs you, compact on a calm machine (`menubarDensity` in UserDefaults, `MENUBAR_DENSITY` env override for dump probes). The question text + wait-time come from the attention sentinel: the Notification hook now writes the notification message as the sentinel content (phnx-labs/.agents-system#74), and the helper reads content + mtime (`LocalState.attentionMarks`); an empty sentinel still renders as "awaiting input". `Session` gained `title`/`question`/`attentionSinceMs`; terminal rows group by working-dir name and carry the live-terminal label as the title. Source: `apps/cli/menubar/Sources/MenubarHelper/StatusItemController.swift`, `apps/cli/menubar/Sources/MenubarHelper/LocalState.swift`.
|
|
37
|
+
- **Wire allowlist support for OpenCode.** OpenCode stores per-tool allow/ask/deny rules in `opencode.json`/`opencode.jsonc` under `permission` (bash patterns etc.; present since ~1.1.1). Flip `allowlist: { since: '1.1.1' }` so the existing `convertToOpenCodeFormat` / `applyPermissionsToVersion` / detector path actually runs. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/permissions.ts`. (RUSH-1385)
|
|
38
|
+
- **Wire subagents support for Grok CLI.** Grok discovers agent definitions as Claude-compatible `.md` files under `~/.grok/agents/` (docs: user-guide/16-subagents.md). Flip `subagents: true`, reuse the Claude flatten transform for install/writer paths, and register a detector plus list/diff/remove. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/subagents.ts`, `apps/cli/src/lib/staleness/writers/subagents.ts`, `apps/cli/src/lib/staleness/detectors/subagents.ts`. (RUSH-1384)
|
|
39
|
+
- **Wire subagents support for Kimi CLI.** Kimi Code loads custom agents as YAML under `~/.kimi-code/agents/*.yaml` with a sibling `*.system.md` referenced via `system_prompt_path` (Kimi has no inline `system_prompt` field) and a managed parent `_agents-cli.yaml` that declares `agent.subagents` for `--agent-file`. Flip `subagents: true`, add `transformSubagentForKimi` / `writeKimiSubagentFiles`, list/diff/remove paths, and wire the subagents writer + detector (underscore-prefixed parent excluded from the installed name list). Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/subagents.ts`, `apps/cli/src/lib/staleness/writers/subagents.ts`, `apps/cli/src/lib/staleness/detectors/subagents.ts`. (RUSH-1383)
|
|
40
|
+
- **Grok + Antigravity now register dir-form subrule hooks.** The hooks writer excluded `grok` from `registerHooksToSettings`, so subrule-bundled guards (absolute paths outside the central hooks copy set) never reached `~/.grok/hooks/hooks.json`. Grok is now in the gate. Antigravity entries carry `matcher` so guards are tool-scoped. Source: `apps/cli/src/lib/staleness/writers/hooks.ts`, `apps/cli/src/lib/hooks.ts`. (RUSH-1353)
|
|
41
|
+
- **Menu-bar Quick Dispatch can now pick agents and fan out autonomous fixes from the screenshot panel.** `Cmd-Shift-O` still supports filing one Linear ticket, but the panel now has a File Ticket / Fix mode control plus a roster picker sourced from the menu-bar agent list. File Ticket runs the selected ticket agent; Fix dispatches every selected agent with `agents run <agent> --mode auto --name quick-<agent>-<timestamp>`, carrying the typed note and selected screenshots into a repo-discovery prompt so those runs surface in normal session/tray views instead of hidden background work. `AGENTS_QUICK_DISPATCH_ROSTER=claude,codex` filters visible agents and `AGENTS_QUICK_DISPATCH_AGENTS=claude,codex` preselects them. Source: `apps/cli/menubar/Sources/MenubarHelper/{LocalState,PromptPanel,AgentsCLI,IssueSelfTest}.swift`, `apps/cli/docs/menubar.md`. (RUSH-1416)
|
|
42
|
+
- **Menu-bar Quick Dispatch keeps immediate typing in the capture field.** The `Cmd-Shift-O` quick-capture panel now uses an activating borderless window, orders it front, and waits briefly for the field editor to become ready before returning from summon, so notes typed immediately after summon no longer lose their leading characters to the previously focused app. Source: `apps/cli/menubar/Sources/MenubarHelper/PromptPanel.swift`. (RUSH-1591)
|
|
43
|
+
- **Internal: consolidated the drifted provider status-normalizers and git-root helpers (#753).** The three copies of `mapStatus` in `cloud/{rush,codex,antigravity}.ts` — which had drifted (different vocabularies and defaults) — collapse into one exported `normalizeProviderStatus(provider, wireStatus)` in `cloud/types.ts`, with provider-specific defaults still explicit (rush default `running`, codex default `running`, antigravity `undefined`-safe default `completed`); factory's structurally-different `mapResultStatus` is left in place. `getGitRoot` moves to `lib/git.ts` and `commands/worktree.ts` now calls it instead of a private `gitRoot` copy; the two divergent `isGitRepo` variants (synchronous root-only in `git.ts` vs async worktree-correct in `teams/worktree.ts`) are documented and deliberately not merged. Source: `apps/cli/src/lib/cloud/{types,rush,codex,antigravity}.ts`, `apps/cli/src/lib/git.ts`, `apps/cli/src/lib/teams/worktree.ts`, `apps/cli/src/commands/worktree.ts`. (#753)
|
|
44
|
+
- **macOS installs now run a Developer-ID-signed + notarized `agents` binary — the primary fix for EDR (CrowdStrike Falcon) blocking the CLI.** The resolved `agents` on macOS was a node-shebang JS file in a user-writable path; unsigned and spawned by editor/Electron children, it matched Falcon's post-exploitation profile and the sessions feature silently died on EDR-enabled Macs (mitigation 1 of #315; the behavioral hardening, mitigations 2-4, shipped earlier). Releases now build a standalone arm64 Mach-O with `bun build --compile` (`scripts/build-bin.sh`), sign it with Developer ID + hardened runtime + the JIT entitlement bun's JavaScriptCore needs under the hardened runtime (`scripts/sign-cli-binary.sh`, `scripts/bun-jit-entitlements.plist`), notarize it with `notarytool`, and ship it in the npm tarball at `dist/bin/agents`. `postinstall` points the alias shims and the `~/.local/bin/agents`/`ag` links at the signed binary — with a run-probe that falls back loudly to the JS entrypoint if the binary is missing, wrong-arch, or blocked — and repoints links an earlier install left at the JS shim. A `prepack` gate (`scripts/verify-cli-binary.sh`) refuses to pack unless the binary matches its sign-run sha pin, embeds the release version, and (on macOS) passes `codesign --verify` with a Developer ID authority. Linux-driven releases build + sign the binary on the mac sign host via `scripts/remote-sign-mac.sh`. Intel Macs and non-mac platforms keep the JS entrypoint. (#315)
|
|
45
|
+
- **Codex multi-file apply_patch now surfaces every path.** `parseCodex` used `.match()` on the patch body so only the first `*** Update/Add/Delete File:` path became a tool_use; files 2+ were invisible to artifact discovery. Now `applyPatchTargetPaths` uses `matchAll` and emits one Edit event per file. Source: `apps/cli/src/lib/session/parse.ts`. (RUSH-1410)
|
|
46
|
+
- **`memory` is a first-class top-level resource (distinct from `rules`).** `agents memory list|add|remove|view|sync` manages portable knowledge facts under `~/.agents/memory/` (project > user > system; `MEMORY.md` index + one `<slug>.md` per fact). The legacy `agents memory` → `rules` tombstone is gone. Capable agents (claude, codex, openclaw, grok) get facts fanned into version homes on `syncResourcesToVersion` / `agents memory sync`. Plugins can ship a `memory/` dir (surfaced in `pluginResourceGroups`). Note: internal `ResourceSelection.memory` still means the composed *rules* file — rename to `rules` is a follow-up. Source: `apps/cli/src/lib/memory.ts`, `apps/cli/src/commands/memory.ts`, `apps/cli/src/lib/resources/memory.ts`, `apps/cli/src/lib/versions.ts`, `apps/cli/src/lib/plugins.ts`. (RUSH-1330)
|
|
47
|
+
- **`agents sessions --active --json` now carries `tokPerSec`, and `agents sessions --roots --json` emits the session-scan directories — one CLI contract the Factory extension consumes instead of re-implementing (issue #741).** Every active row gained `tokPerSec`: live output-token throughput over a rolling 60s window from the transcript tail (Claude assistant `output_tokens`; Codex `token_count` output + reasoning; Gemini output + thoughts), absent when the session is idle or its format reports no usage. The new `agents sessions --roots --json` prints, per on-disk agent, the exact directories the CLI scans for transcripts (every version home + backup mirror), so an external watcher (the Factory Floor's `fs.watch`) tracks the same paths the CLI does instead of hardcoding `~/.claude|.codex|.gemini` — add an on-disk agent to discovery and every consumer watches it automatically. The throughput math and the roots list are now the single source of truth; the extension used to keep parallel copies. Source: `apps/cli/src/lib/session/throughput.ts` (`computeTokPerSec`), `apps/cli/src/lib/session/active.ts` (`ActiveSession.tokPerSec`, `computeLiveSignals`), `apps/cli/src/lib/session/tail.ts` (`readSessionTailWithRaw`), `apps/cli/src/lib/session/discover.ts` (`getSessionRoots`), `apps/cli/src/commands/sessions.ts` (`--roots`).
|
|
48
|
+
- **Fix: `agents sessions --active` no longer attaches stale Codex transcripts to live processes.** Codex session lookup now sorts by indexed `last_activity` and refuses to borrow a transcript outside an explicit 24-hour freshness bound, so a desktop app service or unrelated long-lived process cannot light up a months-old session as `running`. Source: `apps/cli/src/lib/session/active.ts`, `apps/cli/src/lib/session/db.ts`. (RUSH-1489)
|
|
49
|
+
- **Startup shim self-heal stays silent by default, with `--verbose` diagnostics on stderr.** The unified shim/shadow/PATH repair path no longer pollutes command stdout, including `agents sessions --active --json`; `agents --verbose <cmd>` now prints a concise startup self-heal summary to stderr for debugging. Source: `apps/cli/src/index.ts`, `apps/cli/src/lib/shim-heal.ts`. (RUSH-1533)
|
|
50
|
+
- **Wire subagents support for Codex CLI.** Codex custom agents are standalone TOML under `~/.codex/agents/*.toml` (required: `name`, `description`, `developer_instructions`; multi-agent plumbing since 0.117.0). Flip `subagents: { since: '0.117.0' }`, add `transformSubagentForCodex`, and wire the subagents writer + install/remove paths. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/subagents.ts`, `apps/cli/src/lib/staleness/writers/subagents.ts`. (RUSH-1382)
|
|
51
|
+
- **Routines use the same version/account selection and credit failover as `agents run`.** Scheduled jobs used to spawn a bare `claude`/`codex` name under a sandbox HOME, which could surface as `agents: no version of claude configured` even when installs existed, and never walked past a credit-exhausted default. The runner now resolves a healthy install via the configured run strategy (default `balanced`), pins the absolute binary, injects per-version config dirs + the daemon's `CLAUDE_CODE_OAUTH_TOKEN` into sandboxed spawns, and on foreground `agents routines run` re-dispatches to the next healthy same-agent account when a mid-run rate/usage limit is detected (daemon detached fires use the pre-flight pick only). Diagnostic lines log the pick, skipped accounts, and each failover hop. Source: `apps/cli/src/lib/runner.ts` (`resolveRoutineLaunch`, `pinJobBinary`, `buildRoutineSpawnEnv`), `apps/cli/src/lib/sandbox.ts` (OAuth allowlist), `apps/cli/src/commands/routines.ts` (help). (RUSH-1016)
|
|
52
|
+
- **Wire plugin support for Goose.** Goose loads Open Plugins from `$HOME/.agents/plugins/<name>/` (same layout as agents-cli). Flip `plugins: true` and copy each selected plugin into the version home under `.agents/plugins/`. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/plugins.ts` (`installGoosePlugin`). (RUSH-1339)
|
|
53
|
+
- **Wire plugin support for Cursor CLI.** Cursor agent plugins use `.cursor-plugin/plugin.json` (re-enabled 2026-05). Flip `plugins: true`, set `pluginManifestDir: '.cursor-plugin'`, and reuse the centralized marketplace mirror path under `~/.cursor/plugins/`. Source: `apps/cli/src/lib/agents.ts`. (RUSH-1338)
|
|
54
|
+
- **Wire plugin support for OpenCode.** OpenCode loads JS/TS plugin modules from `$HOME/.config/opencode/plugins/` (not Claude marketplace layout). Flip `plugins: true`, install modules from a plugin's `opencode/` or `plugins/` dir (or root) into the version home, and track install/remove via `isPluginSynced` / `removePluginFromVersion`. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/plugins.ts` (`installOpenCodePlugin`, `openCodePluginsDir`). (RUSH-1336)
|
|
55
|
+
- **Wire hooks support for Cursor CLI.** Cursor agent CLI (`cursor-agent`) gained lifecycle hooks on 2026-01-16 (`~/.cursor/hooks.json`, `{ "version": 1, "hooks": {…} }`). Flip the capability, map canonical events to Cursor camelCase (`SessionStart` → `sessionStart`, `UserPromptSubmit` → `beforeSubmitPrompt`, `Stop` → `stop`, …), and merge managed entries into `hooks.json` while preserving user-authored commands outside managed prefixes. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/hooks.ts` (`registerHooksForCursor`, `CURSOR_EVENT_MAP`), `apps/cli/src/lib/staleness/writers/hooks.ts`. (RUSH-1326)
|
|
56
|
+
- **Wire hooks support for Goose.** Goose (`block-goose-cli` ≥ 1.34.0) auto-discovers Open Plugins hooks at `$HOME/.agents/plugins/<name>/hooks/hooks.json`. Flip `supportsHooks` and gate `hooks: { since: '1.34.0' }`, write a managed plugin (`agents-cli-hooks`) under the version home with Claude-shaped event groups, and leave user plugins alone. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/hooks.ts` (`registerHooksForGoose`, `GOOSE_EVENT_MAP`), `apps/cli/src/lib/staleness/writers/hooks.ts`. (RUSH-1325)
|
|
57
|
+
- **Wire hooks support for Kiro CLI.** Kiro CLI v3 stores standalone hooks under `~/.kiro/hooks/*.json` (`{ "version": "v1", "hooks": [...] }` with command/agent actions); PreToolUse/PostToolUse firing was fixed in kiro-cli 0.10. Flip `supportsHooks` and gate `hooks: { since: '0.10.0' }`, map canonical events to Kiro PascalCase triggers, and write a single managed `agents-cli-hooks.json` on every sync (user-authored sibling files untouched). Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/hooks.ts` (`registerHooksForKiro`, `KIRO_EVENT_MAP`), `apps/cli/src/lib/staleness/writers/hooks.ts`. (RUSH-1324)
|
|
58
|
+
- **Wire hooks support for GitHub Copilot CLI.** Copilot GA (`@github/copilot` ≥ 1.x) ships a real hooks system (`~/.copilot/hooks/*.json`, schema `{ "version": 1, "hooks": {…} }`), but agents-cli still declared `hooks: false` so nothing ever installed. Flip the capability, map canonical events to Copilot camelCase (`SessionStart` → `sessionStart`, `PreToolUse` → `preToolUse`, `Stop` → `agentStop`, …), and write a single managed file (`agents-cli-hooks.json`) on every sync so GC is a rewrite and user-authored sibling JSON files are never touched. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/hooks.ts` (`registerHooksForCopilot`, `COPILOT_EVENT_MAP`), `apps/cli/src/lib/staleness/writers/hooks.ts`. (RUSH-1323)
|
|
59
|
+
- **`agents run --lease` now shows live progress instead of a 30-90s blank terminal.** After the runtime picker + consent, the lease used to go completely silent while the box provisioned (`crabboxWarmup` was a blocking `spawnSync`, so nothing could animate), then dump crabbox's raw sync/bootstrap log interleaved with the agent's output. Now a spinner animates each previously-silent phase — `Leasing a hetzner box… (Ns)` → `✔ Box <slug> ready (<ip>) · Ns` → `Setting up box — <latest step>` → `✔ Box provisioned — <agent> output:` — and the agent's own output then prints verbatim. The box bootstrap echoes a marker (`LEASE_AGENT_MARKER`) right before the agent run; `createLeaseOutputRouter` splits the crabbox stream on it so setup noise feeds the spinner while the agent output streams clean, and a setup failure dumps the captured setup log so errors are never swallowed. `crabboxWarmup` is now async so the event loop stays free to animate. The spinner is a purpose-built `createSpinner` (NOT `ora`): ora hooks `stream.write` and re-renders on every external write while spinning, which ballooned to multi-GB output when a lease streamed past a live spinner — `createSpinner` writes exactly one short line per fixed tick and nowhere else (a unit test asserts 100k `update()` calls produce zero writes), and on a non-TTY prints each phase label once and stays silent on update (no piped/CI flood). Verified live on Hetzner: animated warmup counter, clean `LOGIN_OK`, box auto-destroyed, 43KB total output. Source: `apps/cli/src/lib/crabbox/progress.ts` (`createSpinner`, `createLeaseOutputRouter`, `LEASE_AGENT_MARKER`), `apps/cli/src/lib/crabbox/{lease,cli}.ts`, `apps/cli/src/commands/exec.ts`.
|
|
60
|
+
- **Fix: Grok hooks now respect their `matcher` and no longer run twice per event.** The Grok hook registrar (`registerHooksForGrok`) dropped the manifest `matcher` when writing `~/.grok/hooks/`, so a matcher-scoped hook fired on every tool call — the plan-presentation hook (meant for `ExitPlanMode` only) hard-blocked whole Grok sessions (exit 2 = explicit deny). It also double-registered every hook into BOTH `hooks.json` AND a per-event file (`pretooluse.json`, …); Grok merges all of `~/.grok/hooks/*.json`, so each hook ran twice. Now the registrar emits `matcher` only for the events Grok accepts it on (`PreToolUse`, `PostToolUse`, `Notification` — lifecycle events reject it), groups hooks one-per-distinct-matcher like the Claude writer, translates tool names Grok doesn't auto-alias (`ExitPlanMode` → `ExitPlanMode|exit_plan_mode`, since Grok's plan tool is `exit_plan_mode`), and writes a single `hooks.json`, pruning the stale per-event files an older build left behind so already-synced installs stop double-running. Source: `apps/cli/src/lib/hooks.ts` (`registerHooksForGrok`, `isManagedGrokHookFile`, `GROK_MATCHER_EVENTS`, `GROK_MATCHER_ALIASES`).
|
|
61
|
+
- **Agent feed replies now have delivery confirmation and atomic first-answer-wins closure (RUSH-1476).** `agents message` ties a local reply to the agent's current open feed block; if any surface already answered that block, the second attempt is rejected with the surface that won. The feed block records queued → consumed → continued receipts: `queued` when `agents message` enqueues, `consumed` when the mailbox drain archives the message, and `continued` once the agent continues past the block. A human answer typed directly in the terminal is reconciled via the `UserPromptSubmit` hook, which records a terminal answer and removes the visible block within one feed poll cycle. Source: `apps/cli/src/lib/feed.ts` (`recordAnswer`, `recordMessageReceipt`, `recordContinued`, answered-marker lifecycle), `apps/cli/src/lib/mailbox.ts` (`blockId` on `MailboxMessage`, consumed-receipt surfacing), `apps/cli/src/commands/message.ts` (open-block lookup + `--surface`), `apps/cli/src/commands/feed.ts` (receipt rendering).
|
|
62
|
+
- **24/7 multi-operator controls for agent feed (RUSH-1480).** Blocks can now carry `blockClass` (`approval`/`decision`), `consequence` (`normal`/`high`), `allowedOperators`, `timeoutMinutes`, `safeDefault`, and `costOfDelay`; the feed-publish hook captures these from the agent's `AskUserQuestion` tool_input. High-consequence answers require a verified operator id known to `~/.agents/operators.yaml` (`agents message --as <operator>`). `agents feed --dispatch` evaluates default-on-no-answer policy loaded from `~/.agents/feed-policy.yaml`: approval blocks resolve to their safe default after the timeout, decision blocks are hard-parked. Urgent blocks (`costOfDelay` at or above the phone threshold) are paged once via the OpenClaw Telegram gateway (`openclaw message send --channel telegram --account default`). Source: `apps/cli/src/lib/operator.ts`, `apps/cli/src/lib/feed-policy.ts`, `apps/cli/src/lib/notify.ts`, `apps/cli/src/lib/feed.ts` (block metadata + authz + parked/defaulted/notified timestamps), `apps/cli/src/commands/message.ts` (`--as`), `apps/cli/src/commands/feed.ts` (`--dispatch`, metadata rendering).
|
|
63
|
+
- **Mailbox TTL + liveness/GC so messages never rot in dead-agent inboxes (RUSH-1475).** `enqueue` accepts an optional `ttlSeconds`; expired messages are archived to `consumed/` with `dropped: expired` instead of being returned by `drain`/`peek`. `agents feed --dispatch` runs a liveness sweep against `getActiveSessions()`: boxes whose owning agent is no longer alive are treated as dead, their pending messages are archived with `dropped: dead`, and any feed block tied to that mailbox is removed so the operator never answers a ghost. Old `consumed/` entries are pruned after 24 hours by default. Source: `apps/cli/src/lib/mailbox.ts` (`expiresAt`, `isExpired`, `sweepExpired`), `apps/cli/src/lib/mailbox-gc.ts`, `apps/cli/src/commands/feed.ts` (`--dispatch` liveness sweep).
|
|
64
|
+
- **Keychain service names are no longer silently enumerable — items are stored under opaque HMAC-hashed names (macOS).** The helper's `list` never decrypts and never prompts (that's what keeps `agents secrets list` snappy), which meant the service names themselves — `agents-cli.secrets.<bundle>.<KEY>`, `agents-cli.bundles.<name>`, `agents-cli.<provider>.token` — were readable metadata: any same-user process could silently inventory your bundles, keys, and providers before ever popping Touch ID. Every `agents-cli.*` item now lives under an opaque name (`agents-cli.h.<ns>.m` for bundle metadata, `agents-cli.h.<ns>.k.<kh>` for values, `agents-cli.h.o.<ih>` otherwise) keyed by a per-machine random HMAC key (`agents-cli.hmackey`, no-ACL so silent operations stay silent). The structured shape keeps a bundle's items under one hashed prefix, so `secrets exec`/`run --secrets` still resolve metadata + all values behind a single Touch ID. A one-time re-key migrates existing items — automatically on the first interactive keychain use, or via the new `agents secrets rekey` (`--status` to inspect, exit 4 on a cancelled Touch ID; re-running resumes). It is crash-safe end to end: values are batch-read once, hashed copies are written and verified before ANY original is deleted, activation is all-or-nothing, and an interrupted delete phase is finished silently by the next run. A `--prefix`-restricted (partial) run resolves each value item's bundle tier directly from the keychain — scoping a `never`-policy bundle's value items without its metadata item keeps the silent no-ACL tier intact. The signed Swift helper is untouched (it already treats service names as opaque), so no re-notarization or sha re-pin. Caveat: an older agents-cli on the same machine writes/reads cleartext names and won't see re-keyed items — keep all installs current. Source: `apps/cli/src/lib/secrets/index.ts` (`hashedServiceName`, `rekeyServiceNames`), `apps/cli/src/lib/secrets/bundles.ts`, `apps/cli/src/commands/secrets-migrate.ts`, `apps/cli/docs/secrets.md`. (GitHub #316, Finding 1)
|
|
65
|
+
|
|
5
66
|
## 1.20.52
|
|
6
67
|
|
|
7
68
|
- **Re-pinned the keychain helper to a freshly notarized build carrying the new iCloud verbs.** The #904 session changed `keychain-helper.swift` (legacy iCloud/synchronizable item verbs) and re-pinned the sha, but the notarized binary it pinned lived only in that session's since-removed worktree — the release prepack gate (`verify-keychain-helper.sh`) then failed on every machine (`SHA256 mismatch`) because no reachable `bin/Agents CLI.app` matched the pin. Rebuilt the helper from current source on the release machine (universal, signed, notarized: Gatekeeper `Notarized Developer ID`, submission accepted) and pinned that binary. Same class of fix as #835.
|
|
8
69
|
- **Fix: version-skewed CLI invocations no longer wipe the secrets-agent's hot cache — the recurring Touch ID storm on version-churning machines is closed.** `ensureAgentRunning` tore down a reachable broker whenever the broker's running version differed from the caller's on-disk version — unconditionally, via `launchctl kickstart -k`, wiping every unlocked bundle. On a machine where installed versions churn (dev builds stamp a fresh `0.0.0-dev.<sha>` per install; an npm copy and a dev copy invoke in turn), that meant constant wipes, and the next read of *each* held bundle popped a fresh Touch ID prompt — the exact storm #435 fixed on the server side, reintroduced client-side. The client now accepts a protocol-compatible, version-skewed broker while it holds real unlocks (`shouldTeardownVersionSkewedBroker`); the broker's own sweep still adopts new code at the next quiet moment (store empty), so upgrades land without ever costing a re-prompt. Source: `apps/cli/src/lib/secrets/agent.ts` (`ensureAgentRunning`, `shouldTeardownVersionSkewedBroker`).
|
|
9
70
|
- **Fix: mutating a bundle now evicts the broker-held copy — `rotate` no longer serves the old secret for up to 7 days.** Only `secrets policy` invalidated the secrets-agent after a write; `add`, `rotate`, `remove`, `rename`, `delete`, and `import` updated the keychain while a broker-held snapshot kept serving the pre-write values for the rest of the ~7d hold — a rotated credential silently kept injecting the OLD value into every run. `writeBundle` and `deleteBundle` now evict the bundle from the broker after every mutating write (`agentEvictSync`, a synchronous socket client mirroring the read fast-path), so the next read re-resolves fresh from the keychain (one prompt) and re-caches. The usage-telemetry stamp (`stampLastUsed`, fired on every broker HIT) opts out — evicting there would make the cache destroy itself on first use — and the eviction honors `AGENTS_SECRETS_NO_AGENT` plus the test-backend override so suites never evict a user's real unlocks (`shouldEvictAfterBundleWrite`). The `policy` command's bespoke eviction is superseded by the chokepoint. Source: `apps/cli/src/lib/secrets/bundles.ts` (`writeBundle`, `deleteBundle`, `shouldEvictAfterBundleWrite`), `apps/cli/src/lib/secrets/agent.ts` (`agentEvictSync`), `apps/cli/src/commands/secrets.ts`.
|
|
10
|
-
- **`agents secrets import` gains a unified `--from <source>` and recovers bundles stranded in the iCloud Keychain.** One axis for every source: a .env path (`-` reads stdin), `1password:<vault>` (the boolean `--from-1password --vault <name>` pair still works as a hidden deprecated alias), and the new `icloud`. Bundles created in the pre-biometry era were synced via iCloud Keychain; the device-local cutover pinned every query to `kSecAttrSynchronizable: false`, which orphaned those items — visible in Keychain Access under iCloud, invisible to `secrets list` and `migrate-acl`. `agents secrets import --from icloud` discovers them (bundle metadata *and* bare per-key secret items whose metadata never synced), offers an interactive multi-select (or takes an explicit bundle name for non-interactive use), re-imports them as normal device-local biometry-gated bundles, and with `--purge` deletes the
|
|
11
|
-
- **`agents feed` surfaces every top-level agent
|
|
71
|
+
- **`agents secrets import` gains a unified `--from <source>` and recovers bundles stranded in the iCloud Keychain.** One axis for every source: a .env path (`-` reads stdin), `1password:<vault>` (the boolean `--from-1password --vault <name>` pair still works as a hidden deprecated alias), and the new `icloud`. Bundles created in the pre-biometry era were synced via iCloud Keychain; the device-local cutover pinned every query to `kSecAttrSynchronizable: false`, which orphaned those items — visible in Keychain Access under iCloud, invisible to `secrets list` and `migrate-acl`. `agents secrets import --from icloud` discovers them (bundle metadata *and* bare per-key secret items whose metadata never synced), offers an interactive multi-select (or takes an explicit bundle name for non-interactive use), re-imports them as normal device-local biometry-gated bundles, and with `--purge` deletes only the iCloud copies whose value provably lives locally — imported this run or already present in the local bundle; an unreadable item, a key the modern store refuses by policy (reserved/loader env names the pre-cutover store accepted — reported as `reserved, not importable` instead of aborting the bundle), and the metadata item of a partially-recovered bundle all survive. `secrets view <name>` on a missing bundle now points at the recovery command when an iCloud copy of that name exists. Three new keychain-helper verbs (`list-synced`, `get-batch-synced`, `delete-synced`) match synchronizable items exclusively, so the live device-local store is untouchable from the recovery path. Source: `apps/cli/src/lib/secrets/icloud-import.ts`, `apps/cli/src/lib/secrets/keychain-helper.swift`, `apps/cli/src/commands/secrets.ts` (`parseImportSource`).
|
|
72
|
+
- **`agents feed` surfaces every top-level agent block that is waiting on the user across the fleet.** `AskUserQuestion` and waiting `Notification` hooks publish atomic open-block records with the full question/options or notification message, mailbox/session identity, host, and runtime; answer/resume/stop hooks remove resolved blocks, and Task subagents are gated out so internal questions do not flood the operator view. A bare `agents feed` merges local blocks with every registered online device in parallel, `--host`/`--device` scopes the fleet, `--local` skips SSH, and `--json` returns the same merged view. Runtime-managed hooks install from the CLI-writable user layer without dirtying the auto-pulled system repository. Source: `apps/cli/src/commands/feed.ts`, `apps/cli/src/lib/feed.ts`, `apps/cli/src/lib/remote-agents-json.ts`, runtime labelling in `apps/cli/src/lib/exec.ts` and `apps/cli/src/lib/teams/agents.ts`. (RUSH-1473)
|
|
12
73
|
- **Browser-profile credentials: account identity, `secrets get <bundle> <KEY>`, and `browser type --secret` for leak-free login.** `agents browser profiles logins` now shows, per profile, the account signed into each live service (plaintext username from Chromium `Login Data` — never decrypts the encrypted password) and whether login creds are declared in the profile's secrets bundle (columns `SERVICE | ACCOUNT | CREDS`); `profiles show` gains a `Logins:` block. `agents secrets get <bundle> <KEY>` prints one resolved value from a bundle (arg-count overload of the existing raw `get <item>`; ungated like it, and the `secrets.get` audit event fires inside the resolver). `agents browser type <ref> --secret <bundle>/<KEY>` resolves a credential in-process and types it into the page — the value never crosses stdout or the agent transcript — so an agent can drive a login by composing `profiles logins` → `browser start <loginUrl>` → `refs` → `type --secret` → `screenshot`, handling 2FA/selectors itself (no fragile CLI auto-login engine; Google/X block automation anyway). A profile's `--secrets` bundle is the credential store, keyed by the `<PREFIX>_USERNAME`/`<PREFIX>_PASSWORD` convention (per-service prefixes in `AUTH_SIGNATURES`); `profiles create --secrets` now warns if the bundle doesn't exist yet. Cookie-persistence-first remains the headline (the `browser` skill's credential guidance was corrected — the bundle only injected env vars into the browser process before, inert for web login). Source: `apps/cli/src/lib/browser/login-detection.ts`, `apps/cli/src/lib/browser/secret-ref.ts`, `apps/cli/src/commands/browser.ts`, `apps/cli/src/commands/secrets.ts`.
|
|
13
74
|
- **`agents logs audit` / `agents logs stats` / `agents logs rotate` — user-facing audit trail viewer.** The append-only local event log (`~/.agents/events.jsonl`) is now a first-class audit surface. `agents logs audit` queries events with filters (`--module`, `--command`, `--event`, `--agent`, `--caller`, `--level`, `--since`, `--limit`, `--json`) and `--follow` for live tailing; `agents logs stats` shows aggregate breakdowns by level, event type, module, and user; `agents logs rotate` prunes old numbered archives (`--days`, default 7). Events carry `level` (audit/warn/info/debug) and an environment-derived `caller` (`claude-code`, Factory agent kind, terminal, or script). Sensitive flag values, secret-shaped payload fields, token-like strings, and raw prompts are redacted before append. Security-relevant operations (secrets, teams lifecycle, cloud dispatch) auto-classify as `audit`. At 10 MB the active file rotates losslessly through `events.1.jsonl.gz`, `events.2.jsonl.gz`, and so on; `query()` reads every archive transparently. New instrumentation in `cloud.ts`, `factory.ts`, `teams.ts`, `secrets.ts`, `mcp.ts`, and `rotate.ts`. Source: `apps/cli/src/lib/events.ts`, `apps/cli/src/commands/logs.ts`, instrumentation call sites. (RUSH-460)
|
|
14
75
|
- **`agents run claude --lease` now runs the box logged-in — the Claude OAuth token ships alongside the config.** The lease copied `~/.claude.json` (config/account-metadata) but never the OAuth token, so Claude booted "Not logged in" on every leased box. The token lives in the macOS Keychain (hash-suffixed service for an agents-cli managed home, bare for a default install) and on Linux at `~/.claude/.credentials.json`. `resolveClaudeCredentialsBlob()` now reads the raw wrapped Keychain payload **silently** (`/usr/bin/security … -w` — Claude's item trusts it, no Touch ID): bare service first, then enumerate installed version homes, preferring the account whose email matches the copied config; off-darwin it reuses the existing `.credentials.json` file branch. `buildCredentialScript` writes that blob to `~/.claude/.credentials.json` (0600) via the same quoted-heredoc that carries every other cred (the box's `~/.claude` is a symlink into the versioned home, so it lands exactly where the shim's `CLAUDE_CONFIG_DIR` reads it); it is shredded after the run **regardless of `--keep-box`**. Resolved in the command layer after the existing per-run consent prompt, whose text now names the token explicitly. Scope is Claude-only — Codex/Grok already ship their token in the copied auth file. Verified live on Hetzner: a leased box ran `agents run claude` and returned a real model reply (`LOGIN_OK`, exit 0), and the token file was absent (shredded) afterward. Source: `apps/cli/src/lib/crabbox/runtimes.ts` (`resolveClaudeCredentialsBlob`, `buildCredentialScript`), `apps/cli/src/lib/crabbox/lease.ts`, `apps/cli/src/commands/exec.ts`.
|
package/README.md
CHANGED
|
@@ -238,6 +238,18 @@ agents message tester "also cover the null case"
|
|
|
238
238
|
|
|
239
239
|
`agents message <target> <text>` reaches any running agent by name or id -- a live local run, a teammate, a loop agent, or a cloud task -- and the text lands at its next tool call. Tag the sender with `--from <who>`.
|
|
240
240
|
|
|
241
|
+
### See every open block
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
agents feed # grouped by outcome (ticket/PR/worktree) across the fleet
|
|
245
|
+
agents feed --flat # one row per agent (legacy)
|
|
246
|
+
agents feed --host mac-mini # scope the view to one or more hosts
|
|
247
|
+
agents feed --local # skip the SSH fan-out
|
|
248
|
+
agents feed --json # blocks stamped with their outcome key
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Top-level questions and waiting notifications publish one atomic open-block record per session, including the mailbox id, host, runtime, and every answer option. The default view collapses agents under the **outcome** they serve (Linear ticket, PR, worktree slug, or Unassigned) so a 1,100-agent fleet reads as dozens of deliverables. Answered, resumed, and stopped blocks clear automatically; Task subagents are excluded. The rendered reply command uses the same mailbox id with `agents message`, so the decision routes back to the agent that asked it.
|
|
252
|
+
|
|
241
253
|
### Auto-nudge stalls
|
|
242
254
|
|
|
243
255
|
```bash
|
package/dist/bin/agents
ADDED
|
Binary file
|
package/dist/commands/cloud.d.ts
CHANGED
|
@@ -6,5 +6,8 @@
|
|
|
6
6
|
* All tasks are tracked locally in a SQLite database for cross-provider listing.
|
|
7
7
|
*/
|
|
8
8
|
import type { Command } from 'commander';
|
|
9
|
+
/** Print an error message to stderr and exit. */
|
|
10
|
+
/** Return a chalk color function appropriate for the given task status. */
|
|
11
|
+
export declare function statusColor(status: string): (s: string) => string;
|
|
9
12
|
/** Register the `agents cloud` command tree (run, list, status, logs, cancel, message, providers). */
|
|
10
13
|
export declare function registerCloudCommands(program: Command): void;
|
package/dist/commands/cloud.js
CHANGED
|
@@ -38,11 +38,12 @@ function parseSkillRef(raw) {
|
|
|
38
38
|
}
|
|
39
39
|
/** Print an error message to stderr and exit. */
|
|
40
40
|
/** Return a chalk color function appropriate for the given task status. */
|
|
41
|
-
function statusColor(status) {
|
|
41
|
+
export function statusColor(status) {
|
|
42
42
|
switch (status) {
|
|
43
43
|
case 'queued':
|
|
44
44
|
case 'allocating': return chalk.blue;
|
|
45
45
|
case 'running': return chalk.yellow;
|
|
46
|
+
case 'idle': return chalk.gray;
|
|
46
47
|
case 'completed': return chalk.green;
|
|
47
48
|
case 'input_required': return chalk.magenta;
|
|
48
49
|
case 'failed': return chalk.red;
|
package/dist/commands/exec.js
CHANGED
|
@@ -366,9 +366,37 @@ export function registerRunCommand(program) {
|
|
|
366
366
|
console.error(chalk.yellow('Warning: could not read the local Claude OAuth token — the box may come up "Not logged in".'));
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
|
+
// Progress UI. A self-throttled spinner (NOT ora — see progress.ts) covers
|
|
370
|
+
// the otherwise-silent provisioning + box-setup phases; the agent's own
|
|
371
|
+
// output then prints verbatim. The router splits the crabbox stream at the
|
|
372
|
+
// box-side marker. Rule: only ONE spinner phase is active at a time, and no
|
|
373
|
+
// other output is written to stderr while it spins — so it can never storm.
|
|
374
|
+
const { createLeaseOutputRouter, createSpinner } = await import('../lib/crabbox/progress.js');
|
|
375
|
+
const agentName = agentSpec.split('@')[0];
|
|
376
|
+
const spinner = createSpinner({ stream: process.stderr });
|
|
377
|
+
let warmupTimer;
|
|
378
|
+
const stopTimer = () => { if (warmupTimer) {
|
|
379
|
+
clearInterval(warmupTimer);
|
|
380
|
+
warmupTimer = undefined;
|
|
381
|
+
} };
|
|
382
|
+
const fit = (s) => {
|
|
383
|
+
const w = Math.max(20, (process.stderr.columns || 80) - 24);
|
|
384
|
+
return s.length > w ? s.slice(0, w - 1) + '…' : s;
|
|
385
|
+
};
|
|
386
|
+
const router = createLeaseOutputRouter({
|
|
387
|
+
// Setup lines only ever update spinner text (no direct stderr writes),
|
|
388
|
+
// so the spinner stays the single writer during the setup phase.
|
|
389
|
+
onSetupLine: (line) => spinner.update(`Setting up box — ${fit(line)}`),
|
|
390
|
+
onAgentChunk: (chunk) => {
|
|
391
|
+
// First agent byte: stop the setup spinner, THEN stream to stdout.
|
|
392
|
+
if (spinner.active)
|
|
393
|
+
spinner.stopAndPersist('✔', chalk.gray(`Box provisioned — ${agentName} output:`));
|
|
394
|
+
process.stdout.write(chunk);
|
|
395
|
+
},
|
|
396
|
+
});
|
|
369
397
|
try {
|
|
370
398
|
const { exitCode, box, toreDown } = await leaseAndRun({
|
|
371
|
-
agent:
|
|
399
|
+
agent: agentName,
|
|
372
400
|
prompt,
|
|
373
401
|
mode: options.mode,
|
|
374
402
|
model: options.model,
|
|
@@ -378,11 +406,47 @@ export function registerRunCommand(program) {
|
|
|
378
406
|
claudeCredentialsJson,
|
|
379
407
|
secretsBundle: process.env.AGENTS_LEASE_SECRETS_BUNDLE,
|
|
380
408
|
keep: options.keepBox,
|
|
409
|
+
onData: (chunk) => router.push(chunk),
|
|
410
|
+
onPhase: (phase) => {
|
|
411
|
+
if (phase.kind === 'warmup') {
|
|
412
|
+
const label = `Leasing a ${phase.backend ?? 'hetzner'} box`;
|
|
413
|
+
spinner.start(`${label}…`);
|
|
414
|
+
const t0 = Date.now();
|
|
415
|
+
warmupTimer = setInterval(() => spinner.update(`${label}… (${Math.round((Date.now() - t0) / 1000)}s)`), 1000);
|
|
416
|
+
}
|
|
417
|
+
else if (phase.kind === 'ready') {
|
|
418
|
+
stopTimer();
|
|
419
|
+
spinner.stopAndPersist('✔', `Box ${phase.box.slug} ready${phase.box.ip ? ` (${phase.box.ip})` : ''} · ${Math.round(phase.elapsedMs / 1000)}s`);
|
|
420
|
+
spinner.start('Setting up box…');
|
|
421
|
+
}
|
|
422
|
+
else if (phase.kind === 'teardown') {
|
|
423
|
+
if (spinner.active)
|
|
424
|
+
spinner.stop();
|
|
425
|
+
}
|
|
426
|
+
},
|
|
381
427
|
});
|
|
428
|
+
router.end();
|
|
429
|
+
stopTimer();
|
|
430
|
+
if (spinner.active)
|
|
431
|
+
spinner.stop();
|
|
432
|
+
// Safety net: if setup failed before the agent ever ran, the setup log
|
|
433
|
+
// was only shown as transient spinner text — surface it so the error is
|
|
434
|
+
// diagnosable.
|
|
435
|
+
if (exitCode !== 0 && !router.sawAgent()) {
|
|
436
|
+
const log = router.setupLines();
|
|
437
|
+
if (log.length)
|
|
438
|
+
process.stderr.write(chalk.dim(log.join('\n')) + '\n');
|
|
439
|
+
}
|
|
382
440
|
console.error(chalk.gray(toreDown ? `Box ${box.slug} destroyed.` : `Box ${box.slug} kept${box.ip ? ` (${box.ip})` : ''}. Stop it: crabbox stop ${box.slug}`));
|
|
383
441
|
process.exit(exitCode === null ? 1 : exitCode);
|
|
384
442
|
}
|
|
385
443
|
catch (err) {
|
|
444
|
+
stopTimer();
|
|
445
|
+
if (spinner.active)
|
|
446
|
+
spinner.stopAndPersist('✖', chalk.red('Lease failed'));
|
|
447
|
+
const log = router.setupLines();
|
|
448
|
+
if (log.length && !router.sawAgent())
|
|
449
|
+
process.stderr.write(chalk.dim(log.join('\n')) + '\n');
|
|
386
450
|
console.error(chalk.red(err.message));
|
|
387
451
|
process.exit(1);
|
|
388
452
|
}
|
package/dist/commands/feed.d.ts
CHANGED
|
@@ -4,6 +4,35 @@
|
|
|
4
4
|
* Aggregates block records from the local feed store and, with --host, from
|
|
5
5
|
* reachable remote hosts via SSH passthrough. Each block carries enough
|
|
6
6
|
* identity for `agents message` to route a reply back to the right agent.
|
|
7
|
+
*
|
|
8
|
+
* Default view groups by **outcome** (ticket / PR / worktree / Unassigned) so
|
|
9
|
+
* an operator sees dozens of deliverables, not ~1,100 agents. Pass `--flat`
|
|
10
|
+
* for the legacy per-agent list.
|
|
7
11
|
*/
|
|
8
12
|
import type { Command } from 'commander';
|
|
13
|
+
import { type OpenBlock } from '../lib/feed.js';
|
|
14
|
+
import { type OutcomeGroup, type SessionOutcomeHint } from '../lib/feed-outcome.js';
|
|
15
|
+
export declare const FEED_NO_FANOUT_ENV = "AGENTS_FEED_LOCAL";
|
|
16
|
+
export declare function parseRemoteFeed(stdout: string, machine: string): OpenBlock[];
|
|
17
|
+
/** Merge local and remote rows, keeping the first copy of a host/session block. */
|
|
18
|
+
export declare function mergeFeedBlocks(...groups: OpenBlock[][]): OpenBlock[];
|
|
19
|
+
export declare function shouldIncludeLocalFeed(hosts: string[] | undefined, self: string): boolean;
|
|
20
|
+
export declare function remoteFeedHostsToDial(hosts: string[] | undefined, self: string): string[] | undefined;
|
|
21
|
+
/** Human summary line for one outcome rollup. */
|
|
22
|
+
export declare function formatOutcomeHeader(group: OutcomeGroup): string;
|
|
23
|
+
/** Map active sessions into the lightweight hints outcome enrichment needs. */
|
|
24
|
+
export declare function sessionHintsFromActive(sessions: Array<{
|
|
25
|
+
sessionId?: string;
|
|
26
|
+
agentId?: string;
|
|
27
|
+
ticket?: {
|
|
28
|
+
id?: string;
|
|
29
|
+
};
|
|
30
|
+
pr?: {
|
|
31
|
+
url?: string;
|
|
32
|
+
number?: number;
|
|
33
|
+
};
|
|
34
|
+
worktree?: {
|
|
35
|
+
slug?: string;
|
|
36
|
+
};
|
|
37
|
+
}>): SessionOutcomeHint[];
|
|
9
38
|
export declare function registerFeedCommand(program: Command): void;
|