@phnx-labs/agents-cli 1.22.52 → 1.22.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.
Files changed (180) hide show
  1. package/CHANGELOG.md +336 -0
  2. package/README.md +42 -9
  3. package/dist/bootstrap.js +55 -154
  4. package/dist/cli/command-registry.d.ts +5 -0
  5. package/dist/cli/command-registry.js +8 -1
  6. package/dist/commands/accounts.js +220 -174
  7. package/dist/commands/apply.js +6 -3
  8. package/dist/commands/auth-mint.d.ts +8 -0
  9. package/dist/commands/auth-mint.js +96 -0
  10. package/dist/commands/auth.js +5 -1
  11. package/dist/commands/browser.js +1 -1
  12. package/dist/commands/cost.js +8 -2
  13. package/dist/commands/daemon.js +2 -2
  14. package/dist/commands/doctor.js +6 -1
  15. package/dist/commands/exec.js +26 -17
  16. package/dist/commands/fleet-capture.js +7 -0
  17. package/dist/commands/focus.d.ts +1 -0
  18. package/dist/commands/focus.js +4 -2
  19. package/dist/commands/go.d.ts +5 -4
  20. package/dist/commands/go.js +8 -7
  21. package/dist/commands/insights.js +9 -0
  22. package/dist/commands/monitors.js +85 -30
  23. package/dist/commands/output.js +8 -2
  24. package/dist/commands/repo.js +18 -0
  25. package/dist/commands/secrets.js +33 -14
  26. package/dist/commands/sessions-inject.js +8 -3
  27. package/dist/commands/sessions-picker.js +2 -1
  28. package/dist/commands/sessions.d.ts +20 -12
  29. package/dist/commands/sessions.js +94 -40
  30. package/dist/commands/setup-accounts.d.ts +8 -0
  31. package/dist/commands/setup-accounts.js +47 -0
  32. package/dist/commands/setup.d.ts +1 -1
  33. package/dist/commands/setup.js +11 -2
  34. package/dist/commands/share.d.ts +52 -3
  35. package/dist/commands/share.js +262 -18
  36. package/dist/commands/ssh.d.ts +7 -0
  37. package/dist/commands/ssh.js +53 -14
  38. package/dist/commands/status.js +14 -0
  39. package/dist/commands/sync.js +44 -0
  40. package/dist/commands/view.d.ts +3 -1
  41. package/dist/commands/view.js +5 -4
  42. package/dist/lib/account-registry.d.ts +15 -5
  43. package/dist/lib/account-registry.js +165 -53
  44. package/dist/lib/accounting/rotate.d.ts +20 -6
  45. package/dist/lib/accounting/rotate.js +38 -7
  46. package/dist/lib/accounting/usage.d.ts +37 -1
  47. package/dist/lib/accounting/usage.js +71 -6
  48. package/dist/lib/agent-spec/agents.d.ts +5 -2
  49. package/dist/lib/agent-spec/agents.js +25 -7
  50. package/dist/lib/analytics/mix-commands.js +12 -6
  51. package/dist/lib/answer-router.js +2 -1
  52. package/dist/lib/auth-mint.d.ts +150 -0
  53. package/dist/lib/auth-mint.js +434 -0
  54. package/dist/lib/browser/profiles.d.ts +18 -0
  55. package/dist/lib/browser/profiles.js +26 -1
  56. package/dist/lib/browser/registry.d.ts +44 -14
  57. package/dist/lib/browser/registry.js +141 -45
  58. package/dist/lib/browser/remote-control.d.ts +9 -7
  59. package/dist/lib/browser/remote-control.js +9 -7
  60. package/dist/lib/claude-account-token.d.ts +10 -0
  61. package/dist/lib/claude-account-token.js +14 -4
  62. package/dist/lib/config-drift.d.ts +37 -0
  63. package/dist/lib/config-drift.js +72 -0
  64. package/dist/lib/daemon/auth-sync-service.d.ts +19 -0
  65. package/dist/lib/daemon/auth-sync-service.js +34 -0
  66. package/dist/lib/daemon/daemon.js +30 -4
  67. package/dist/lib/daemon/runner.js +10 -2
  68. package/dist/lib/daemon-services.d.ts +1 -1
  69. package/dist/lib/daemon-services.js +5 -0
  70. package/dist/lib/device-config.d.ts +3 -3
  71. package/dist/lib/device-config.js +8 -7
  72. package/dist/lib/devices/config-migration.js +147 -1
  73. package/dist/lib/devices/connect.d.ts +26 -0
  74. package/dist/lib/devices/connect.js +48 -1
  75. package/dist/lib/devices/device-docs.d.ts +35 -0
  76. package/dist/lib/devices/device-docs.js +163 -0
  77. package/dist/lib/devices/discovery-policy.d.ts +14 -2
  78. package/dist/lib/devices/discovery-policy.js +31 -21
  79. package/dist/lib/devices/doctor-findings.d.ts +5 -1
  80. package/dist/lib/devices/doctor-findings.js +19 -1
  81. package/dist/lib/devices/registry.d.ts +11 -5
  82. package/dist/lib/devices/registry.js +46 -18
  83. package/dist/lib/exec.d.ts +88 -30
  84. package/dist/lib/exec.js +138 -34
  85. package/dist/lib/feed/feed.d.ts +10 -2
  86. package/dist/lib/feed/feed.js +35 -2
  87. package/dist/lib/feed-broadcast.js +1 -1
  88. package/dist/lib/fleet/apply.d.ts +11 -0
  89. package/dist/lib/fleet/apply.js +23 -3
  90. package/dist/lib/fleet/auth-sync.js +5 -3
  91. package/dist/lib/help.d.ts +9 -0
  92. package/dist/lib/help.js +29 -1
  93. package/dist/lib/hosts/dispatch.d.ts +4 -3
  94. package/dist/lib/hosts/dispatch.js +12 -8
  95. package/dist/lib/hosts/passthrough.d.ts +1 -10
  96. package/dist/lib/hosts/passthrough.js +1 -13
  97. package/dist/lib/hosts/providers/local.d.ts +9 -3
  98. package/dist/lib/hosts/providers/local.js +23 -12
  99. package/dist/lib/hosts/reconnect.d.ts +7 -4
  100. package/dist/lib/hosts/reconnect.js +29 -25
  101. package/dist/lib/hosts/registry.js +4 -1
  102. package/dist/lib/hosts/remote-os.js +3 -1
  103. package/dist/lib/installations/versions.js +9 -1
  104. package/dist/lib/linux-userns.d.ts +58 -0
  105. package/dist/lib/linux-userns.js +116 -0
  106. package/dist/lib/memory.d.ts +26 -0
  107. package/dist/lib/memory.js +80 -1
  108. package/dist/lib/monitors/config.d.ts +11 -0
  109. package/dist/lib/monitors/config.js +8 -0
  110. package/dist/lib/monitors/engine.js +8 -1
  111. package/dist/lib/monitors/state.d.ts +37 -1
  112. package/dist/lib/monitors/state.js +79 -4
  113. package/dist/lib/permissions-registry.d.ts +2 -0
  114. package/dist/lib/permissions-registry.js +116 -14
  115. package/dist/lib/permissions.d.ts +5 -3
  116. package/dist/lib/permissions.js +25 -27
  117. package/dist/lib/profiles.d.ts +8 -7
  118. package/dist/lib/profiles.js +12 -0
  119. package/dist/lib/project-key.d.ts +9 -0
  120. package/dist/lib/project-key.js +11 -0
  121. package/dist/lib/secrets/bundles.d.ts +35 -0
  122. package/dist/lib/secrets/bundles.js +78 -1
  123. package/dist/lib/secrets/push.d.ts +3 -8
  124. package/dist/lib/secrets/push.js +18 -14
  125. package/dist/lib/secrets/remote.d.ts +9 -18
  126. package/dist/lib/secrets/remote.js +11 -26
  127. package/dist/lib/secrets/reserved-sync.d.ts +65 -0
  128. package/dist/lib/secrets/reserved-sync.js +129 -0
  129. package/dist/lib/self-heal/checks/hook-manifest.d.ts +2 -0
  130. package/dist/lib/self-heal/checks/hook-manifest.js +56 -0
  131. package/dist/lib/self-heal/registry.js +4 -0
  132. package/dist/lib/self-heal/types.d.ts +1 -1
  133. package/dist/lib/session/active.d.ts +10 -1
  134. package/dist/lib/session/active.js +8 -5
  135. package/dist/lib/session/actor-sidecar.d.ts +7 -0
  136. package/dist/lib/session/actor-sidecar.js +2 -0
  137. package/dist/lib/session/db.d.ts +39 -4
  138. package/dist/lib/session/db.js +168 -31
  139. package/dist/lib/session/discover.d.ts +32 -4
  140. package/dist/lib/session/discover.js +126 -37
  141. package/dist/lib/session/insights.d.ts +14 -0
  142. package/dist/lib/session/insights.js +25 -2
  143. package/dist/lib/session/linear.js +1 -1
  144. package/dist/lib/session/live-metadata.js +1 -0
  145. package/dist/lib/session/pid-registry.d.ts +7 -0
  146. package/dist/lib/session/prompt.d.ts +15 -0
  147. package/dist/lib/session/prompt.js +21 -0
  148. package/dist/lib/session/shell-programs.d.ts +17 -0
  149. package/dist/lib/session/shell-programs.js +21 -0
  150. package/dist/lib/session/state.js +2 -1
  151. package/dist/lib/session/stream-render.js +2 -1
  152. package/dist/lib/session/tool-calls.js +2 -5
  153. package/dist/lib/session/trajectory-html.js +2 -1
  154. package/dist/lib/session/trajectory.js +3 -12
  155. package/dist/lib/session/types.d.ts +25 -0
  156. package/dist/lib/session/types.js +10 -0
  157. package/dist/lib/share/publish.d.ts +53 -5
  158. package/dist/lib/share/publish.js +99 -17
  159. package/dist/lib/share/worker-template.js +594 -64
  160. package/dist/lib/startup/root-command.js +2 -1
  161. package/dist/lib/state.d.ts +24 -0
  162. package/dist/lib/state.js +318 -54
  163. package/dist/lib/sync-status.d.ts +4 -0
  164. package/dist/lib/sync-status.js +3 -0
  165. package/dist/lib/terminal/resolve.d.ts +7 -0
  166. package/dist/lib/terminal/resolve.js +41 -2
  167. package/dist/lib/traces/classify.js +24 -19
  168. package/dist/lib/traces/insights.d.ts +67 -0
  169. package/dist/lib/traces/insights.js +178 -0
  170. package/dist/lib/traces/phenotype.d.ts +67 -0
  171. package/dist/lib/traces/phenotype.js +437 -0
  172. package/dist/lib/traces/segments.d.ts +133 -0
  173. package/dist/lib/traces/segments.js +301 -0
  174. package/dist/lib/traces/sync.d.ts +33 -0
  175. package/dist/lib/traces/sync.js +11 -2
  176. package/dist/lib/types.d.ts +47 -1
  177. package/dist/lib/usage-refresh.js +2 -1
  178. package/dist/lib/view-types.d.ts +2 -0
  179. package/dist/lib/watchdog/runner.js +18 -4
  180. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,341 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.22.54
4
+
5
+ - **`agents accounts mint claude` (also `agents auth mint claude`) mints a
6
+ setup-token and seeds it — the first-class close of the mint-auth recipe
7
+ (PHNX-2364).** `claude setup-token` is driven through the same injectable PTY
8
+ driver `agents fleet login` uses; the authorize URL is opened on this machine;
9
+ the captured value is accepted only when it is a single-line `sk-ant-oat01-…`
10
+ token (the #1767 TTY-banner capture is refused). The command seeds a named
11
+ provider account (policy never) for `agents run --account` / `agents accounts
12
+ sync` **and** the reserved FILE-BASED `auth` bundle keyed per email, which
13
+ usage/probe reads. `--token-stdin` skips the browser dance; `--fleet` /
14
+ `--device` syncs both bundles to workers. `--json` (including `--code --json`)
15
+ emits only the machine-readable result on stdout — progress URLs are
16
+ suppressed, matching `agents fleet login`. Native rotating OAuth is never
17
+ copied. Other harnesses fail loud (`agents fleet login` for device-code,
18
+ `agents accounts add` for API keys). `agents setup accounts` surfaces the
19
+ same step in the onboarding hub. Source: `cli/src/lib/auth-mint.ts`,
20
+ `cli/src/commands/auth-mint.ts`, `cli/src/commands/setup-accounts.ts`.
21
+
22
+ - **Reserve the `auth` secrets bundle as file-backed (PHNX-2365 / SEC-GAP-3).**
23
+ `agents secrets create/import auth` now forces the file backend and fails
24
+ loud on keychain or vault, instead of letting a keychain-backed `auth`
25
+ bundle be silently ignored by usage/probe. `resolveClaudeSetupToken` throws
26
+ the same error rather than returning null, and `agents doctor` emits
27
+ `auth-bundle-wrong-backend`. Source: `cli/src/lib/secrets/bundles.ts`,
28
+ `cli/src/lib/claude-account-token.ts`.
29
+
30
+ - **Auto-sync the reserved `auth` bundle across the fleet (PHNX-2371).**
31
+ File-backend `secrets export --device` no longer forwards
32
+ `AGENTS_SECRETS_PASSPHRASE` (that keyed remotes to a secret their daemons
33
+ lacked while still printing "Imported N key(s)"). Import and remote push
34
+ now read-back-verify decryptability. The daemon `auth-sync` service, plus
35
+ `fleet apply` and `repo push/pull user`, push a local file-backed `auth`
36
+ bundle to pinned devices that lack it. Source: `cli/src/lib/secrets/reserved-sync.ts`,
37
+ `cli/src/lib/secrets/remote.ts`, `cli/src/lib/daemon/auth-sync-service.ts`.
38
+
39
+ - **The menu-bar helper no longer permanently forgets refresh children after a crash (PHNX-2413).** Child ownership is persisted before spawn in a versioned, cross-process-locked registry, completed with PID/PGID/start-time/executable/argv identity, and recoverable from an unguessable provenance marker carried by the group-leading supervisor if the helper dies in the spawn-to-registration window. Reaping now retains individual ambiguous entries and registry I/O failures for retry, and deletes a child only after its process group is confirmed absent. Existing two-field records migrate conservatively. Source: `cli/menubar/Sources/MenubarHelper/ChildProcess.swift`.
40
+
41
+ - **Honor an OpenCode custom-harness `--model` pin (PHNX-2577).** `agents run`
42
+ of a harness forked from opencode now forwards the pinned model as
43
+ `--model` on `opencode run`. OpenCode does not read `OPENCODE_MODEL`, so the
44
+ pin previously vanished and the host used its configured default. Source:
45
+ `cli/src/lib/profiles.ts`, `cli/src/lib/exec.ts`.
46
+
47
+ - **`agents accounts` now fails with a clean CLI error instead of an uncaught Node stack (PHNX-2578).** A missing or locked `--from-secrets` bundle, or `inspect` of an unknown name, prints the user-facing message and exits 1. Ctrl+C at the add/set-key secret prompt still exits 130 with no error. Source: `cli/src/commands/accounts.ts`.
48
+
49
+ - **`agents monitors add` no longer auto-starts the daemon when `daemon.enabled` is false (PHNX-2637).** The kill switch already stopped `routines add`/`start`/`catchup` and webhook auto-start; `monitors add` called `startDaemon()` directly and brought the daemon up (and wrote the systemd autostart symlink) on a box whose config disabled it. Auto-start now goes through the same `assertDaemonEnabled()` gate: the monitor is still created, the refusal names `agents daemon enable`, and the engine-pickup wait is skipped. Source: `cli/src/commands/monitors.ts`.
50
+
51
+ - **Unpinned dispatch no longer dies on a logged-out default version (PHNX-2685).** Version selection now consults per-device auth state: a logged-out (or revoked) workspace/global default yields to a signed-in sibling on the execution device instead of spawning into a credential-less home, and fails loud naming each excluded version when nothing is signed in. `--strategy pinned` still forces a *rate-limited* default; an explicit `@version` pin is unchanged. Off macOS, a Claude home whose `.credentials.json` is missing (and which has no setup-token) reports signed out even when leftover `.claude.json` still names an email. Source: `cli/src/lib/accounting/rotate.ts`, `cli/src/lib/agent-spec/agents.ts`, `cli/src/commands/exec.ts`.
52
+
53
+ - **Codex `permissions list` now shows deny rules, and a later apply with no deny clears them (PHNX-2703).** `toCanonical` used to skip `.codex/rules/agents-deny.rules`, so `agents permissions list codex` omitted every forbid the writer had already emitted. The reader now inverts that file, and the writer deletes it when a later apply has an empty deny so a removed forbid does not persist. Source: `cli/src/lib/permissions-registry.ts`, `cli/src/lib/permissions.ts`.
54
+
55
+ - **Content search returns indexed transcripts the listing page missed (PHNX-2767).** `agents sessions "tmux pane"` intersected FTS hits with the in-memory pool (cwd-scoped, default-capped at 50), so grep-visible project transcripts dropped out and the query returned 0. FTS hits are now unioned with the pool and hydrated from the index when the page does not already hold them. `--project` / `--agent` / `--routine` still apply after that union, so a content hit outside the requested scope does not reappear as a multi-match. Source: `cli/src/lib/session/discover.ts`, `cli/src/commands/sessions.ts`.
56
+
57
+ - **A non-ASCII share title / `--label` / `--meta` value now publishes in full instead of degrading to `(unnamed)` or dropping glyphs (PHNX-2786).** `publishToEndpoint` still sends the latin1-safe folded value in `x-share-<field>` (so an already-deployed Worker keeps rendering exactly as before — the ByteString crash stays fixed), and now ALSO sends a percent-encoded `x-share-<field>-u` companion plus an `x-share-encoding: percent` opt-in whenever the fold was lossy. An updated Worker (`agents artifacts share update`) prefers the decoded companion, so a Japanese/Arabic/Hindi title or an emoji renders as written; a Worker that predates the scheme ignores the unknown headers and folds gracefully. `--meta` rides its whole raw object in one percent-encoded `x-share-meta-u` companion. Source: `cli/src/lib/share/publish.ts`, `cli/src/lib/share/worker-template.ts`.
58
+
59
+ - **Claude Code's native per-project memory now survives an agent version upgrade (PHNX-2817).** `getVersionHomePath` gives every installed Claude version its own isolated HOME, so `.claude/projects/<project-key>/memory/*.md` — the freeform notes Claude writes into itself, distinct from the `agents memory` resource — was a THIRD, unmanaged per-version copy: a note written under one version was invisible under another (empty dir, no symlink, no copy). Every version home's copy is now a symlink into one canonical dir shared across versions for that project, wired into the same sync path as `agents memory` (`agents sync` / `agents run` / `agents doctor --fix`); a pre-existing real directory's content is migrated into the canonical dir once instead of discarded. Source: `cli/src/lib/memory.ts`, `cli/src/lib/installations/versions.ts`.
60
+
61
+ - **`agents monitors` `--run` no longer records `ok` just because the agent
62
+ exited 0 (PHNX-2842).** A fire's frozen `ok` was reconciled against the run's
63
+ later status (RUSH-2690), but `completed` still counted as success — so a
64
+ merge-on-green monitor whose agent ran, hunted the fleet, and never merged
65
+ showed `ok` while the PR stayed OPEN. `run`/`routine` actions now take an
66
+ optional `postcondition` (CLI: `--postcondition <cmd>`, YAML:
67
+ `action.postcondition`) — a shell command, `{event}` interpolated, that must
68
+ exit 0 after the run settles. `agents monitors runs`/`view` then show `no
69
+ effect` (not `ok`) when it fails, and persist the result on the fire record.
70
+ `--run` without a postcondition still records `ok` on `completed` and warns
71
+ at add time. Source: `cli/src/lib/monitors/{state,config,engine}.ts`,
72
+ `cli/src/commands/monitors.ts`.
73
+
74
+ Provider accounts created with `accounts add` now resolve silently in headless
75
+ `--account` launches on macOS. Their `policy never` credentials are stored
76
+ without a biometry ACL and are read through the policy-aware secrets bundle
77
+ path instead of being rejected by the raw-keychain Touch ID guard.
78
+
79
+ - **`agents ssh <box> agents browser …` is gated by `browser.remote-control` (PHNX-3065).** The `--device` passthrough already stamped `AGENTS_FLEET_REMOTE=1` so the far-side consent gate could refuse a drive when remote-control is off. `agents ssh` built its invocation with only askpass vars in the local env — which OpenSSH does not forward — so a browser drive over ssh skipped the gate entirely. `buildSshInvocation` now prefixes an `agents browser` / `ag browser` / standalone `browser` remote command with the same `markFleetRemote` helper the fan-out uses (`env AGENTS_FLEET_REMOTE=1 …` on POSIX, `$env:AGENTS_FLEET_REMOTE='1'; …` on PowerShell). Non-browser commands and interactive logins stay unmarked. Source: `cli/src/lib/devices/connect.ts`, `cli/src/lib/hosts/passthrough.ts`.
80
+
81
+ - **Feed terminal answers now write a resolution tombstone (PHNX-3074).**
82
+ The TypeScript `recordAnswer` / `removeBlock` paths already recorded
83
+ `resolutions/<id>.json` before clearing an open block, but the Python
84
+ `UserPromptSubmit` hook (the path that fires when a human types in the
85
+ TUI) unlinked the block with no tombstone. A later `reconcileAttention`
86
+ consumer would then resurrect that generation from a stale session
87
+ re-read at the same cursor. The hook now writes an `answered` tombstone
88
+ (generation + `sourceCursor` from the still-present block) before
89
+ unlink, matching the TS writer. Source: `cli/src/lib/feed/feed.ts`.
90
+
91
+ - **Daemon PATH now includes `~/.rush/bin` and `~/.local/bin` (PHNX-3075).**
92
+ systemd/launchd pin a PATH that never sources a login shell, so `which rush`
93
+ failed inside the daemon even when an interactive shell on the same box
94
+ resolved `~/.rush/bin/rush`. Monitor `notify` actions and dispatched
95
+ `agents run` children inherit that PATH and silently never delivered
96
+ (`rush CLI not found on PATH`) while the watcher kept polling. Both
97
+ user-bin dirs now sit after the agents shim and Node dirs (so a
98
+ `~/.local/bin/agents` cannot shadow the daemon binary) and before the
99
+ platform system dirs. Source: `cli/src/lib/daemon/daemon.ts`.
100
+
101
+ - **Kimi expired usage state says "run Kimi once" and `--json --refresh` exposes the refresh result (PHNX-3198).** An expired Kimi OAuth credential used to render "re-auth for usage", even though a normal Kimi launch refreshes its own token. The usage error now reads "run Kimi once". `agents view <agent> --refresh --json` now actually forces a live refresh and includes a `usageError` field on each version so programmatic consumers see why a snapshot is missing. Source: `cli/src/lib/accounting/usage.ts`, `cli/src/commands/view.ts`, `cli/src/lib/view-types.ts`.
102
+
103
+ - **Share metadata can be corrected without republishing (PHNX-3278).** `agents artifacts share edit <target>` now sets/removes labels and merges, replaces, or removes validated arbitrary metadata through an authenticated Worker `PATCH`. The Worker streams the exact stored body back to R2 while preserving HTTP metadata, publication time, visibility, expiry, provenance, cover, and revisions, and deliberately creates no revision. `agents artifacts share list --meta key=value` is repeatable and filters exact metadata values in both human and `--list-json` output. Reserved keys and missing targets fail loudly.
104
+
105
+ - **Fail loud when codex's Linux sandbox can't start, instead of landing zero tools (PHNX-3285).**
106
+ Codex ≥0.146 sandboxes its `read-only` and `workspace-write` runs on Linux with a
107
+ bundled bubblewrap that needs an unprivileged user namespace. On Ubuntu 23.10+
108
+ (`kernel.apparmor_restrict_unprivileged_userns=1`) that's denied, so bwrap dies with
109
+ `bwrap: setting up uid map: Permission denied` and a **headless** codex run — an
110
+ `agents teams` codex teammate, or `agents run codex`, always headless + sandboxed —
111
+ burned a turn and wrote nothing while still reporting a completed turn. `agents run`
112
+ now preflights the box before spawning a headless sandboxed codex and **fails loud**
113
+ with the one-time fix, rather than silently under-delivering. The preflight is scoped
114
+ to codex + Linux + a headless + sandboxed (non-`skip`) run — an interactive TUI, a
115
+ `--mode skip` (no-sandbox) run, macOS/Windows, and every other harness are untouched,
116
+ and the intended `auto`=workspace-write + `approval_policy=never` config is preserved,
117
+ never downgraded to `danger-full-access`. `cli/scripts/enable-codex-sandbox.sh`
118
+ re-enables unprivileged userns on a box (sysctl drop-in, idempotent, self-verifying)
119
+ so codex's sandbox works with its isolation intact; run it once per fleet worker.
120
+ Source: `cli/src/lib/linux-userns.ts`, `cli/src/lib/exec.ts` (`codexSandboxPreflight`),
121
+ `cli/scripts/enable-codex-sandbox.sh`.
122
+
123
+ - **New `hook-manifest` self-heal check — a hook registered at an unresolvable path no longer fails silently.** `resolveHookScriptPath` only resolves a manifest `script:` under `<root>/hooks/`, and `resolveContainedHookPath` rejects any candidate escaping that root; an entry pointing anywhere else returns `null` and the hook is dropped with no error, no warning, and no trace in `agents doctor`. This is how `main-branch-guard` — declared in `agents.yaml` as `rules/subrules/truly-agentic-git-workflow/main-branch-guard.sh`, authored and covered by a 100+ case suite — reached zero of 25 settings files across three machines, and stayed there until four agent sessions had already written into a primary checkout it exists to prevent. The check walks the manifest, resolves every relative `script:`, and reports each one that resolves to nothing. Detect-only: guessing where a script was meant to live could wire the wrong file into a `PreToolUse` gate, so it names the broken entry instead. Absolute scripts (subrule-composed hooks, which bypass the `hooks/` resolver) and `enabled: false` entries are skipped. Source: `cli/src/lib/self-heal/checks/hook-manifest.ts`. (PHNX-3312)
124
+
125
+ - **Device-scope is now the DEFAULT for new config keys, not a hardcoded set (PHNX-3315).**
126
+ Routing a `Meta` key used to hinge on a decorative `Record<keyof Meta, 'central' | 'device'>`
127
+ string map whose values nothing consumed — the real split was a hand-written
128
+ destructure, so a newly added key that nobody wired silently landed in the synced
129
+ top-level `agents.yaml` (`central`), the very trap behind the recurring per-box
130
+ churn and key-loss. Classification is now an OPT-IN `CENTRAL_META_KEYS` allowlist
131
+ that DRIVES routing: a key not explicitly marked fleet-shared is device-scoped by
132
+ default and round-trips through this box's `devices/<host>/agents.yaml` under its
133
+ own name, with no bespoke wiring — so a slipped-through key can never leak to the
134
+ shared file. A compile-time check keeps the classification exhaustive (a nudge,
135
+ not a safety gate — the runtime default is still the safe per-box file), and a
136
+ foreign key an older CLI does not model but a newer one wrote to central is left
137
+ in place and preserved verbatim (never relocated). Behavior-identical for every
138
+ existing key. Source: `cli/src/lib/state.ts`.
139
+ - **`agents sync status` now surfaces config drift — a box that hasn't drained its device-scoped state (PHNX-3315).**
140
+ The one-shot fold-and-delete migrations (P1's frozen-header heal + central
141
+ `browser:` tombstone drain; P2's `fleet.discovery`/`fleet.ignored`, `hosts:`, and
142
+ device-scoped `accounts:` folds) run on config access, so a converged box shows
143
+ nothing — but a box that never re-synced kept carrying that state centrally, and
144
+ it was INVISIBLE: the report only showed per-agent "N missing", so the un-drained
145
+ box was found the hard way, as a mystery `agents repo pull` conflict. A distinct
146
+ drift line now flags (1) a stale top-level `agents.yaml` header (!= the current
147
+ header) and (2) any lingering central `fleet` / `hosts` / `accounts` (device-scoped)
148
+ / `browser` block that should have folded into the device doc. The detector is
149
+ read-only and never triggers the migration; the machine-readable
150
+ `UnifiedSyncStatus` gains a `config` field. Source: `cli/src/lib/config-drift.ts`
151
+ (new), `cli/src/lib/{sync-status,state}.ts`, `cli/src/commands/status.ts`.
152
+
153
+ - **Root `--help` now surfaces the measured front-door groups and points to the full surface (PHNX-3320).** `agents --help` renders the ten most-used groups (Quick start: setup, view, run, sessions; Most-used: teams, browser, secrets, devices, accounts, add) and a pointer to `agents --help-all`, which lists all 69 command groups. Previously 29 groups were never mentioned on root help. Source: `cli/src/lib/help.ts`, `cli/src/cli/command-registry.ts`, `cli/src/bootstrap.ts`.
154
+
155
+ - **`agents focus <id> --attach-only` now names the real session id in its recovery hint when the live row has not registered one yet (PHNX-3356).** A live IDE/plain-terminal session with no `sessionId` used to print the literal `agents sessions resume <id>` placeholder even though the caller already had the indexed id. Attach-only still refuses rather than forking. Source: `cli/src/commands/go.ts`, `cli/src/commands/focus.ts`.
156
+
157
+ - **Every `agents insights` subcommand now honors `--json` / `--since` / `--by` (PHNX-3360).** These flags share their long names with the `insights` parent command, so commander bound them to the parent at parse time and the subcommand actions — which read their own `opts()` — never saw them: `--json` printed the human table (invalid for machine callers), `--by` fell back to the default agent grouping, and `--since` was ignored (7d default). The whole group is affected — `output`, `cost`, `mix`, `trends`, `recipes`, `query`, and the per-recipe leaves — and all now read `command.optsWithGlobals()`. Unblocks the `yc:workweave` shipped-output engine, which shells `agents insights output --by project --json`. Source: `cli/src/commands/output.ts`, `cli/src/commands/cost.ts`, `cli/src/lib/analytics/mix-commands.ts`.
158
+
159
+ - **`agents sessions` content search now indexes what the agent ANSWERED, not just what you asked (PHNX-3363).** `session_text`'s extractor pushed only `role === 'user'` text — every harness parser's assistant branch recorded tokens/skills/tools but never the reply itself — so `agents sessions "<an assistant-only phrase>"` returned 0 hits even when the transcript on disk plainly had it. Every harness parser (Claude, Codex, Gemini, Rush, Hermes, Muse, Droid, Cursor) now accumulates the agent's answer text the same way it already accumulates the user's prompt text, into a new `session_text.assistant` FTS5 column with a lower BM25 weight than `content` (an equivalent user-prompt match still ranks first). A `scan_ledger.extractor_version` + `CONTENT_INDEX_VERSION` (`lib/session/db.ts`) lever makes the change-detector (`filterChangedEntries`) re-extract an already-indexed, byte-unchanged session on its next scan — the mechanism that backfills every existing session's assistant text without a destructive `DELETE FROM scan_ledger` (which would also drop the Claude/Codex resumable `parser_state`). `ftsSearch`'s FTS5-tier hit now carries a short bm25 `snippet()` excerpt (auto-picking the best-matching column), surfaced on `SessionMeta.snippet` and included, unlike `_matchedTerms`/`_bm25Score`, in `--json` output. Source: `cli/src/lib/session/db.ts`, `cli/src/lib/session/discover.ts`.
160
+
161
+ - **`agents artifacts share visibility <target> <level>` changes a published page's visibility in place.** Until now the only way to re-scope an already-shared page (public ↔ unlisted ↔ me ↔ org) was a full re-publish or a delete. The new verb re-stamps only the visibility on the stored object: the slug — and so the URL — is preserved, the body is untouched, and (like `agents artifacts share edit`) it is a metadata-only rewrite that creates no revision. It reuses the delete target parser (full URL, `<user>/<slug>`, or a bare slug in your namespace) and the same `PATCH` metadata-edit route/client — visibility is a first-class edit field alongside `label`, never a `--meta` entry (`visibility` is reserved). `me`/`org` require a Phoenix session and fail loud with an `agents auth login` hint when signed out; `org` is refused on a public-inbox email domain. A BYO endpoint whose deployed Worker predates the visibility edit fails loud (`agents artifacts share update`) rather than silently reporting success. **Adds a `visibility` field to the Worker `PATCH` route, so already-provisioned BYO endpoints must run `agents artifacts share update` to pick it up (their template reads `outdated` until then); the managed endpoint is updated by the release.** Source: `cli/src/commands/share.ts`, `cli/src/lib/share/worker-template.ts`, `cli/src/lib/share/publish.ts`.
162
+
163
+ - **The cross-platform CI matrix moved off the release path to a nightly lane (PHNX-3237).** The six-job `ci.yml` matrix (ubuntu + macOS + Windows × Node 22/24) used to fire on every `release/**` branch — 16-53 min of macOS-10×/Windows-2×-billed work that gated nothing: it is not a required check, and `release.sh` gates the merge on `test` + `gitleaks` and the publish on an exact-tree attestation, never this matrix. Worse, a macOS-only failure had it red on every recent release, adding a false "release is failing" signal. It now runs on a nightly schedule plus manual `workflow_dispatch`, so cross-platform regressions are still caught without putting slow, billed, non-gating work on the release. Source: `.github/workflows/ci.yml`, `.github/workflows/ci.test.ts`.
164
+
165
+ - **A release-tree attestation can now inherit the suite result instead of re-running the full suite (PHNX-3237).** An ordinary release ran the ~13k-test suite twice — once for the default-branch tree, once for the `chore(release)` commit tree — even though the second differs from the first only by the version bump, the folded changelog, and the regenerated command-index. New `release-attestation.sh derive --base <green-attestation.json> --tarball <release.tgz>` mints the release-tree record by inheriting the base's suite pass, verifying that the tree diff touches **only** `package.json`, `.changelog/**`, `CHANGELOG.md`, and `docs/command-index.{md,json}` — it fails closed on any other changed path, so a code change can never ride a stale pass. `release-attestation-produce.sh --inherit-suite-from <base.json>` uses it to skip the suite while still building + packing the real release-tree tarball, cutting the second full-suite run per release. The derived record inherits the base's lockfile/policy/toolchain identity, which the allowlist proves are byte-identical to the release tree's, so the release's `require()` still keys to it exactly. Source: `cli/scripts/release-attestation.sh`, `cli/scripts/release-attestation-produce.sh`.
166
+
167
+ - **`agents view` names why a Claude usage bar is empty instead of printing a blanket "usage unavailable" (#2987).** Three distinct states shared one generic label, so an operator chasing empty bars was sent to `claude setup-token` for a remedy that cannot work. (1) A signed-in Claude account with no provisioned setup-token reported the shared `usageNoCredentialError` — "sign in, or provision a long-lived token" — advice whose first half the usage path will never honour: a read-only caller resolves only a file-based setup-token and never the interactive login (RUSH-1822), so an account that IS signed in still reads as unreadable. It now reports `usage unavailable (no usage credential)` and states both constraints, including that a setup-token cannot read usage either (RUSH-2392). (2) `headless-scope` was listed with the generic bucket in `formatUsageErrorKindLabel`, so the RUSH-2392 phrase appeared only on the surface that also passes the `headless` flag — every other caller printed "usage unavailable" for the same account. It now renders `usage unavailable (headless)` from the classified kind alone. (3) The read-only cache-miss sentinel (`'stale'`, now the exported `USAGE_NOT_COLLECTED_MARKER`) had no classification arm and fell through to `'rejected'`, so a cold cache — no request made, nothing failed — rendered as a failure; it classifies as `not-collected` and renders `usage pending`. Source: `cli/src/lib/accounting/usage.ts`, `cli/docs/secrets.md`.
168
+
169
+ - **`agents insights` now breaks the `Bash` lump down by binary.** The tool mix used to render every shell call as one undifferentiated `Bash` bar (and one `failed tool loop: Bash` line), which said nothing about *what* ran. Two new sections split it by the actual executable + subcommand, reusing the existing `bucketKey` classifier: **Shell commands** (`git commit`, `gh pr`, `agents ssh`, `find`, `ssh→git pull`, …) and **Shell command failures** (which binary was running when a tool loop failed). It is cross-harness — keyed on the command string, which Codex's `exec_command` carries too, not just Claude's `Bash`. The Actions block also now prints a one-line pointer to drill a sample session id into its full timeline (`agents sessions trace <id>`); the report itself stays aggregate by contract (SES-IF-4c). Source: `cli/src/lib/session/insights.ts`, `cli/src/commands/insights.ts`.
170
+
171
+ - **`release.sh --apply` from a non-interactive shell now fails loudly instead of exiting 0 having published nothing (PHNX-3176).** The confirmation gate reads `[y/N]` from stdin; with no TTY (a backgrounded run, a sandbox, `< /dev/null`) `read` gets EOF, the prompt default-declines, and the script exited **0** having done nothing — no attestation, no PR, no tag, no publish. A caller that backgrounded the release, checked `$?`, and saw 0 then believed a release shipped when none did, which is exactly how a silent version gap opens. `--apply` without a TTY and without `--yes` now aborts with a non-zero exit before any work, naming `--yes` as the sanctioned non-interactive path. The internal `--home-base-phase` / `--orchestration-phase` re-execs are exempt (they inherit the already-checked stdin). Source: `cli/scripts/release.sh`.
172
+
173
+ - **The release attestation no longer dead-loops on a changed `computer-mac` helper (PHNX-2943).** `release-attestation-produce.sh` never rebuilds `computer-mac` (it is signed on a separate macOS path), so when its source drifted it died telling the operator to run `publish-computer-helper-mac.sh` and re-run — but that script recorded nothing, so the re-run hit the identical error forever (workaround used live cutting 1.22.43). The publish script now uploads a `computer-mac-input-digest.txt` sidecar naming the exact source it built from, and the producer records the published binary against the current source **only after** that sidecar matches (verifying the downloaded zip against its `.sha256`). A mismatch, a missing sidecar, or an undownloadable release fails closed with the exact publish command, so a stale binary can never be attested against changed source. Source: `cli/scripts/release-attestation-produce.sh`, `cli/scripts/publish-computer-helper-mac.sh`.
174
+
175
+ - **`release.sh` now refuses to fold the changelog queue when an earlier release's version-bump PR is still open, closing a cross-version note-misattribution (PHNX-3084).** The decoupled release (RUSH-2395) merges the version-bump PR asynchronously after publish; if that merge is stuck on a CHANGELOG conflict, v1.2.3's `.changelog/next/*` fragments stay queued on `main`, and a later `release.sh 1.2.4` re-read them and folded v1.2.3's notes under v1.2.4. The same-target `STUCK_BUMP_PR` retry only ever queried `release/v<current-target>`, so it was blind to an OTHER version's stuck PR. Before folding, `release.sh` now detects any other open `release/v*` bump PR (via the new unit-tested `scripts/release-other-bump-prs.sh`) and fails loud with the exact merge command, rather than silently re-attributing an earlier version's release notes. Source: `cli/scripts/release.sh`, `cli/scripts/release-other-bump-prs.sh`.
176
+
177
+ - **Shared images / videos / PDFs now get the attribution bar too, via a viewer page (PHNX-3332).** The attribution bar only injects into HTML; a directly-shared non-HTML asset had no "shared by / visibility" chrome. Now a **browser** navigating to a shared image, video, audio, or PDF gets a lightweight viewer page — the same attribution bar on top, the asset centered below. A **non-browser fetch** (Accept without `text/html`: an `<img src>`, an OG crawler, curl) still gets the **raw bytes**, so embeds and social previews are never broken; the viewer's own media element points back at the asset with `?raw` (which also re-runs the me/org gate, so a private asset stays private inside its own viewer). Non-viewable types (JSON, arbitrary downloads) are served raw. Source: `cli/src/lib/share/worker-template.ts`.
178
+
179
+ - **The share attribution bar now always shows the sharer's avatar.** Every shared page (and every asset viewer page) renders an avatar in the bar next to "Shared by …": a coloured initials circle derived from the handle, always present, so a share is never faceless — especially useful for an org/company share where knowing *who* posted it matters. When the sharer is signed in, the CLI stamps a Gravatar (keyed on a SHA-256 of the lowercased email — only the hash lands in public metadata, never the raw address) so a real profile photo layers over the initials; if the user has no Gravatar the photo request 404s and the bar cleanly falls back to the initials circle (`onerror`, honoured because the share page sets no CSP). Source: `cli/src/lib/share/worker-template.ts` (render + `x-share-avatar` → `customMetadata.avatar`), `cli/src/lib/share/publish.ts` (`resolveShareAvatar`). Deployed by re-running `agents artifacts share update`; a hosted-photo pipeline for users without a Gravatar is tracked as a follow-up.
180
+
181
+ - **Shared pages now show page stats and let the owner change visibility inline.** The attribution bar injected on every shared HTML page gained a right-side stats cluster — `👁 <n> views · updated <rel>` — where views is a per-slug visitor count (owner views and `?raw`/embed fetches are not counted) and the relative time comes from the page's last write. For the page's owner (the signed-in viewer whose handle is the namespace), the visibility chip becomes a live control: click it for an inline dropdown of Public / Unlisted / Only you / Anyone at your org, pick a level, and the bar PATCHes the same in-place edit route `share visibility` uses (with `credentials:'include'`), flipping the chip optimistically and reverting with the server's error text on failure (e.g. org from a public-inbox domain). Everyone else keeps today's static read-only cue. The view counter is a separate `__views/<path>` R2 object, so counting a view never rewrites the page (which would corrupt "last updated"). Source: `cli/src/lib/share/worker-template.ts` (`renderAttributionBar` owner control + stats, `readViews`/`writeViews`, `gateVisibility`, owner-cookie write principal in `authorizeWrite`). Because this changes the Worker template, a deployed endpoint reads `outdated` until its owner redeploys with **`agents artifacts share update`** — a pure Worker update, no new bindings.
182
+
183
+ - **`agents artifacts share list` can now show the owner's hidden pages.** The default remains the public gallery (`--scope public`). Pass `--scope unlisted|me|org` or `--all` to include pages that are hidden from the public listing; the CLI sends the owner's bearer and a `scope=mine` hint to the Worker's JSON listing route, which returns hidden pages only after verifying the caller owns the namespace. Each human row now shows the page's visibility so public vs hidden is obvious at a glance. Source: `cli/src/commands/share.ts` (`--scope`/`--all` filter, owner bearer forwarding, visibility column), `cli/src/lib/share/worker-template.ts` (`resolveListingScope` + owner-gated hidden inclusion in `renderListing`). The Worker change only takes effect on a live endpoint after its owner runs `agents artifacts share update`.
184
+
185
+ ---
186
+ type: fix
187
+ ---
188
+
189
+ `agents sessions trace` now recognizes a session's shell steps consistently across every surface. The "is this tool a shell command?" test lived as six hand-synced copies that had drifted apart — the tool-call indexer treated Codex's `exec`/`execute`/`run_command` as shell, but the trajectory model, the directory-touched scan, and both the HTML and stream renderers each used a narrower or differently-cased list. They now share one case-insensitive `isShellExecTool` predicate (`shell-programs.ts`), so a Codex or Droid shell step is colored, program-resolved, and counted the same as Claude's `Bash` everywhere the trace reads it.
190
+
191
+ - **Phoenix Evals topic buckets now read as human task types, not five generic labels.** The session-topic classifier (`agents traces sync`) collapsed every code session into `Engineering` and every ops session into `Operations`, so the console treemap showed coarse, robotic labels. `classifyTopic` now emits the taxonomy the approved console mockup always intended — **Feature work · Bug fixes · Refactor · Debugging · Code review · Release · Blog & docs · Fleet / ops** — as an ordered rules table (still metadata/tool-mix only, no transcript read), keyed within the five stable `TraceTopicGroup` groups so the treemap grouping and existing consumers don't move. One-time: a session previously bucketed as `engineering`/`operations`/`content` re-keys on next sync, so `bucketHistory`/`driftSignals` show a single-day discontinuity the day this ships, then track the new keys. Source: `cli/src/lib/traces/classify.ts`.
192
+
193
+ ## 1.22.53
194
+
195
+ - **Custom-harness runs launched via a profile now show their real harness name in agents sessions instead of 'claude' (PHNX-2935).**
196
+
197
+ - **Watchdog un-addressable stalls now include the same recovery command as `agents focus` (PHNX-3070).**
198
+ Rotate, needs-human declared-block, and nudge-refuse paths append `addressabilityRecoveryHint` so an un-addressable session prints `agents sessions resume <id>` / tmux wrap instead of a reason-only skip. Source: `cli/src/lib/watchdog/runner.ts`.
199
+
200
+ - **Un-addressable live sessions now surface a clear recovery hint at the point of use** — `agents focus`, `sessions inject`, and the session picker report when a bare interactive session has no addressable terminal rail (not tmux/iTerm/an IDE terminal/a pty sidecar) instead of failing silently, and the hint now names the session's real id in `agents sessions resume <id>` rather than a placeholder (PHNX-3070).
201
+
202
+ - **Vitest can no longer launch the developer's browser (PHNX-3072).** `tests/setup.ts`
203
+ sandboxed `HOME` but not desktop-opener binaries, so a test that reached a real
204
+ `spawn('open' | 'xdg-open')` opened the URL on the developer's machine — green on
205
+ Linux CI, where `xdg-open` is absent, and disruptive locally. The harness now
206
+ prepends stub `open` / `xdg-open` / `gnome-open` binaries to `PATH` so a spawn
207
+ cannot reach the real handler; an unauthorized spawn fails the file. Source:
208
+ `cli/tests/opener-sandbox.ts`.
209
+
210
+ - **Feed attention stamps `sourceCursor` at block-write time (PHNX-3073).**
211
+ `coveredByResolution` conservatively suppresses a candidate with no comparable
212
+ cursor when a tombstone exists, which is correct for a stale lifecycle re-read
213
+ but buried a genuinely new open block whenever `session.lastActivityMs` was
214
+ unresolvable (cloud / remote / index-lag) because neither writer stamped a
215
+ cursor. `buildDeclaredBlock` and the feed-publish hook now stamp
216
+ `sourceCursor.lastActivityMs` from the write instant, so a fresh generation
217
+ compares strictly newer than any prior tombstone. Source:
218
+ `cli/src/lib/feed/feed.ts`.
219
+
220
+ - **Cursor auto-titles go through the same skill-scaffolding cleaner as Claude (PHNX-3123).**
221
+ `readCursorMeta` took `chatMeta.title` verbatim, so a Cursor session whose
222
+ server-generated title echoed the injected `Base directory for this skill: …`
223
+ line kept that path as `SessionMeta.label` — the field that wins on every
224
+ surface for the session's whole life. Claude's `ai-title` was already collapsed
225
+ to `/<skill>` at label composition (PR #2995); that collapse now lives in one
226
+ shared helper (`cleanGeneratedSessionLabel`) and Cursor's title takes the same
227
+ path. An ordinary Cursor title, and one that merely names a `skills/…` path,
228
+ is unchanged. A Claude `/rename` (`custom-title`) is still never rewritten.
229
+ Codex / OpenCode / Kimi / Droid put auto-titles on `topic`, not `label`, so
230
+ they are out of this change. **Not retroactive:** already-indexed labels stay
231
+ until the transcript next changes (RUSH-3122). Source:
232
+ `cli/src/lib/session/prompt.ts`, `cli/src/lib/session/discover.ts`.
233
+
234
+ - **Traces insight engine — cross-session failure clustering + wasted time (PHNX-3141).** `agents traces sync`'s `index.json` now carries `failurePatterns[]` (top-K, ranked by wasted time, not raw occurrence count — a rare 8-hour retry loop still outranks a frequent 2-minute one), `wastedMsTotal`, and `latency` (time-to-first-tool p50/p90/p99/max). Failed `tool_calls` are grouped by `(tool, cause, normalized error)` so near-identical failures (a GitHub rate-limit message with a different countdown each time) fold into one pattern instead of a wall of near-duplicates. Per-session `SessionDetail` also gains `surfacedToolFailures` — every failed step, listed regardless of the run's overall outcome, so a session that ultimately succeeded still shows the tool failures it hit along the way. Source: `cli/src/lib/traces/insights.ts`, `cli/src/lib/traces/sync.ts`.
235
+
236
+ - **Traces insight group-by dimensions (PHNX-3141).** `cli/src/lib/traces/segments.ts` derives the console bar axes — agent (model × harness), task type, failure timing — and the time-to-first-tool latency insight (`firstToolMs` p50/p90/p99/max). Pure functions; the sync integrator wires them into the shard. Source: `cli/src/lib/traces/segments.ts`.
237
+
238
+ - **Native-account labels live on the central `accounts.native` rows in agents.yaml (already fleet-synced via `agents repo push/pull`). `remove`/`rename`/`label` operate on every row matching `(agent, identityKey)`, so a post-merge duplicate cannot silently survive (PHNX-3206).**
239
+
240
+ - **The top-level `agents.yaml` header heals itself instead of freezing (PHNX-3315).**
241
+ `serializeCentral` parses the committed file to preserve its hand-written body
242
+ comments, but that also preserved the leading metadata header verbatim — so a
243
+ top-level `agents.yaml` written before the `agi-cli` rename kept its stale
244
+ 3-line header (repo `agents-cli`, no `$schema:` line) forever while every
245
+ freshly-written device doc got the current header. The header is now rewritten
246
+ to the current `META_HEADER` on the next central write, textually (the `yaml`
247
+ library folds the leading comment onto the first key when that key already
248
+ carries a comment, so `doc.commentBefore` is unreliable), leaving body comments
249
+ and every config key intact, and byte-stable once healed so it never re-churns.
250
+ Source: `cli/src/lib/state.ts`.
251
+ - **The legacy central `browser:` tombstone drains itself on `agents sync` (PHNX-3315).**
252
+ New browser profiles write the per-device doc, but profiles created before the
253
+ device-scoped store lingered in the shared top-level `agents.yaml` and churned
254
+ every fleet pull until someone ran `agents browser profiles claim` by hand.
255
+ `agents sync` now auto-evicts a lingering central profile into this device's
256
+ doc and clears it from central — but ONLY for remote (`ssh://`) profiles. An
257
+ `ssh://` endpoint names a specific host, so the profile is fungible by design:
258
+ any box resolves it to the same browser, so a concurrent cross-machine
259
+ double-claim is harmless. A local/`cdp://` profile has no per-machine ownership
260
+ signal ("that browser is installed here" is not "I hold this profile's
261
+ credentialed session"), so auto-claiming it would let two boxes with the same
262
+ common browser each claim the same tombstone and flip a credentialed profile
263
+ from identity-bearing to fungible fleet-wide — the exact logged-out-browser
264
+ failure this module exists to prevent. Local/cdp tombstones are left central for
265
+ an explicit `agents browser profiles claim`. The claim is selected and committed
266
+ inside a single meta-lock acquisition, and non-throwing (a config conflict or
267
+ unhostable profile is left central rather than wedging the sync). `profileRegistry` is left as the single source of truth: a
268
+ claimed profile lives in the device doc alone, never double-counted across two
269
+ stores. Source: `cli/src/lib/browser/registry.ts`, `cli/src/commands/sync.ts`,
270
+ `cli/src/lib/state.ts`.
271
+
272
+ - **`fleet.discovery` / `fleet.ignored` are device-scoped with a union-on-read view (PHNX-3315).**
273
+ Every box used to rewrite one shared `fleet.discovery`/`fleet.ignored` map in the
274
+ top-level `agents.yaml` (each dismissal stamped `ignoredOn:<host>`), so two boxes
275
+ dismissing different peers produced guaranteed git line-conflicts on the next
276
+ pull. Each box now records ONLY its own discovery decisions and dismissals in its
277
+ tracked device doc (`devices/<host>/agents.yaml` `fleet:` block), and the
278
+ effective fleet view is recomputed as a deterministic, order-independent UNION
279
+ across every device doc at read time — `ignored` beats `approved` for a
280
+ conflicting name, and the newest `ignoredAt` wins a shared dismissal — so every
281
+ box computes the identical view and no box ever rewrites another's file. A
282
+ fold-then-delete migration moves the existing central maps into the owning box's
283
+ device doc before the central keys are dropped (the device-doc write lands before
284
+ the central strip, so a crash re-folds idempotently). `agents devices
285
+ register/ignore/unignore/ignored`, discovery reconcile, the sync pending-diff, and
286
+ `agents fleet capture` all read the union. Source:
287
+ `cli/src/lib/devices/{discovery-policy,registry,device-docs,config-migration}.ts`,
288
+ `cli/src/commands/{ssh,fleet-capture}.ts`, `cli/src/lib/state.ts`.
289
+ - **Agent-host registrations are device-scoped (`deviceHosts`) with a merged read (PHNX-3315).**
290
+ Locally-discovered SSH hosts and inline registrations landed in one shared
291
+ `hosts:` map in the top-level `agents.yaml`, so every box that enrolled a host
292
+ rewrote the fleet-shared file. Registrations now write this box's device doc
293
+ `hosts:` block; the effective host directory is the cross-box union (newest
294
+ `addedAt` wins a name collision), and `remove` only edits this box's own doc. A
295
+ fold-then-delete migration relocates the existing central `hosts:` map into this
296
+ box's device doc and drops the central key. Source:
297
+ `cli/src/lib/hosts/{providers/local,registry,remote-os}.ts`,
298
+ `cli/src/lib/devices/{device-docs,config-migration}.ts`, `cli/src/lib/state.ts`.
299
+ - **`scope:'device'` native accounts move off the shared file into the device doc (PHNX-3315).**
300
+ `addNativeAccount` accepted `scope:'device'` but always wrote the central
301
+ `accounts.native` map, so a per-box login (and its email / `identityKey` PII)
302
+ landed in the git-tracked, fleet-synced `agents.yaml`. A native login is
303
+ machine-local, so its home now follows its scope: a `scope:'device'` identity
304
+ (and any binding that targets it) writes this box's device doc `accounts:` block,
305
+ keeping its identity PII off the shared file; genuinely fleet-shared account
306
+ `defaults` and `scope:'version'` identities stay central. Every account reader
307
+ (`listNativeAccounts`, `findUnifiedAccount`, binding resolution, rename/remove)
308
+ merges the fleet-shared central store with this box's device slice. A
309
+ fold-then-delete migration relocates existing `scope:'device'` natives and their
310
+ bindings out of central (forward-only; no history rewrite). Source:
311
+ `cli/src/lib/account-registry.ts`, `cli/src/lib/devices/config-migration.ts`,
312
+ `cli/src/lib/state.ts`.
313
+
314
+ - **`tmux.enabled` now gates remote `--device` runs too — no more forced tmux wrap (PHNX-3316).**
315
+ `resolveTmuxWrap` (`cli/src/lib/exec.ts`) no longer lets `remoteDispatch`
316
+ override the device's `tmux.enabled`: off (the default) means a followed
317
+ `agents run --device <host>` spawns the agent directly, with no tmux session
318
+ on the peer. Durability for a bare remote run comes from the existing
319
+ auto-reconnect in `cli/src/lib/hosts/reconnect.ts`, whose target
320
+ (`agents sessions focus <id> --local`) already resumes the harness session in
321
+ place when no pane exists — a dropped link costs the in-flight turn, not the
322
+ conversation. One case still wraps regardless: a followed remote run whose
323
+ launcher has no TTY (CI, scripts, another agent driving the CLI), where the
324
+ detached pane is the run's only interface.
325
+ `undurable` is now reached only when a wrap was actually wanted (opted-in
326
+ device, or the no-TTY case) but the peer has no tmux; its refusal message
327
+ names the `agents ssh <host> 'agents devices config <host> tmux.enabled off'`
328
+ alternative (the key is machine-local, so it must be set on the peer)
329
+ alongside `--raw`.
330
+ RUSH-3125's forced wrap conflated durability with an ergonomics preference
331
+ and wrapped boxes whose operator had explicitly left tmux off.
332
+ `hosts/reconnect.ts`'s header premise, the `hosts/dispatch.ts`
333
+ REMOTE_INTERACTIVE_ENV comments, and the `tmux.enabled` help text
334
+ (`device-config.ts`) are updated to match; wrap-matrix tests cover the new
335
+ gate.
336
+
337
+ - **Share attribution bar: full-width chrome, not a floating box.** The bar was injected `position:sticky` inside `<body>`, so on a page whose body is a narrow centered column (`max-width` + `margin:auto`) it inherited that width and rendered as a floating black rectangle. It's now `position:fixed`, full viewport width, with an injected `html{padding-top}` pushing the page content down beneath it — clean top chrome on any page layout. `flex-wrap:nowrap` + left-side ellipsis keep it one line so the push height stays correct. Source: `cli/src/lib/share/worker-template.ts`.
338
+
3
339
  ## 1.22.52
4
340
 
5
341
  - **`agents artifacts share --visibility me|org` — identity-gated share pages (PHNX-3260).**
package/README.md CHANGED
@@ -194,7 +194,7 @@ agents run claude@ --device auto # pick the device, then choose there
194
194
  agents run claude@ --device yosemite-s0 # choose from one device's accounts
195
195
  ```
196
196
 
197
- `--strategy balanced` spreads work across available versions of the same agent -- useful when you have multiple accounts and want to avoid burning through one. When a Claude run reports a session limit, agents-cli records the stated reset time, shows `session-limited` in `agents view`, and excludes that account until the reset. When every account is rate-limited, the run exits nonzero naming each excluded account and the earliest window reset (use `--strategy pinned` to force the default) -- it never launches into an exhausted account.
197
+ `--strategy balanced` spreads work across available versions of the same agent -- useful when you have multiple accounts and want to avoid burning through one. When a Claude run reports a session limit, agents-cli records the stated reset time, shows `session-limited` in `agents view`, and excludes that account until the reset. When every account is rate-limited, the run exits nonzero naming each excluded account and the earliest window reset (use `--strategy pinned` to force a rate-limited default) -- it never launches into an exhausted account. A logged-out default is never forced: unpinned dispatch picks a signed-in version on the execution device instead of dying on a credential-less default home.
198
198
 
199
199
  ### Don't care which harness? `agents run auto`
200
200
 
@@ -523,7 +523,7 @@ agents add claude@2.0.65 # Install a specific version
523
523
  agents add codex@latest # Install latest
524
524
  agents add codex@oldest # Install the oldest published version
525
525
  agents view # See everything installed
526
- agents accounts add work --provider anthropic --auth setup-token
526
+ agents accounts mint claude --account work # mint a setup-token into a named account
527
527
  agents run claude --account work
528
528
  ```
529
529
 
@@ -1070,6 +1070,8 @@ Distinct from **Accounts** below: this is *your human identity*; those are the *
1070
1070
  Give a provider credential a durable name once, reuse it everywhere -- across harnesses, across machines.
1071
1071
 
1072
1072
  ```bash
1073
+ agents accounts mint claude # drive `claude setup-token`, seed a named account + reserved auth bundle
1074
+ agents accounts mint claude --token-stdin # already have a token
1073
1075
  agents accounts add work --provider anthropic --auth setup-token
1074
1076
  agents accounts add gateway --provider openrouter --auth api-key \
1075
1077
  --from-secrets openrouter.ai:OPENROUTER_API_KEY # import from an existing secrets bundle
@@ -1083,9 +1085,11 @@ agents run claude --account work
1083
1085
  agents harness add deepinfra --account deepinfra
1084
1086
  ```
1085
1087
 
1088
+ `agents accounts mint claude` (also `agents auth mint claude`) is the first-class replacement for the mint-auth recipe: it drives `claude setup-token` in a PTY, captures a well-formed `sk-ant-oat01-` token, and seeds both the named account and the reserved file-based `auth` bundle that usage/probe reads. Native rotating OAuth is never copied.
1089
+
1086
1090
  One provider account **is** one `agents secrets` bundle -- `agents accounts add` creates it with secrets policy `never`, so a background agent launch on that account never raises Touch ID. `agents accounts` (no subcommand) lists provider bundles next to harness-native signed-in identities so you see both kinds of credential together; `accounts list` / `inspect <name>` / `set-key <name>` (rotate) / `rename` / `remove` manage a bundle by its stable id, independent of its current label. `accounts switch <harness>` is the fast picker over that default -- it writes the same binding as `set-default`, and balanced rotation already honors it.
1087
1091
 
1088
- Harness-native OAuth logins (Claude Code's own `/login`, `codex login`, and so on) stay exactly where the harness put them -- agi-cli discovers and displays them but never copies, renames, or converts them into a provider bundle. Native `accounts name` / `attach` is only for harnesses agents-cli can isolate today (claude, codex, grok; muse when an email is present). A device-scoped login (kimi, droid, …) is refused with a named reason; provider `accounts add --provider` is unrestricted. `accounts sync <name> --device <device>` is the only way a provider account crosses machines, and it's explicit: nothing syncs automatically. Selection order for a run is explicit `--account`, then `accounts set-default` / `switch` for that harness, then the harness's native/balanced account behavior.
1092
+ Harness-native OAuth logins (Claude Code's own `/login`, `codex login`, and so on) stay exactly where the harness put them -- agi-cli discovers and displays them but never copies, renames, or converts them into a provider bundle. Native `accounts name` / `attach` is only for harnesses agents-cli can isolate today (claude, codex, grok; muse when an email is present). A device-scoped login (kimi, droid, …) is refused with a named reason; provider `accounts add --provider` is unrestricted. Native **labels** (`agents accounts label codex personal`) bind to a stable identityKey on the central account row in `agents.yaml`, which `agents repo push/pull` already syncs fleet-wide, so `codex#personal` selects the same login on every box. `accounts sync <name> --device <device>` is the only way a **provider** account credential crosses machines, and it's explicit: nothing copies OAuth automatically. Selection order for a run is explicit `--account`, then `accounts set-default` / `switch` for that harness, then the harness's native/balanced account behavior.
1089
1093
 
1090
1094
  ---
1091
1095
 
@@ -1219,7 +1223,7 @@ on `SIGHUP reload`.
1219
1223
  There is no `agents daemon jobs` -- scheduled work is always `agents routines`
1220
1224
  (see `agents routines stats` for per-routine failure detail). `disable` is a
1221
1225
  device-local kill switch: with it set, `routines add`/`routines start`/
1222
- `routines catchup`/webhook triggers stop auto-starting the daemon, mirroring
1226
+ `routines catchup`/`monitors add`/webhook triggers stop auto-starting the daemon, mirroring
1223
1227
  `systemctl disable` -- `agents daemon start` still works as the explicit
1224
1228
  override.
1225
1229
 
@@ -1242,6 +1246,12 @@ agents monitors add ci-red \
1242
1246
  --run claude --prompt 'CI failed: {event}. Diagnose and fix.' \
1243
1247
  --device yosemite-s0
1244
1248
 
1249
+ # Merge-on-green: ok only if the PR actually merged, not just because the agent exited 0
1250
+ agents monitors add merge-1682 \
1251
+ --poll 'gh pr view 1682 --json state --jq .state' 2m --match OPEN \
1252
+ --run claude --prompt 'Rebase-merge #1682: {event}' \
1253
+ --postcondition 'gh pr view 1682 --json state --jq .state | grep -qx MERGED'
1254
+
1245
1255
  # A fleet box goes unreachable or overloaded -> notify (watch the fleet itself)
1246
1256
  agents monitors add box-down --watch-device mac-mini --on-change --notify telegram
1247
1257
 
@@ -1253,7 +1263,7 @@ agents monitors test ci-red # Dry-run: evaluate the source once, show what it
1253
1263
  agents monitors list # Every monitor: source, owner device, last fired
1254
1264
  ```
1255
1265
 
1256
- Sources: a command's stdout (`--watch` / `--poll`), an HTTP endpoint (`--poll-http`), a file (`--watch-file`), or a fleet device's reachability + load (`--watch-device`). Push sources -- a signed webhook (`--on`) and a WebSocket (`--ws`) -- are accepted today and delivered through a receiver wired in a follow-up. Conditions: fire on any change (`--on-change`), on a regex (`--match`), or `--every` tick -- deduped by a native state store, so a monitor stays silent until something *actually* changes. Actions: `--run <agent>` (the event is injected into the prompt as `{event}`), `--routine`, `--notify`, or `--webhook-out`. Pin a monitor to one owner device with `--device` (exactly-once), or offload the action elsewhere with `--run-on`. Runs in the routines daemon; `agents monitors pause` / `resume` any time.
1266
+ Sources: a command's stdout (`--watch` / `--poll`), an HTTP endpoint (`--poll-http`), a file (`--watch-file`), or a fleet device's reachability + load (`--watch-device`). Push sources -- a signed webhook (`--on`) and a WebSocket (`--ws`) -- are accepted today and delivered through a receiver wired in a follow-up. Conditions: fire on any change (`--on-change`), on a regex (`--match`), or `--every` tick -- deduped by a native state store, so a monitor stays silent until something *actually* changes. Actions: `--run <agent>` (the event is injected into the prompt as `{event}`), `--routine`, `--notify`, or `--webhook-out`. A `--run`/`--routine` action can take `--postcondition '<cmd>'` — a shell command that must exit 0 after the agent settles, otherwise `agents monitors runs` records `no effect` rather than `ok`. Pin a monitor to one owner device with `--device` (exactly-once), or offload the action elsewhere with `--run-on`. Runs in the routines daemon; `agents monitors pause` / `resume` any time.
1257
1267
 
1258
1268
  ---
1259
1269
 
@@ -1270,8 +1280,13 @@ agents traces setup # provision the priv
1270
1280
  agents artifacts share plan.html --slug fleet --expire 30d # → https://<base>/fleet
1271
1281
  agents artifacts share plan.html --label "Q3 fleet plan" --meta kind=plan # human title + structured metadata
1272
1282
  agents artifacts share plan.html --json # URL object for plan-render hooks
1273
- agents artifacts share list --agent claude # everything published, filterable
1283
+ agents artifacts share list --agent claude # public gallery, filterable
1284
+ agents artifacts share list --meta kind=plan # exact, repeatable metadata filters
1285
+ agents artifacts share list --all # include hidden unlisted/me/org pages
1286
+ agents artifacts share list --scope me # just the owner-only pages
1287
+ agents artifacts share edit fleet --label "Final fleet plan" --meta status=final
1274
1288
  agents artifacts share revisions fleet # prior versions kept under a slug
1289
+ agents artifacts share visibility fleet me # re-scope a published page in place (public|unlisted|me|org)
1275
1290
  agents artifacts share status # show the endpoint
1276
1291
  agents artifacts unshare fleet # take a published link (+ its OG cover) down
1277
1292
  ```
@@ -1285,7 +1300,19 @@ email (`muqsitnawaz@gmail.com` → `muqsitnawaz`); the page slug is readable plu
1285
1300
  short view-id. HTML is stored as one object: local images are inlined, `file://` TOC
1286
1301
  links become in-page hashes, so the published page is actually viewable. `--visibility unlisted` (hidden aliases
1287
1302
  `--unlisted` / `--private`) is a capability URL: GET still works, the gallery hides it,
1288
- and the Worker sends `X-Robots-Tag: noindex`. **BYO Cloudflare** remains: `setup` reads
1303
+ and the Worker sends `X-Robots-Tag: noindex`. `--visibility me` is visible only to
1304
+ you (the signed-in owner); `--visibility org` is visible to anyone at your email
1305
+ **domain** — derived from your own address, so it needs a **workspace** Google
1306
+ account and is refused on a public-inbox domain (`gmail.com`, `outlook.com`,
1307
+ `icloud.com`, …). Both need a Phoenix session and are identity-gated at read time (a
1308
+ stranger is redirected to sign in, then 404s). `share visibility <target> <level>`
1309
+ re-scopes an **already-published** page in place — the slug/URL is preserved and the
1310
+ body is untouched (a metadata-only change, like `share edit`, so no revision) — so you
1311
+ can promote a draft to `public` or pull a link back to `me`/`org` without re-publishing
1312
+ (`me`/`org` need a Phoenix session; `org` needs a workspace domain). Sign-in is a
1313
+ single **Phoenix ID** (Google-only device-code OAuth, `agents auth login`) — see
1314
+ [`docs/share.md`](cli/docs/share.md) for the full identity + visibility model.
1315
+ **BYO Cloudflare** remains: `setup` reads
1289
1316
  a Cloudflare API token from your `cloudflare.com` secrets bundle (or `--token`), creates
1290
1317
  an R2 bucket, uploads a tiny Worker, and enables the free `*.workers.dev` subdomain (or
1291
1318
  maps `--domain share.example.com` when the token owns the zone). Writes are bearer-gated
@@ -1304,8 +1331,14 @@ captured automatically from the exec env and git — never invented, only sent w
1304
1331
  present. `--label`/`--title` names a share (else one is derived from the HTML
1305
1332
  `<title>`, frontmatter, or filename, with a nudge — never a blocking prompt); `--meta
1306
1333
  key=value` attaches structured metadata (`kind`, `project`, `ticket`, `status`, ...).
1307
- `agents artifacts share list --agent <name> | --session <id> | --label-contains <substr>` filters
1308
- by any of it, so the listing is a real "what have I shared" gallery, not just slugs.
1334
+ `agents artifacts share list --agent <name> | --session <id> | --label-contains <substr>
1335
+ | --meta <key=value>` filters by provenance, title, or exact structured metadata, so
1336
+ the listing is a real "what have I shared" gallery, not just slugs. By default it mirrors
1337
+ the public gallery; `--scope unlisted|me|org` (or `--all`) includes your hidden pages and
1338
+ marks each row with its visibility so you can tell public from private at a glance.
1339
+ `agents artifacts share edit <slug>` changes only the label/arbitrary metadata in place:
1340
+ the URL, exact body, HTTP metadata, publication time, visibility, expiry, provenance,
1341
+ cover, and revision history stay unchanged.
1309
1342
  Republishing an existing slug keeps the prior version as a revision by default
1310
1343
  (`--no-revision` to skip); `agents artifacts share revisions <slug>` shows the retained
1311
1344
  history, newest first.