@phnx-labs/agents-cli 1.20.89 → 1.20.91
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 +361 -0
- package/README.md +6 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/exec.js +7 -1
- package/dist/commands/feed.js +77 -4
- package/dist/commands/harness.d.ts +27 -0
- package/dist/commands/harness.js +120 -13
- package/dist/commands/hooks.js +22 -6
- package/dist/commands/perf.d.ts +14 -0
- package/dist/commands/perf.js +221 -0
- package/dist/commands/profiles.d.ts +3 -0
- package/dist/commands/profiles.js +1 -1
- package/dist/commands/routines.d.ts +19 -0
- package/dist/commands/routines.js +58 -30
- package/dist/commands/secrets.d.ts +52 -4
- package/dist/commands/secrets.js +234 -37
- package/dist/commands/send.d.ts +5 -1
- package/dist/commands/send.js +1 -1
- package/dist/commands/sessions-browser.d.ts +4 -0
- package/dist/commands/sessions-browser.js +51 -9
- package/dist/commands/sessions-favorite.d.ts +20 -0
- package/dist/commands/sessions-favorite.js +120 -0
- package/dist/commands/sessions-picker.js +70 -1
- package/dist/commands/sessions.d.ts +103 -20
- package/dist/commands/sessions.js +356 -62
- package/dist/commands/setup-secrets.d.ts +7 -0
- package/dist/commands/setup-secrets.js +12 -9
- package/dist/commands/versions.js +12 -4
- package/dist/commands/view.d.ts +14 -1
- package/dist/commands/view.js +103 -128
- package/dist/index.js +18 -3
- package/dist/lib/activity.d.ts +11 -1
- package/dist/lib/activity.js +1 -0
- package/dist/lib/agents.d.ts +4 -2
- package/dist/lib/agents.js +21 -6
- package/dist/lib/catchup.d.ts +105 -0
- package/dist/lib/catchup.js +160 -0
- package/dist/lib/channels/providers/desktop.d.ts +49 -0
- package/dist/lib/channels/providers/desktop.js +132 -0
- package/dist/lib/channels/providers/index.js +2 -0
- package/dist/lib/daemon.js +74 -13
- package/dist/lib/events.d.ts +12 -0
- package/dist/lib/events.js +122 -9
- package/dist/lib/exec.js +10 -0
- package/dist/lib/feed-broadcast.d.ts +47 -0
- package/dist/lib/feed-broadcast.js +65 -1
- package/dist/lib/feed-post.d.ts +10 -0
- package/dist/lib/feed-post.js +1 -1
- package/dist/lib/feed.d.ts +47 -1
- package/dist/lib/feed.js +38 -0
- package/dist/lib/hooks/cache.d.ts +2 -0
- package/dist/lib/hooks/cache.js +24 -4
- package/dist/lib/hosts/dispatch.js +19 -1
- package/dist/lib/hq/floor.js +12 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/overdue.d.ts +14 -0
- package/dist/lib/overdue.js +37 -1
- package/dist/lib/perf/db.d.ts +25 -0
- package/dist/lib/perf/db.js +290 -0
- package/dist/lib/perf/spool.d.ts +18 -0
- package/dist/lib/perf/spool.js +79 -0
- package/dist/lib/perf/types.d.ts +45 -0
- package/dist/lib/perf/types.js +2 -0
- package/dist/lib/picker.d.ts +27 -2
- package/dist/lib/picker.js +71 -7
- package/dist/lib/profiles.d.ts +48 -0
- package/dist/lib/profiles.js +67 -0
- package/dist/lib/rotate.d.ts +24 -2
- package/dist/lib/rotate.js +63 -6
- package/dist/lib/routines-project.js +6 -0
- package/dist/lib/routines.d.ts +30 -1
- package/dist/lib/routines.js +11 -0
- 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/list-filter.d.ts +94 -0
- package/dist/lib/secrets/list-filter.js +245 -0
- package/dist/lib/session/active.d.ts +109 -3
- package/dist/lib/session/active.js +269 -13
- package/dist/lib/session/db.d.ts +14 -0
- package/dist/lib/session/db.js +35 -0
- package/dist/lib/session/digest.d.ts +7 -0
- package/dist/lib/session/digest.js +29 -1
- package/dist/lib/session/discover.d.ts +1 -2
- package/dist/lib/session/discover.js +7 -24
- package/dist/lib/session/favorites.d.ts +39 -0
- package/dist/lib/session/favorites.js +101 -0
- package/dist/lib/session/highlights.d.ts +82 -0
- package/dist/lib/session/highlights.js +251 -0
- package/dist/lib/session/host-link.d.ts +68 -0
- package/dist/lib/session/host-link.js +64 -0
- package/dist/lib/session/parse.js +23 -1
- package/dist/lib/session/presence.d.ts +85 -0
- package/dist/lib/session/presence.js +150 -0
- package/dist/lib/session/relative-time.d.ts +14 -0
- package/dist/lib/session/relative-time.js +36 -0
- package/dist/lib/session/remote-list.d.ts +10 -0
- package/dist/lib/session/remote-list.js +47 -9
- package/dist/lib/session/render.d.ts +7 -0
- package/dist/lib/session/render.js +87 -17
- package/dist/lib/session/types.d.ts +4 -1
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/dist/lib/state.d.ts +9 -0
- package/dist/lib/state.js +11 -0
- package/dist/lib/tmux/binary.d.ts +7 -0
- package/dist/lib/tmux/binary.js +11 -1
- package/dist/lib/types.d.ts +4 -3
- package/dist/lib/usage-backoff.d.ts +29 -0
- package/dist/lib/usage-backoff.js +165 -0
- package/dist/lib/usage.d.ts +112 -5
- package/dist/lib/usage.js +464 -46
- package/dist/lib/watchdog/runner.d.ts +13 -0
- package/dist/lib/watchdog/runner.js +16 -1
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,366 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.20.91
|
|
4
|
+
|
|
5
|
+
- **An agent can now say it is stuck: `agents feed post --blocked` (RUSH-2110).** The
|
|
6
|
+
feed carried benign progress but had no way to signal "I cannot proceed", so agents
|
|
7
|
+
hand-rolled it into the status text (`NEEDS MUQSIT: …`) and it reached nobody. A
|
|
8
|
+
blocked post writes `status.blocked` to the shared activity stream *and* opens an
|
|
9
|
+
answerable block in the ledger, so the ask stays open until someone resolves it
|
|
10
|
+
instead of scrolling away. It is a flag on the existing verb, not a new command —
|
|
11
|
+
one thing for an agent to learn, and one stream where most posts are benign and
|
|
12
|
+
some need a human. Blocked is a state, not a volume: it always broadcasts at
|
|
13
|
+
`important`, so passing `--level` too is a usage error rather than a silent
|
|
14
|
+
override. Pair it with `--option` for an answerable choice or `--default` for a
|
|
15
|
+
safe fallback policy may apply. Source: `apps/cli/src/commands/feed.ts`,
|
|
16
|
+
`apps/cli/src/lib/feed.ts`.
|
|
17
|
+
- **Feed blocks are actually delivered.** `publishBlock` wrote every "needs you"
|
|
18
|
+
record to the ledger and stopped there — `broadcastPostedEvent` ran only for
|
|
19
|
+
`feed post`, so a block was durable and invisible at the same time. Blocks now
|
|
20
|
+
reach the configured `feed.broadcast` sinks, carrying the ask and the literal
|
|
21
|
+
`agents focus <id>` command that unblocks it, and a block that reaches nobody
|
|
22
|
+
exits non-zero instead of looking like a success. Source:
|
|
23
|
+
`apps/cli/src/lib/feed-broadcast.ts`.
|
|
24
|
+
- **New `desktop` channel provider.** `agents send --channel desktop` (and
|
|
25
|
+
`notify.owner.channel: desktop`) posts a native notification through the branded
|
|
26
|
+
menu-bar helper. It is the only channel with no external dependency — no network,
|
|
27
|
+
no login, no vendor CLI — so it still reaches you at your Mac when a messaging
|
|
28
|
+
gateway is down. It reports real deliverability rather than always succeeding:
|
|
29
|
+
on Linux it probes for `notify-send` instead of trusting the platform name.
|
|
30
|
+
Source: `apps/cli/src/lib/channels/providers/desktop.ts`.
|
|
31
|
+
|
|
32
|
+
- **`agents perf` — disposable SQLite latency warehouse.** Indexed p50/p99
|
|
33
|
+
rollups for hooks, CLI commands, and `agent.run` timings without scanning the
|
|
34
|
+
audit JSONL. Warehouse lives at `~/.agents/.cache/perf/perf.db` (safe to wipe);
|
|
35
|
+
identity columns reuse sessions/events string shapes (`session_id`, `agent`,
|
|
36
|
+
`machine`, …) for soft cross-reference — no foreign keys. Hook shims spool
|
|
37
|
+
into the same DB; `agents hooks profile` reads it first. Source:
|
|
38
|
+
`apps/cli/src/lib/perf/db.ts`, `apps/cli/src/commands/perf.ts`.
|
|
39
|
+
|
|
40
|
+
- **A routine that misses its fire now runs late instead of being silently lost.** Fires
|
|
41
|
+
are in-process croner timers, and croner only ever schedules forward from "now" — so a
|
|
42
|
+
daemon that was down, asleep, or wedged when a routine came due dropped that fire
|
|
43
|
+
outright, and `loadAll()` rebuilt every timer looking only at the future. Detection
|
|
44
|
+
existed but ran **once, at daemon startup**, and only logged a warning plus a
|
|
45
|
+
notification; catching up was a manual `agents routines catchup`. Observed cost: zion's
|
|
46
|
+
daemon was down from 02:03Z to 08:23Z while the laptop slept, `weekly-fleet-retro` was
|
|
47
|
+
armed for exactly 04:00Z, never ran, and the restart logged `2 routine(s) overdue` and
|
|
48
|
+
did nothing. The daemon now re-scans every 5 minutes as well as at startup and runs each
|
|
49
|
+
missed routine via the same detached path `catchup` already used. Source:
|
|
50
|
+
`apps/cli/src/lib/catchup.ts`, `apps/cli/src/lib/daemon.ts`.
|
|
51
|
+
- **New `catchup:` routine field, and `agents routines add --no-catchup`.** Defaults to
|
|
52
|
+
true — a routine you scheduled is one you expect to have run. Set `catchup: false` for a
|
|
53
|
+
routine whose worth expires with its slot (a 9am brief is useless at 3pm); the miss is
|
|
54
|
+
still recorded, it just is not re-run. `agents routines list --json` reports the
|
|
55
|
+
effective value as `catchup`.
|
|
56
|
+
- **New `missed` run status.** A missed fire previously left no trace anywhere — no run
|
|
57
|
+
record, no log line in the routine's history — so `agents routines list` kept showing the
|
|
58
|
+
previous run's `completed` as though it were current, sometimes for weeks. A miss is now
|
|
59
|
+
written as a real run stamped at the moment the fire was due, so `agents routines runs
|
|
60
|
+
<name>` shows the gap, and the listing renders it distinctly from `failed` (a miss is an
|
|
61
|
+
infrastructure problem, not a task failure). That record is also what makes catch-up
|
|
62
|
+
idempotent: it advances the overdue comparison, so the same missed fire is never
|
|
63
|
+
reconsidered across ticks or a daemon restart storm, and its directory is created with a
|
|
64
|
+
non-recursive `mkdir` — an atomic claim, so if the daemon's timer and a manual
|
|
65
|
+
`agents routines catchup` overlap, only one of them runs the routine. Source: `apps/cli/src/lib/routines.ts` (`RunMeta`),
|
|
66
|
+
`apps/cli/src/commands/routines.ts`.
|
|
67
|
+
- **A routine is never caught up for a fire that predates it.** `detectOverdueJobs` walks back
|
|
68
|
+
a week for the most recent expected occurrence, and a routine with no runs is overdue by
|
|
69
|
+
definition — so before this, `agents routines add` on any daily or weekly schedule whose slot
|
|
70
|
+
had already passed made the routine instantly "overdue". That was cosmetic while catch-up was
|
|
71
|
+
a manual command; with the daemon now catching up automatically it would have run every newly
|
|
72
|
+
created routine once, within five minutes of creating it. Routines gain a `createdAt` stamp
|
|
73
|
+
(written once, like `actor`), and overdue detection floors the expected fire at it — falling
|
|
74
|
+
back to the routine file's mtime for routines written before the field existed. Observed on
|
|
75
|
+
the live fleet: `agents-cli-updates`, created Aug 1 and never run, was flagged overdue for a
|
|
76
|
+
Jul 27 fire. Source: `apps/cli/src/lib/overdue.ts` (`routineEffectiveStart`),
|
|
77
|
+
`apps/cli/src/lib/routines.ts` (`writeJob`).
|
|
78
|
+
|
|
79
|
+
- **`agents secrets list` can be filtered.** It had no filtering at all —
|
|
80
|
+
`--host` picks a machine and `--json` picks a format, but nothing selected over
|
|
81
|
+
the bundles themselves, so "which of these read with no Touch ID?", "which
|
|
82
|
+
still store a raw value inline?", "what have I not touched in three months?"
|
|
83
|
+
meant piping the table through `grep` or went unanswered. There is now an axis
|
|
84
|
+
per question: a `[query]` positional over name and description, `--policy`,
|
|
85
|
+
`--backend`, `--type`, `--kind`, `--held`/`--not-held`, `--expired`,
|
|
86
|
+
`--expiring [days]`, `--unused <duration>`, plus `--sort` and `-n/--limit`.
|
|
87
|
+
Every axis narrows independently, so they compose. Following the `agents
|
|
88
|
+
sessions` house style, an unknown value is a loud error naming the valid set
|
|
89
|
+
rather than an empty list, filters apply before `--json` so the payload is the
|
|
90
|
+
exact twin of the table, and they are forwarded over `--host` so a remote list
|
|
91
|
+
narrows the same way. `--held`/`--not-held` read live broker state and so
|
|
92
|
+
refuse to run off macOS instead of reporting every bundle as unheld. An empty
|
|
93
|
+
result names the filters that emptied it and the total it started from. Source:
|
|
94
|
+
`apps/cli/src/lib/secrets/list-filter.ts`, `apps/cli/src/commands/secrets.ts`.
|
|
95
|
+
|
|
96
|
+
- **The EXPIRING column no longer hides keys that have already expired.**
|
|
97
|
+
`countExpiringSoon` counted only keys due in the next 30 days — the guard is
|
|
98
|
+
`d >= 0` — so a bundle whose token died last month rendered `-`, identical to
|
|
99
|
+
one with no expiry at all. The only places a lapsed key surfaced were
|
|
100
|
+
`agents secrets view` and a hard abort at inject time, i.e. after it had already
|
|
101
|
+
broken a run. The column now counts lapsed and upcoming together and turns red
|
|
102
|
+
once anything has lapsed, and `secrets list --json` gains an `expired` count
|
|
103
|
+
alongside the existing `expiringSoon`. Source: `apps/cli/src/commands/secrets.ts`.
|
|
104
|
+
|
|
105
|
+
- **`agents secrets list` now states the hold window instead of the bare word
|
|
106
|
+
`hold`.** The `hold` tier is a duration — prompt once, then stay silent for
|
|
107
|
+
`secrets.agent.holdMs` (7 days by default) — but the POLICY column printed only
|
|
108
|
+
the tier name, so a reader could not tell it meant a window, let alone which
|
|
109
|
+
one; finding out meant running `agents secrets status`. The column now reads
|
|
110
|
+
`hold 7d`, and `hold 7d · held 6d` while the broker is actually caching the
|
|
111
|
+
bundle. It follows the configured window, so a 24-hour hold reads `hold 1d`.
|
|
112
|
+
`always` and `never` are unchanged — neither has a window, and annotating one
|
|
113
|
+
would repeat the mistake the `daily` rename fixed. Two adjacent bugs go with
|
|
114
|
+
it: `agents secrets view` printed "7d by default" as a string literal and so
|
|
115
|
+
misstated the window for anyone who had configured `holdMs`, and a stale broker
|
|
116
|
+
entry past its expiry rendered as `hold · held expired` because the column
|
|
117
|
+
tested the entry for presence rather than liveness. `secrets list --json` and
|
|
118
|
+
`secrets view --json` gain an additive `holdMs` field (null on `always`/`never`)
|
|
119
|
+
so a machine caller gets the window too. Source:
|
|
120
|
+
`apps/cli/src/commands/secrets.ts`.
|
|
121
|
+
|
|
122
|
+
- **Richer session previews: skills, hooks, links, artifacts, repos, todo status.** The `agents sessions` quick preview and full summary now show the skills a session invoked (with counts), the hooks that fired (Claude transcripts, with repeat counts and failures), a clickable Links section (Linear/Jira/GitHub/GitLab URLs harvested from the conversation), the documents the session produced (`.agents/artifacts|plans|reports` and other `*.md`/`*.html` creations), the repos it worked in (via `.git` walk-up), and an error tally in the picker. The full summary's Plan section now marks checklist items `[x]`/`[>]`/`[ ]` and renders the checklist alongside the ExitPlanMode text instead of hiding it. Changes/Dirs lines collapse `.agents/worktrees/<slug>` prefixes to `⧉ <slug>/…`, are width-capped, and no longer list shell junk (`2>&1`, `$VAR` paths), `node_modules`, or agents-cli internal archives. Source: `apps/cli/src/lib/session/highlights.ts`, `apps/cli/src/lib/session/parse.ts`, `apps/cli/src/lib/session/render.ts`, `apps/cli/src/commands/sessions-picker.ts`.
|
|
123
|
+
|
|
124
|
+
## 1.20.90
|
|
125
|
+
|
|
126
|
+
- **`agents sessions --active` now shows one row per agent, not one per directory.**
|
|
127
|
+
A live tmux agent pane whose durable identity records were missing (the common case
|
|
128
|
+
once meta/pid-registry entries age out) was dropped, then re-surfaced by the ps-scan
|
|
129
|
+
under the newest transcript in its cwd — so many distinct sessions collapsed onto one
|
|
130
|
+
stranger's id with an inflated `×N` badge, and `agents sessions focus <id>` could not
|
|
131
|
+
find them. The scanner now recovers the session id straight from the `ag-<agent>-<shortid>`
|
|
132
|
+
tmux pane name (resolved to the full UUID via the short-id index in one batched query),
|
|
133
|
+
and refuses to borrow a co-located sibling's transcript when no id is known — so every
|
|
134
|
+
live session surfaces as its own row and is focus-able again. Also adds a `runTmux`
|
|
135
|
+
timeout so a wedged tmux server can't hang the scan. Source: `apps/cli/src/lib/session/active.ts`.
|
|
136
|
+
|
|
137
|
+
- **`agents view` now shows live usage bars for Antigravity.** The `agy` account
|
|
138
|
+
row renders one bar per model quota bucket (`3.1P: ███░░ 42% (1d)` style),
|
|
139
|
+
sourced from the same Google Code Assist `:retrieveUserQuota` endpoint `agy`
|
|
140
|
+
itself talks to. Auth reuses the stored `agy` OAuth credential (macOS Keychain
|
|
141
|
+
item `gemini`/`antigravity`, Linux Secret Service, or the
|
|
142
|
+
`~/.gemini/antigravity-cli/antigravity-oauth-token` file fallback), refreshing
|
|
143
|
+
the access token in memory when expired — safe from a read path because
|
|
144
|
+
Google's refresh tokens are non-rotating, and never written back to the
|
|
145
|
+
keychain. Each per-model bucket also flows into the throttle badge, run
|
|
146
|
+
rotation eligibility, and `agents view --json` (whose usage windows now carry
|
|
147
|
+
a `label` so same-keyed per-model bars are distinguishable). Source:
|
|
148
|
+
`apps/cli/src/lib/usage.ts`, `apps/cli/src/lib/agents.ts`,
|
|
149
|
+
`apps/cli/src/commands/view.ts`.
|
|
150
|
+
|
|
151
|
+
- **A custom harness is now its own agent type in `agents view`.** A harness created
|
|
152
|
+
with `agents harness add` (or `agents profiles add`) used to render as an indented
|
|
153
|
+
`profile` row under whichever host CLI executes it. It now gets its own block beside
|
|
154
|
+
Claude and Codex — a bold name header, then one row carrying the pinned model, the
|
|
155
|
+
account/auth state, and `via <host> <version>` naming the native harness underneath.
|
|
156
|
+
That matches how it is already launched: `agents run <name>` treats a custom harness
|
|
157
|
+
exactly like a native agent id. A harness whose host CLI has no install is flagged
|
|
158
|
+
`(host <id> not installed)` rather than listed as runnable, and the separate
|
|
159
|
+
"Profile-only Agents" section is gone — those harnesses now render in the main list
|
|
160
|
+
like every other one. Source: `apps/cli/src/commands/view.ts`.
|
|
161
|
+
- **`agents view <harness>` describes a custom harness** — host, model, provider, auth,
|
|
162
|
+
fork lineage, YAML path — instead of failing with "unknown agent";
|
|
163
|
+
`agents view <harness> --json` emits its summary. Source:
|
|
164
|
+
`apps/cli/src/commands/harness.ts` (`renderHarnessDetail`).
|
|
165
|
+
- **New `agents harness fork <source> <name>`.** One verb over both starting points:
|
|
166
|
+
fork a native harness (`agents harness fork opencode deepseek --model
|
|
167
|
+
deepseek/deepseek-v4-flash-0731 --auth-provider openrouter`) or copy a custom one you
|
|
168
|
+
already tuned and change only what you name (`agents harness fork deepseek
|
|
169
|
+
deepseek-chat --model deepseek/deepseek-chat-v3`). Forking a custom harness is a full
|
|
170
|
+
copy — env, endpoint, auth binding, `fallback_model`, host version pin — so the two
|
|
171
|
+
diverge and deleting the source never affects the fork; forking a native harness
|
|
172
|
+
requires `--model` because there is no model to inherit. Flags: `--model`,
|
|
173
|
+
`--base-url`, `--auth-provider`, `--version`, `--label`, `--description`,
|
|
174
|
+
`--key-stdin`, `--force`. Source: `apps/cli/src/lib/profiles.ts` (`forkProfile`).
|
|
175
|
+
- **Profile YAML gains optional `label:` and `forkedFrom:`.** `label` sets the name
|
|
176
|
+
`agents view` prints for the harness (defaults to the file name); `forkedFrom` records
|
|
177
|
+
the parent as display-only lineage. Existing profiles keep working untouched. Source:
|
|
178
|
+
`apps/cli/src/lib/profiles.ts`.
|
|
179
|
+
- **Breaking (`--json`):** in `agents view <agent> --json`, the per-agent `profiles` key
|
|
180
|
+
is now `harnesses`, and each entry carries new `label`, `hostVersion`, `description`,
|
|
181
|
+
and `forkedFrom` fields alongside the existing ones. Source:
|
|
182
|
+
`apps/cli/src/commands/view.ts` (`ViewJsonAgent`).
|
|
183
|
+
|
|
184
|
+
- **Menu bar ACTIVE: project accordion + session detail submenu.** Projects are
|
|
185
|
+
collapsed by default as a status strip (`▶ agents-cli ●8 ◐1 zion`); click
|
|
186
|
+
`▶`/`▼` to fold agents open under the project (idle-row caps removed — collapse
|
|
187
|
+
is the wall protection). Focusing an agent opens a side submenu with linkable
|
|
188
|
+
detail (work title URL, cwd, Linear ticket, GitHub PR, duration, copy session
|
|
189
|
+
id) from the warm `sessions --active` cache. Accordion reopen rebuilds from
|
|
190
|
+
cache only (no teams walk / no CLI schedule). Local/remote uses the same host
|
|
191
|
+
normalize as CLI `machineId()` so local rows are not mislabeled remote. Source:
|
|
192
|
+
`apps/cli/menubar/Sources/MenubarHelper/StatusItemController.swift`,
|
|
193
|
+
`LocalState.swift`, `Models.swift`.
|
|
194
|
+
|
|
195
|
+
- **An offloaded editor tab no longer displays another session's id.** A Factory
|
|
196
|
+
tab launched with `agents run --host <device>` has no local agent process, but
|
|
197
|
+
the extension still resolved its "live" session id by reading the SessionStart
|
|
198
|
+
hook's `~/.agents/.cache/state/sessions/<pid>.json` for the local pid tree —
|
|
199
|
+
the pid of the ssh client. Those files are keyed by pid alone and are only
|
|
200
|
+
pruned when the pid is dead, so once the OS recycled a pid the tab adopted
|
|
201
|
+
whatever session had last held it: one remote tab showed the id and version of
|
|
202
|
+
an unrelated synthetic run from 20 days earlier while `/status` inside it
|
|
203
|
+
reported the truth. An offloaded tab now takes its identity from the device
|
|
204
|
+
instead of local disk, and a local tab rejects any state record whose
|
|
205
|
+
SessionStart timestamp predates the tab itself.
|
|
206
|
+
- **`AGENT_TERMINAL_ID` now rides the SSH hop.** `agents run --host` forwarded
|
|
207
|
+
actor provenance but not the launching tab's terminal id, so the remote pid
|
|
208
|
+
registry recorded no terminal — leaving `agents sessions --active --host
|
|
209
|
+
<device>` unable to answer "which session is this tab running?" once the agent
|
|
210
|
+
moved on (a `/clear`, or an exit and rerun in the same tab).
|
|
211
|
+
- **`agents sessions --active --json` now carries `terminalId`.** The pid registry
|
|
212
|
+
has always recorded it; the emitted row dropped it, so no consumer could join a
|
|
213
|
+
live session back to the editor tab that launched it.
|
|
214
|
+
|
|
215
|
+
- **Balanced routing no longer launches into an account it only *thinks* has
|
|
216
|
+
headroom.** Account usage is cached per machine under stale-while-revalidate:
|
|
217
|
+
a snapshot up to 24h old was served instantly, and the background refresh that
|
|
218
|
+
should have corrected it lands after the pick is already made. On a box whose
|
|
219
|
+
refresh is failing that state is permanent — measured on `yosemite-s1`, every
|
|
220
|
+
Claude snapshot sat 26 hours to 2.7 days old, so balanced read
|
|
221
|
+
`muqsit@getrush.ai` as 48% used and launched into it while the account was at
|
|
222
|
+
its weekly cap; the session answered "You've hit your weekly limit" on its
|
|
223
|
+
first turn. Routing now caps how stale a snapshot may be when it is about to
|
|
224
|
+
decide (5 minutes), blocking on one bounded, parallel live read past that — and
|
|
225
|
+
no read at all inside the existing 2-minute fresh window, which back-to-back
|
|
226
|
+
launches hit. Display paths (`agents view`) keep the full 24h window and stay
|
|
227
|
+
off the network.
|
|
228
|
+
- **A pick made on unconfirmed data says so.** When no account on the machine
|
|
229
|
+
could be refreshed, routing still launches — a broken refresh must not make a
|
|
230
|
+
box unusable — but the banner now reads `… (2 of 5 healthy, usage unverified —
|
|
231
|
+
no account could be refreshed)` instead of presenting a guess as a fact. An
|
|
232
|
+
account with a verified snapshot always wins over one with a stale snapshot,
|
|
233
|
+
even when the stale number looks emptier. This applies to `--strategy
|
|
234
|
+
available` as well as `balanced` — both route on the same cache, and
|
|
235
|
+
`available`'s headroom sort was inverted by a stale number in exactly the same
|
|
236
|
+
way. An explicit version preference is an instruction, not a ranking signal, so
|
|
237
|
+
it still wins.
|
|
238
|
+
- **The mid-run failover chain is unchanged.** Declining to *pick* an account on
|
|
239
|
+
unconfirmed data and declining to *fail over to* it after the primary already
|
|
240
|
+
hit a 429 are different risks — by then the alternative is not launching at
|
|
241
|
+
all. Every eligible account stays in the failover chain; only the initial pick
|
|
242
|
+
prefers verified ones.
|
|
243
|
+
|
|
244
|
+
- **`agents routines list` no longer reports another device's routine as failed.** Run
|
|
245
|
+
records are written into the runs dir of whichever machine fired the routine and carry
|
|
246
|
+
no device attribution, but the listing resolved Last Status from any local record and
|
|
247
|
+
rendered it even on rows for routines pinned elsewhere. A routine re-pinned to another
|
|
248
|
+
device therefore kept reporting the old machine's leftover records forever — on zion,
|
|
249
|
+
`security-sweep`, `review-open-prs` and `hetzner-lease-gc` all read `failed` from late
|
|
250
|
+
July while `yosemite-s0`/`s1`, the devices that actually fire them, had completed them
|
|
251
|
+
that morning. The macOS menu bar reads this JSON, so it painted a column of red `exit 1`
|
|
252
|
+
rows for routines that were green. Last Status is now scoped to the device that owns the
|
|
253
|
+
run: a routine this device does not fire shows `-`, and `--json` returns `null` for
|
|
254
|
+
`lastStatus`, `exitCode`, `failureReason`, `lastRunStartedAt` and `lastRunCompletedAt`
|
|
255
|
+
(`runsHere: false` already says why). A routine pinned to several devices renders one row
|
|
256
|
+
per device but carries a status only on its **This machine** row. Read a peer's status
|
|
257
|
+
with `agents routines list --device <name>`; the local history is untouched and still
|
|
258
|
+
readable via `agents routines runs <name>`. Source: `apps/cli/src/commands/routines.ts`
|
|
259
|
+
(`localLatestRun`, `groupRoutineJobsByDevice`), `apps/cli/docs/03-routines.md`.
|
|
260
|
+
|
|
261
|
+
- **`agents watchdog` now tracks per-session presence (RUSH-2007 Layer C).** Each
|
|
262
|
+
tick reconciles a per-session presence record — `{location, device, transport,
|
|
263
|
+
lastSeen, status}` at `~/.agents/.cache/state/watchdog/presence.json` — from the
|
|
264
|
+
tick's active scan, deriving `connected` / `disconnected` by diffing consecutive
|
|
265
|
+
ticks. A session that was tracked but is now absent (its SSH link dropped or the
|
|
266
|
+
peer went unreachable) flips to `disconnected`, and the flip is surfaced in
|
|
267
|
+
`agents watchdog --json` under `presence.transitions` — an interactive drop as a
|
|
268
|
+
`reconnect-nudge` candidate, a headless remote as `keep-alive`. Folded into the
|
|
269
|
+
existing tick (no revived daemon, no extra SSH fan-out); additive and does not
|
|
270
|
+
change the tick's nudge decisions. Source:
|
|
271
|
+
`apps/cli/src/lib/session/presence.ts`, `apps/cli/src/lib/watchdog/runner.ts`.
|
|
272
|
+
|
|
273
|
+
- **`agents setup secrets --policy hold` no longer fails, and `agents secrets
|
|
274
|
+
status` stops naming the retired `daily` policy.** The 1.20.79 `daily` → `hold`
|
|
275
|
+
rename swept the help, docs, and the `secrets list` POLICY column, but two
|
|
276
|
+
surfaces were never migrated. The worse one was functional: the onboarding
|
|
277
|
+
wizard carried its own copy of the policy vocabulary, so
|
|
278
|
+
`agents setup secrets --policy hold` — the canonical name every other secrets
|
|
279
|
+
command prints — exited with `Invalid --policy 'hold'. Use daily, always, or
|
|
280
|
+
never.`, and its interactive prompt still offered `daily` as the default
|
|
281
|
+
choice. It now shares `parsePolicyOpt` with `agents secrets policy`, so the two
|
|
282
|
+
commands can't disagree about what a policy is called; `daily`/`session` stay
|
|
283
|
+
accepted as aliases and the wizard's default is unchanged (the hold tier). The
|
|
284
|
+
second was cosmetic: `agents secrets status` printed "a daily bundle prompts
|
|
285
|
+
once…" and "the next read of each daily bundle…" — the one command a user runs
|
|
286
|
+
to answer *why did it prompt again*, naming a policy its sibling commands no
|
|
287
|
+
longer emit. Both lines now say `hold` and are pure values pinned by tests, so
|
|
288
|
+
the vocabulary can't drift again. Source:
|
|
289
|
+
`apps/cli/src/commands/setup-secrets.ts`, `apps/cli/src/commands/secrets.ts`.
|
|
290
|
+
|
|
291
|
+
- **Favorite sessions from the browser.** `*` stars the highlighted session in
|
|
292
|
+
`agents sessions` and `f` filters the list to the starred ones; outside a TTY,
|
|
293
|
+
`agents sessions favorite <id>` (`--remove` / `--list` / `--json`) and
|
|
294
|
+
`agents sessions --favorites` do the same. Stars live in
|
|
295
|
+
`~/.agents/.history/favorites.json` keyed by session id, so they survive a reindex
|
|
296
|
+
of the session cache. They are per-machine — session sync carries transcripts, not
|
|
297
|
+
this file. Source:
|
|
298
|
+
`apps/cli/src/lib/session/favorites.ts`, `apps/cli/src/commands/sessions-favorite.ts`.
|
|
299
|
+
- **Detect sessions that lost their host — two new statuses, `crashed` and `orphaned`.**
|
|
300
|
+
A session whose editor window or connection went down hard used to just VANISH from
|
|
301
|
+
`agents sessions --active` (its dead-pid registry entry was filtered out), and one
|
|
302
|
+
still running in tmux with nobody attached reported a plain `idle`. Both now say so:
|
|
303
|
+
`✗ crashed` when the host window stopped republishing and the agent died with it,
|
|
304
|
+
`◍ orphan` when the agent is alive with zero clients attached. Derived from tmux's
|
|
305
|
+
`#{session_attached}` and the IDE window's registry heartbeat — never from a
|
|
306
|
+
deliberate `agents sessions detach`, and never over a session that is still working.
|
|
307
|
+
Source: `apps/cli/src/lib/session/host-link.ts`, `apps/cli/src/lib/session/active.ts`.
|
|
308
|
+
- **`agents sessions --active --favorites` now actually filters.** The flag was wired
|
|
309
|
+
into the interactive browser only, so every path that skips it — `--json`,
|
|
310
|
+
`--waiting`, a pipe, a multi-host scope, an SSH-fanout peer — silently returned the
|
|
311
|
+
whole fleet. Source: `apps/cli/src/commands/sessions.ts`.
|
|
312
|
+
- **`agents sessions --active --waiting` no longer counts a dead session.** `activity`
|
|
313
|
+
is not rewritten when a session dies, so one that crashed mid-question reported "needs
|
|
314
|
+
your input" forever — what it needs is a relaunch. Source:
|
|
315
|
+
`apps/cli/src/commands/sessions.ts`.
|
|
316
|
+
|
|
317
|
+
- **Resolve historical sessions safely across the fleet (#1757).** `agents sessions --resolve <full-id|prefix|keywords> --json` uses a versioned safe peer protocol, returns only resolver metadata, reports every full-ID candidate on ambiguity, treats synced copies as one match, and exits 2 without deciding when a peer fails, returns malformed output, or runs an older CLI. Source: `apps/cli/src/commands/sessions.ts`.
|
|
318
|
+
|
|
319
|
+
- **A rate-limited usage endpoint is now backed off instead of hammered.** The
|
|
320
|
+
daemon warms auth-health every 3 minutes and probes *every installed version
|
|
321
|
+
home* in one parallel batch, so a machine with five Claude accounts sent five
|
|
322
|
+
concurrent requests to `api.anthropic.com/api/oauth/usage` every three minutes
|
|
323
|
+
— roughly 100/hour — before the usage refresh added its own. Nothing read
|
|
324
|
+
`Retry-After`. Measured on `yosemite-s1`: the endpoint answered
|
|
325
|
+
`429 rate_limit_error` with `retry-after: 2678` (about 45 minutes) for every
|
|
326
|
+
account while the credentials themselves read healthy, and the next tick fired
|
|
327
|
+
three minutes later, deep inside the penalty window, re-arming it. The box
|
|
328
|
+
never recovered, every usage read failed, and its cache froze — the
|
|
329
|
+
permanently-stale state balanced routing was already having to defend against.
|
|
330
|
+
- **A 429 now records its deadline and every read honours it.** Usage fetches and
|
|
331
|
+
health probes for that provider short-circuit until the window passes — no
|
|
332
|
+
request, no renewed penalty — and report
|
|
333
|
+
`Claude rate-limited this machine — not retrying for 45 minutes.` The state is
|
|
334
|
+
on disk, because the callers are separate processes: the long-lived daemon and
|
|
335
|
+
every one-shot `agents view` / `agents run` — one empty file per penalty under
|
|
336
|
+
`~/.agents/.cache/usage-backoff/`, named `<agent>.<deadline>`, so two
|
|
337
|
+
processes recording the same provider at once cannot displace each other and a
|
|
338
|
+
read takes the furthest deadline. A server delay is capped at an hour, and a
|
|
339
|
+
missing or unparseable `Retry-After` still backs off.
|
|
340
|
+
|
|
341
|
+
- **A usage read that fails now says so, instead of returning a silent null.**
|
|
342
|
+
Four branches in every networked usage fetch — Claude, Kimi, Droid and
|
|
343
|
+
Cursor — returned `{ snapshot: null, error: null }`: no readable credential, a
|
|
344
|
+
locally-expired one, a rejected request, and a request that threw (timeout,
|
|
345
|
+
DNS/TLS, an unparseable payload). The caller could not tell any of them apart
|
|
346
|
+
from a healthy read, so it fell
|
|
347
|
+
back to whatever the stale-while-revalidate cache held and drew those bars as
|
|
348
|
+
fact. Measured on `yosemite-s1`: every Claude account's stored access token had
|
|
349
|
+
expired (one of them eleven days earlier), so no read could succeed, and
|
|
350
|
+
`agents view claude --refresh` printed a full, healthy-looking table twice
|
|
351
|
+
while writing nothing to the cache. A usage read never refreshes a token
|
|
352
|
+
(RUSH-1822), so an expired credential does not heal on its own — the account
|
|
353
|
+
stays unreadable until that agent actually runs. A rate-limited endpoint (429)
|
|
354
|
+
now reads differently from a rejected credential (401), because re-authing
|
|
355
|
+
fixes one and not the other.
|
|
356
|
+
- **`agents view` marks bars the live read could not confirm.** A row whose
|
|
357
|
+
snapshot came from the cache after a failed live read renders the reading plus
|
|
358
|
+
`unverified`, rather than looking identical to a confirmed one. The number
|
|
359
|
+
still shows — it is the last thing we saw — but it no longer reads as current.
|
|
360
|
+
- **`agents view --refresh` reports what it could not refresh.** It now lists
|
|
361
|
+
each account it failed to reach and why, instead of rendering a table that
|
|
362
|
+
looks fully refreshed regardless.
|
|
363
|
+
|
|
3
364
|
## 1.20.89
|
|
4
365
|
|
|
5
366
|
- **Webhook handler layer for one-off agent/workflow/command/routine triggers.**
|
package/README.md
CHANGED
|
@@ -265,6 +265,8 @@ On a terminal, `agents sessions --active` (and a bare `agents sessions`) open th
|
|
|
265
265
|
|---|---|---|
|
|
266
266
|
| `s` | search text | `--query` / positional |
|
|
267
267
|
| `r` | running only | `--active` |
|
|
268
|
+
| `f` | favorites only | `--favorites` |
|
|
269
|
+
| `*` | star / unstar the highlighted session | `agents sessions favorite <id>` |
|
|
268
270
|
| `c` | team sessions | `--teams` |
|
|
269
271
|
| `a` | agent (cycles) | `-a` |
|
|
270
272
|
| `d` | device (cycles) | `--device` |
|
|
@@ -274,6 +276,10 @@ On a terminal, `agents sessions --active` (and a bare `agents sessions`) open th
|
|
|
274
276
|
| `⏎` | resume / attach | `resume` / `focus` |
|
|
275
277
|
| `y` | copy the equivalent command | `--print-cmd` |
|
|
276
278
|
|
|
279
|
+
**Star the sessions you keep coming back to.** `*` marks the highlighted row (a `★` shows in the listing), `f` narrows to the starred ones, and `agents sessions favorite <id>` / `--favorites` do the same outside a TTY. Stars live in `~/.agents/.history/favorites.json` keyed by session id, so they survive a reindex of the session cache. They're per-machine — session sync carries transcripts, not this file.
|
|
280
|
+
|
|
281
|
+
**A session that lost its host says so.** When an editor window or an SSH connection goes down hard, the agent it owned used to simply disappear from `--active`; when an agent outlived its window in tmux, it reported a plain `idle`. Both now carry their own status: `✗ crashed` (the host went down and took the agent with it) and `◍ orphan` (still alive, but no client is attached — nothing is showing it). Read from tmux's attached-client count and the editor window's registry heartbeat, so a deliberate `agents sessions detach` is never mistaken for one, and a session that is still *working* headlessly is left alone.
|
|
282
|
+
|
|
277
283
|
Filters **stack** (they AND together), the active set shows in the header, and the highlighted row **previews below by default** (`tab` hides it) — prompt, activity, last response, plus a links line where the worked-on ticket and the PR the session opened are **clickable** (OSC 8 hyperlinks: the ticket jumps to Linear, the `PR#` to GitHub, in terminals that support them). The Linear workspace is resolved from `LINEAR_WORKSPACE` or the linear-cli config, so tickets stay plain text when it's unknown. Because every hotkey has a flag, the view you build by hand is a real command: press `y` (or run `--print-cmd`) to get the exact `ag sessions …` line — explore interactively, hand the line to an agent. Piped output, `--json`, or `--no-interactive` keep the plain listing for scripts. Peek without opening the pager with `agents sessions <id> --preview`.
|
|
278
284
|
|
|
279
285
|
| before — preview hidden | after — preview open + clickable links |
|
package/dist/bin/agents
CHANGED
|
Binary file
|
package/dist/commands/exec.js
CHANGED
|
@@ -59,7 +59,13 @@ function formatRotationBanner(result, verb = 'balanced') {
|
|
|
59
59
|
const { picked, healthy, excluded } = result;
|
|
60
60
|
const label = picked.email ? `${picked.email} · ${picked.agent}@${picked.version}` : `${picked.agent}@${picked.version}`;
|
|
61
61
|
const ratio = `${healthy.length} of ${healthy.length + excluded.length} healthy`;
|
|
62
|
-
|
|
62
|
+
// Say it when the pick was a guess. A machine whose usage refresh is failing
|
|
63
|
+
// reports old percentages with total confidence, so a silent banner reads
|
|
64
|
+
// identical whether the router knew the account had headroom or merely hoped
|
|
65
|
+
// so — and the operator only finds out when the agent answers "you've hit
|
|
66
|
+
// your weekly limit".
|
|
67
|
+
const caveat = result.usageUnverified ? ', usage unverified — no account could be refreshed' : '';
|
|
68
|
+
return `[agents] ${verb} picked ${label} (${ratio}${caveat})`;
|
|
63
69
|
}
|
|
64
70
|
/**
|
|
65
71
|
* Whether `cwd` is inside a git work tree.
|
package/dist/commands/feed.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { ensureFeedPublishHook, listAskStats, listBlocks, recordNotified } from '../lib/feed.js';
|
|
2
|
+
import { ensureFeedPublishHook, listAskStats, listBlocks, recordNotified, buildDeclaredBlock, publishBlock, } from '../lib/feed.js';
|
|
3
3
|
import { ensureActivityLogHook, readRecentActivity, formatActivityLine, formatProgressUpdate, mergeActivityEvents, parseActivityPayload, } from '../lib/activity.js';
|
|
4
4
|
import { postFeedStatus } from '../lib/feed-post.js';
|
|
5
|
-
import { parseFeedPostLevel, planFeedBroadcast, runFeedBroadcast, } from '../lib/feed-broadcast.js';
|
|
5
|
+
import { parseFeedPostLevel, planFeedBroadcast, runFeedBroadcast, blockBroadcastContext, blockDeliveryFailure, } from '../lib/feed-broadcast.js';
|
|
6
6
|
import { getSessionById } from '../lib/session/db.js';
|
|
7
7
|
import { readMeta } from '../lib/state.js';
|
|
8
8
|
import { enrichBlocksFromSessions, groupBlocksByOutcome, isUnambiguousOutcomeAnswer, openBlocksForOutcome, stampBlockOutcomes, } from '../lib/feed-outcome.js';
|
|
@@ -238,6 +238,9 @@ export function registerFeedCommand(program) {
|
|
|
238
238
|
.option('--session <id>', 'Session id escape hatch (default: auto from env / pid registry)')
|
|
239
239
|
.option('--attach <path-or-url...>', 'Attach an artifact (local file or URL); repeatable')
|
|
240
240
|
.option('--level <level>', 'How loudly to broadcast: milestone (default) or important. Configured sinks with minLevel: important only fire on the latter.', 'milestone')
|
|
241
|
+
.option('--blocked', 'You are STUCK and need the user. Opens an answerable block and always broadcasts at important — do not also pass --level.')
|
|
242
|
+
.option('--option <label...>', 'With --blocked: an answer the user can pick; repeatable')
|
|
243
|
+
.option('--default <answer>', 'With --blocked: a safe default policy may apply if nobody answers in time')
|
|
241
244
|
.option('--json', 'Emit the written event as JSON')
|
|
242
245
|
.addHelpText('after', `
|
|
243
246
|
Examples:
|
|
@@ -249,6 +252,17 @@ Examples:
|
|
|
249
252
|
# Worth interrupting someone over — reaches sinks gated on minLevel: important:
|
|
250
253
|
agents feed post "release blocked: npm token expired" --level important
|
|
251
254
|
|
|
255
|
+
# You are STUCK and cannot proceed. Opens an answerable block that stays in
|
|
256
|
+
# 'agents feed' until someone resolves it, and always reaches the owner —
|
|
257
|
+
# do NOT also pass --level:
|
|
258
|
+
agents feed post "force-push denied by git-guard on PR #1749" --blocked
|
|
259
|
+
agents feed post "publish to npm or wait for review?" --blocked --option publish --option wait
|
|
260
|
+
agents feed post "delete the stale preview env?" --blocked --default "leave it"
|
|
261
|
+
|
|
262
|
+
# Exhaust self-serve FIRST. A block is for what you genuinely cannot do:
|
|
263
|
+
# a credential only the user holds, a decision only they can make, an
|
|
264
|
+
# approval only they can give. Not "should I do the obvious next step?".
|
|
265
|
+
|
|
252
266
|
# Outside a run, pass the session explicitly:
|
|
253
267
|
agents feed post "manual note" --session 00998b0e-2d15-4d2f-a58b-974a886c9b47
|
|
254
268
|
|
|
@@ -268,22 +282,66 @@ docs/06-observability.md.
|
|
|
268
282
|
session: opts?.session ?? cmd?.opts?.()?.session,
|
|
269
283
|
attach: opts?.attach ?? cmd?.opts?.()?.attach,
|
|
270
284
|
level: opts?.level ?? cmd?.opts?.()?.level,
|
|
285
|
+
blocked: Boolean(opts?.blocked ?? cmd?.opts?.()?.blocked),
|
|
286
|
+
option: opts?.option ?? cmd?.opts?.()?.option,
|
|
287
|
+
default: opts?.default ?? cmd?.opts?.()?.default,
|
|
271
288
|
json: Boolean(opts?.json ?? cmd?.opts?.()?.json ?? cmd?.parent?.opts?.()?.json),
|
|
272
289
|
};
|
|
273
290
|
try {
|
|
274
|
-
|
|
291
|
+
// Blocked is a state, not a volume: it always broadcasts at `important`,
|
|
292
|
+
// so an agent has exactly one thing to say. Passing both is a usage
|
|
293
|
+
// error rather than a silent override -- an agent that thinks it chose
|
|
294
|
+
// the level should not be quietly ignored.
|
|
295
|
+
if (flags.blocked && flags.level && flags.level !== 'milestone') {
|
|
296
|
+
throw new Error('--blocked already broadcasts at important; drop --level.');
|
|
297
|
+
}
|
|
298
|
+
if (!flags.blocked && (flags.option?.length || flags.default)) {
|
|
299
|
+
throw new Error('--option/--default only apply with --blocked.');
|
|
300
|
+
}
|
|
301
|
+
const level = flags.blocked ? 'important' : parseFeedPostLevel(flags.level);
|
|
275
302
|
const { event } = postFeedStatus({
|
|
276
303
|
text: Array.isArray(textParts) ? textParts.join(' ') : String(textParts ?? ''),
|
|
277
304
|
sessionId: flags.session,
|
|
278
305
|
attach: flags.attach,
|
|
306
|
+
blocked: flags.blocked,
|
|
279
307
|
});
|
|
280
|
-
|
|
308
|
+
// A blocked post lands in BOTH stores: the event in the shared activity
|
|
309
|
+
// stream (what happened) and an OpenBlock in the ledger (what is still
|
|
310
|
+
// open). The ledger is what makes it answerable and clearable -- without
|
|
311
|
+
// it the ask would scroll away like any other update.
|
|
312
|
+
let outcomes;
|
|
313
|
+
if (flags.blocked) {
|
|
314
|
+
const block = buildDeclaredBlock(event, {
|
|
315
|
+
text: event.detail ?? '',
|
|
316
|
+
options: flags.option,
|
|
317
|
+
safeDefault: flags.default,
|
|
318
|
+
});
|
|
319
|
+
publishBlock(block);
|
|
320
|
+
outcomes = broadcastBlock(block, { project: event.project, agent: event.agent });
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
outcomes = broadcastPostedEvent(event, level);
|
|
324
|
+
}
|
|
325
|
+
// Fail loud when a block reached nobody. This is computed BEFORE the
|
|
326
|
+
// --json early return: a machine caller is exactly the one that reads the
|
|
327
|
+
// exit code, so returning 0 there while a human gets 1 would make the
|
|
328
|
+
// undelivered block invisible to the caller most likely to act on it —
|
|
329
|
+
// reintroducing, behind a flag, the silent failure this exists to remove.
|
|
330
|
+
// One sink failing among several stays a warning: the channels are
|
|
331
|
+
// redundant by design.
|
|
332
|
+
const undelivered = blockDeliveryFailure(flags.blocked, outcomes);
|
|
333
|
+
if (undelivered)
|
|
334
|
+
process.exitCode = 1;
|
|
281
335
|
if (flags.json) {
|
|
282
336
|
console.log(JSON.stringify(outcomes.length ? { ...event, broadcast: outcomes } : event, null, 2));
|
|
337
|
+
if (undelivered)
|
|
338
|
+
console.error(chalk.red(undelivered));
|
|
283
339
|
return;
|
|
284
340
|
}
|
|
285
341
|
console.log(formatProgressUpdate(event));
|
|
286
342
|
reportBroadcast(outcomes);
|
|
343
|
+
if (undelivered)
|
|
344
|
+
console.error(chalk.red(undelivered));
|
|
287
345
|
}
|
|
288
346
|
catch (err) {
|
|
289
347
|
console.error(chalk.red(err.message));
|
|
@@ -516,6 +574,21 @@ function broadcastPostedEvent(event, level) {
|
|
|
516
574
|
});
|
|
517
575
|
return runFeedBroadcast(planned);
|
|
518
576
|
}
|
|
577
|
+
/**
|
|
578
|
+
* Mirror a declared block to the same sinks a post reaches.
|
|
579
|
+
*
|
|
580
|
+
* Blocks previously never broadcast at all: `broadcastPostedEvent` ran only for
|
|
581
|
+
* `feed post`, while every `publishBlock` call wrote to the ledger and stopped
|
|
582
|
+
* there — so a "needs you" record was durable and invisible at the same time.
|
|
583
|
+
*/
|
|
584
|
+
function broadcastBlock(block, extras) {
|
|
585
|
+
const config = readMeta().feed?.broadcast;
|
|
586
|
+
if (!config || Object.keys(config).length === 0)
|
|
587
|
+
return [];
|
|
588
|
+
const ticket = getSessionById(block.sessionId)?.ticketId;
|
|
589
|
+
const ctx = blockBroadcastContext({ ...block, ticket: block.ticket ?? ticket }, extras);
|
|
590
|
+
return runFeedBroadcast(planFeedBroadcast(config, ctx));
|
|
591
|
+
}
|
|
519
592
|
/** One line per sink that ran. Silent when nothing is configured. */
|
|
520
593
|
function reportBroadcast(outcomes) {
|
|
521
594
|
for (const o of outcomes) {
|
|
@@ -11,4 +11,31 @@
|
|
|
11
11
|
* native harness registry. The `agents profiles` tree stays unchanged.
|
|
12
12
|
*/
|
|
13
13
|
import type { Command } from 'commander';
|
|
14
|
+
import { type Profile } from '../lib/profiles.js';
|
|
15
|
+
/**
|
|
16
|
+
* Print one custom harness. Shared by `agents harness view <name>` and by
|
|
17
|
+
* `agents view <name>` — a custom harness resolves as an agent type there, so
|
|
18
|
+
* both entry points must describe it identically.
|
|
19
|
+
*/
|
|
20
|
+
export declare function renderHarnessDetail(name: string): void;
|
|
21
|
+
/** Options accepted by `agents harness fork`. */
|
|
22
|
+
export interface ForkOptions {
|
|
23
|
+
model?: string;
|
|
24
|
+
baseUrl?: string;
|
|
25
|
+
authProvider?: string;
|
|
26
|
+
version?: string;
|
|
27
|
+
label?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
keyStdin?: boolean;
|
|
30
|
+
force?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build the new harness for `agents harness fork <source> <name>`.
|
|
34
|
+
*
|
|
35
|
+
* Two sources, one verb: an existing custom harness is copied and overridden;
|
|
36
|
+
* a native agent id is turned into a harness pinned to `--model` on that host.
|
|
37
|
+
* Forking a native harness therefore requires `--model` — there is nothing to
|
|
38
|
+
* copy a model from.
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildFork(source: string, name: string, opts: ForkOptions): Profile;
|
|
14
41
|
export declare function registerHarnessCommands(program: Command): void;
|