@phnx-labs/agents-cli 1.22.21 → 1.22.23
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 +256 -0
- package/README.md +8 -2
- package/dist/commands/doctor.js +15 -7
- package/dist/commands/exec.js +20 -6
- package/dist/commands/focus.d.ts +76 -4
- package/dist/commands/focus.js +219 -40
- package/dist/commands/fork.d.ts +20 -2
- package/dist/commands/fork.js +91 -64
- package/dist/commands/go.d.ts +25 -0
- package/dist/commands/go.js +63 -2
- package/dist/commands/harness-wizard.d.ts +206 -0
- package/dist/commands/harness-wizard.js +403 -0
- package/dist/commands/harness.d.ts +12 -0
- package/dist/commands/harness.js +97 -101
- package/dist/commands/insights.d.ts +32 -0
- package/dist/commands/insights.js +478 -0
- package/dist/commands/resume.js +10 -3
- package/dist/commands/secrets.js +25 -30
- package/dist/commands/sessions-picker.d.ts +2 -0
- package/dist/commands/sessions-picker.js +1 -0
- package/dist/commands/sessions-resume.d.ts +21 -3
- package/dist/commands/sessions-resume.js +50 -11
- package/dist/commands/sessions.d.ts +81 -5
- package/dist/commands/sessions.js +331 -66
- package/dist/commands/watchdog.js +13 -2
- package/dist/index.js +3 -1
- package/dist/lib/agents.d.ts +1 -1
- package/dist/lib/agents.js +132 -0
- package/dist/lib/codex-policy.d.ts +17 -0
- package/dist/lib/codex-policy.js +48 -0
- package/dist/lib/crabbox/lease.d.ts +25 -0
- package/dist/lib/crabbox/lease.js +62 -0
- package/dist/lib/daemon.js +70 -0
- package/dist/lib/exec.d.ts +4 -0
- package/dist/lib/exec.js +88 -54
- package/dist/lib/feed-broadcast.d.ts +1 -20
- package/dist/lib/feed-broadcast.js +31 -1
- package/dist/lib/hooks.js +12 -2
- package/dist/lib/hosts/passthrough.js +1 -0
- package/dist/lib/mcp.js +44 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +1 -5
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +2 -15
- package/dist/lib/models.d.ts +0 -5
- package/dist/lib/models.js +48 -0
- package/dist/lib/picker.d.ts +45 -0
- package/dist/lib/picker.js +75 -6
- package/dist/lib/plugin-marketplace.js +9 -0
- package/dist/lib/pricing/prices.json +119 -92
- package/dist/lib/pricing/table.js +13 -0
- package/dist/lib/remote-agents-json.d.ts +29 -1
- package/dist/lib/remote-agents-json.js +47 -10
- package/dist/lib/resources/mcp.js +2 -0
- package/dist/lib/resources/permissions.js +3 -0
- package/dist/lib/resources/types.d.ts +2 -1
- package/dist/lib/runner.js +28 -19
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -2
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -13
- package/dist/lib/secrets/index.d.ts +15 -1
- package/dist/lib/secrets/index.js +118 -49
- package/dist/lib/secrets/reaper.d.ts +87 -0
- package/dist/lib/secrets/reaper.js +184 -0
- package/dist/lib/secrets/remote.d.ts +29 -0
- package/dist/lib/secrets/remote.js +37 -1
- package/dist/lib/session/active.d.ts +36 -1
- package/dist/lib/session/active.js +60 -19
- package/dist/lib/session/actor-sidecar.d.ts +14 -0
- package/dist/lib/session/actor-sidecar.js +67 -4
- package/dist/lib/session/db.d.ts +35 -1
- package/dist/lib/session/db.js +132 -1
- package/dist/lib/session/digest.d.ts +3 -0
- package/dist/lib/session/digest.js +3 -3
- package/dist/lib/session/discover.js +168 -0
- package/dist/lib/session/insights.d.ts +126 -0
- package/dist/lib/session/insights.js +330 -0
- package/dist/lib/session/parse.d.ts +29 -2
- package/dist/lib/session/parse.js +111 -5
- package/dist/lib/session/remote-list.d.ts +10 -1
- package/dist/lib/session/remote-list.js +2 -8
- package/dist/lib/session/remote.d.ts +57 -3
- package/dist/lib/session/remote.js +90 -26
- package/dist/lib/session/resume-command.d.ts +6 -0
- package/dist/lib/session/resume-command.js +8 -0
- package/dist/lib/session/session-cache.d.ts +173 -0
- package/dist/lib/session/session-cache.js +399 -0
- package/dist/lib/session/types.d.ts +2 -2
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/session/width.d.ts +1 -1
- package/dist/lib/session/width.js +12 -2
- package/dist/lib/shims.d.ts +2 -2
- package/dist/lib/shims.js +40 -5
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/usage.d.ts +13 -0
- package/dist/lib/usage.js +254 -3
- package/dist/lib/versions.js +13 -2
- package/package.json +1 -1
- package/dist/bin/agents +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,261 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.22.23
|
|
4
|
+
|
|
5
|
+
- **Daemon-warmed cross-surface session-status cache (RUSH-2062).** Menubar,
|
|
6
|
+
Factory, watchdog, and CLI used to each re-run a full `sessions --active`
|
|
7
|
+
gather (~9s / ~170MB) with no sharing. The daemon now warms a local active-
|
|
8
|
+
session snapshot every 15s; those surfaces read the warm file when fresh.
|
|
9
|
+
`session/remote.ts` is cache-first too — a reachable host skips SSH while its
|
|
10
|
+
cache is within the freshness window (it used to replay only on
|
|
11
|
+
unreachable). Immutable per-session fields (topic, label, cwd, …) are memoized
|
|
12
|
+
by transcript mtime; live status never rides that memo. Source:
|
|
13
|
+
`apps/cli/src/lib/session/session-cache.ts`, `apps/cli/src/lib/session/remote.ts`,
|
|
14
|
+
`apps/cli/src/lib/daemon.ts`.
|
|
15
|
+
|
|
16
|
+
- **`agents sessions <id>` / `agents resume <id>` resolve across the fleet fast, with
|
|
17
|
+
early-exit and SSH cancellation (RUSH-2203).** The cross-machine resolve fanned out
|
|
18
|
+
with `await Promise.all` and no cancellation, so one slow or unreachable peer stalled
|
|
19
|
+
the whole lookup to the 12s per-peer timeout even after a fast peer already returned
|
|
20
|
+
the exact match — a full-UUID resolve on a fleet with an offline box took ~16-40s. A
|
|
21
|
+
**full-UUID** resolve (globally unique) now opts into a cancellable, early-exit
|
|
22
|
+
fan-out: the first peer holding it resolves the sweep and SIGTERMs every
|
|
23
|
+
still-outstanding peer, and a local UUID hit resolves with **zero** SSH.
|
|
24
|
+
`agents resume <label>` auto-resumes the one exact-label match; labels are not globally
|
|
25
|
+
unique, so they stay all-settle (a same-label session may live on another peer) — a
|
|
26
|
+
unique label resumes, a cross-machine collision surfaces as an ambiguity. The not-found
|
|
27
|
+
message reports the actual sweep result (devices searched, which were unreachable)
|
|
28
|
+
instead of the misleading "search with `--device <host>`" — fleet search is already the
|
|
29
|
+
default. `--device all` / `--devices` are accepted and mean "the whole fleet". The
|
|
30
|
+
all-settle default is unchanged for the shared fan-out's other callers (tool-search,
|
|
31
|
+
program-count) and for labels / ambiguous short-id prefixes, which still wait for every
|
|
32
|
+
peer. Source:
|
|
33
|
+
`apps/cli/src/lib/remote-agents-json.ts`, `apps/cli/src/lib/session/remote-list.ts`,
|
|
34
|
+
`apps/cli/src/commands/sessions.ts`, `apps/cli/src/commands/resume.ts`.
|
|
35
|
+
|
|
36
|
+
- **`agents sessions focus` gains device + live-state scoping and multi-select tabs
|
|
37
|
+
(RUSH-2206).** `focus` now honors `--device/--host <name>` (scope the picker to a
|
|
38
|
+
device's live sessions) and the same live-state filters `sessions --active` defines
|
|
39
|
+
(`--orphan`/`--crashed`/`--waiting`/`--idle`/`--working`/`--closed`/`--abandoned`/
|
|
40
|
+
`--queued`/`--unknown`) — they compose, so `agents sessions focus --orphan --device
|
|
41
|
+
yosemite-s0` lists only that host's orphans. The interactive picker is now
|
|
42
|
+
multi-select: check several sessions and each opens as a new tab in the terminal
|
|
43
|
+
you're in (Ghostty / iTerm / tmux, auto-detected), reusing `resume`'s batch open and
|
|
44
|
+
flood guard. Per tab, live semantics hold — a tmux session is joined (a second
|
|
45
|
+
client, no fork), local or remote over SSH; a session with no attach rail resumes a
|
|
46
|
+
copy in the tab, reported never silently dropped. `focus <id>` direct-jump and
|
|
47
|
+
`--attach-only` (old `go`) behavior are unchanged. Source:
|
|
48
|
+
`apps/cli/src/commands/focus.ts`, `apps/cli/src/commands/go.ts`.
|
|
49
|
+
|
|
50
|
+
- **`agents harness edit` is now wizard-capable, on a shared step engine (RUSH-2219).**
|
|
51
|
+
Run `agents harness edit <name>` with no flags in a terminal and it walks each field
|
|
52
|
+
(model, endpoint, auth, version, fallback, description) pre-filled with the harness's
|
|
53
|
+
current values — where before it was flag-only and errored without one. Both `add`/`fork`
|
|
54
|
+
and `edit` now drive one shared step engine: every step is skippable by the matching flag,
|
|
55
|
+
so scripting stays non-interactive and a flagless non-interactive `edit` still errors as
|
|
56
|
+
before. Endpoint and version prompts are gated by the host's API format (a host with no
|
|
57
|
+
custom-endpoint slot, or one that self-updates, shows the field disabled with a reason
|
|
58
|
+
instead of silently accepting a value the run would drop). This is the foundation the
|
|
59
|
+
model catalog, connection test, edit matrix, and cross-host portability build on.
|
|
60
|
+
Source: `apps/cli/src/commands/harness-wizard.ts`, `apps/cli/src/commands/harness.ts`.
|
|
61
|
+
|
|
62
|
+
- **Codex launches now default to safe writable access instead of an offline
|
|
63
|
+
read-only sandbox.** When no configured run default exists, omitting `--mode`
|
|
64
|
+
selects a managed workspace profile with network access, on-request approvals,
|
|
65
|
+
`~/.agents`, and regenerable build caches. Explicit `--mode plan` remains
|
|
66
|
+
filesystem-read-only but now retains network access. The same policy covers
|
|
67
|
+
`agents run`, fallback attempts, resumes, routines, direct `codex` shims,
|
|
68
|
+
versioned aliases, and Windows shim passthrough; only explicit `skip` disables
|
|
69
|
+
the sandbox and approvals. Source: `apps/cli/src/lib/codex-policy.ts`.
|
|
70
|
+
|
|
71
|
+
- **Phone forwards are shaped to a text, not a wall.** `agents feed post`
|
|
72
|
+
(`--level important` / `--blocked`), `agents notify`, and `agents send --to owner`
|
|
73
|
+
all forward to the owner's phone through one seam, `composeBroadcastMessage`; it now
|
|
74
|
+
keeps the post **title** (the scannable headline) and truncates a long **body** to a
|
|
75
|
+
short excerpt marked `… (full in feed)` (caps: 500 chars / 8 lines). The full post is
|
|
76
|
+
untouched in the feed — only the outbound phone copy is shortened — so a requested
|
|
77
|
+
long write is not lost, and enforcing it at this seam covers every sink (owner alias,
|
|
78
|
+
in-process `channel:`, spawned `command:` via `{message}`), which a per-command shell
|
|
79
|
+
hook cannot. Source: `apps/cli/src/lib/feed-broadcast.ts` (`truncateBroadcastBody`).
|
|
80
|
+
|
|
81
|
+
- **`agents run --lease` self-cleans its warm pool.** Each lease now auto-stops **expired, idle** boxes in the run's profile pool (an expired box can never be reused, so it was pure cost the 1h-idle `agents lease gc` window left running). Conservative: only a box that is running, in the same profile+netMode pool, past its lease expiry, and untouched for a grace window is stopped — a mid-boot or in-use box is never touched. Skipped when an explicit `--box <slug>` is named. Source: `apps/cli/src/lib/crabbox/lease.ts`.
|
|
82
|
+
|
|
83
|
+
- **Muse Code (Meta) harness support.** `muse` is a first-class agent: install via
|
|
84
|
+
`curl -fsSL https://dev.meta.ai/install.sh | sh`, run with `agents run muse`,
|
|
85
|
+
and use in teams. Modes map to Muse safety flags (`--disable-write` /
|
|
86
|
+
`--disable-approval` / `--yolo`); `--model` and `--reasoning-effort` are
|
|
87
|
+
forwarded; headless is `muse exec` with `--json`; interactive resume is
|
|
88
|
+
`muse resume <id>` (id immediately after the verb), headless resume is
|
|
89
|
+
`--session-id`. Sessions under `~/.local/share/muse/sessions/` (plus version
|
|
90
|
+
homes) are discovered and parsed; usage shows Meta Model API rate limits when
|
|
91
|
+
a key is present, otherwise local 7-day token totals. MCP writes to
|
|
92
|
+
`~/.config/muse/settings.json` (`mcp_servers`, `schema_version: 1`). Model
|
|
93
|
+
catalog: `muse-spark-1.2` (default), `1.1`, `1.2-contributor` with offline
|
|
94
|
+
pricing. Aliases: `muse-code`, `meta-muse`. Hooks write Claude-shaped
|
|
95
|
+
matcher groups into `~/.config/muse/settings.json` (`schema_version: 1`);
|
|
96
|
+
plugins use the Claude marketplace layout under the XDG data plugin store
|
|
97
|
+
(`~/.local/share/muse/plugins`) with `.muse-plugin` manifests. Allowlist
|
|
98
|
+
stays false (Muse uses approval-mode + sandbox, not tool-name allow/deny).
|
|
99
|
+
Source: `apps/cli/src/lib/{agents,exec,models,usage,mcp,hooks,plugins,
|
|
100
|
+
plugin-marketplace,runner,shims}.ts`, `apps/cli/src/lib/session/*`.
|
|
101
|
+
|
|
102
|
+
- **Release CI no longer re-runs the six-job cross-platform matrix on `v*` tags.**
|
|
103
|
+
The expensive `ci.yml` matrix (ubuntu + macOS + Windows × Node 22/24) still
|
|
104
|
+
runs on `release/**` branch pushes and manual `workflow_dispatch`, but not when
|
|
105
|
+
a version tag is pushed. `release.sh` tags the exact commit that already passed
|
|
106
|
+
the release-branch matrix, so the post-tag matrix was pure cost. Source:
|
|
107
|
+
`.github/workflows/ci.yml`.
|
|
108
|
+
|
|
109
|
+
- **`claude-opus-5` and `claude-sonnet-5` were unpriced, so every session using them
|
|
110
|
+
cost $0.** The pricing table carried `claude-opus-4`, `claude-sonnet-4`,
|
|
111
|
+
`claude-fable-5` and `claude-mythos-5` but not the Opus/Sonnet 5 line. Matching is
|
|
112
|
+
dash-bounded (`getModelPricing`), so `claude-opus-5` cannot fall back to the
|
|
113
|
+
`claude-opus-4` entry — it resolved to null, and an unpriced model contributes
|
|
114
|
+
nothing rather than erroring. On one real index that silently zeroed **526 sessions**,
|
|
115
|
+
478 of them the current default model, understating `agents cost`, `agents output`
|
|
116
|
+
and `agents insights` alike. Rates from the published table: Opus 5 $5/$25 per MTok
|
|
117
|
+
(cache write $6.25, cache read $0.50); Sonnet 5 $2/$10 (cache write $2.50, cache read
|
|
118
|
+
$0.20). Source: `apps/cli/src/lib/pricing/prices.json`.
|
|
119
|
+
|
|
120
|
+
- **Schema v34 reprices the sessions that were zeroed.** Adding prices alone fixes
|
|
121
|
+
nothing already indexed: `cost_usd` is computed at scan time and the scanner skips any
|
|
122
|
+
transcript whose `(file_mtime_ms, file_size)` is unchanged, so those rows would keep
|
|
123
|
+
their NULL forever. They cannot be repaired in place either — the row stores
|
|
124
|
+
`token_count` and `output_tokens` but not the uncached-input / cache-read / cache-write
|
|
125
|
+
split the price table needs. So v34 flushes `scan_ledger`, the same remedy v5 → v6 used
|
|
126
|
+
for this exact column when cost was introduced. One slower scan, then correct numbers.
|
|
127
|
+
Source: `apps/cli/src/lib/session/db.ts`.
|
|
128
|
+
|
|
129
|
+
**Sonnet 5's $2/$10 is introductory pricing that ends 2026-08-31.** From 2026-09-01 the
|
|
130
|
+
standard rate is $3/$15. The table holds one current rate per model with no notion of an
|
|
131
|
+
effective date, so that entry must be updated then or Sonnet 5 spend reads ~33% low.
|
|
132
|
+
Called out at the top of `table.ts`.
|
|
133
|
+
|
|
134
|
+
- **`agents sessions --orphan`/`--active`: cross-harness live discovery + richer rows (RUSH-2205).**
|
|
135
|
+
Two fixes. (1) The headless `ps`-scan recognized only 6 agent executables, so a
|
|
136
|
+
bare-headless `grok`/`kimi`/`antigravity`/`openclaw`/`hermes`/`rush` run was
|
|
137
|
+
silently dropped from the live views; the comm→kind map is now derived from
|
|
138
|
+
`SESSION_AGENTS` × the AGENTS registry (`cliCommand`), so every discoverable
|
|
139
|
+
harness surfaces. (2) Each live row now shows the agent **version** and a human
|
|
140
|
+
**created · idle** time cell, and backfills the **ticket/PR/label** from the
|
|
141
|
+
indexed session history onto orphan rows that lack them, with the label/topic on
|
|
142
|
+
its own line — grouped-by-directory layout unchanged, rows stay width-safe.
|
|
143
|
+
Source: `apps/cli/src/lib/session/active.ts`, `apps/cli/src/commands/sessions.ts`.
|
|
144
|
+
|
|
145
|
+
- **`agents doctor --check` verdict lines now carry a `check:` prefix and a total
|
|
146
|
+
version count**, so CI logs and log-scrapers get one consistent, grep-alike shape
|
|
147
|
+
whether the result is clean or drifted: `check: ok — 3 version(s) in sync` /
|
|
148
|
+
`check: drift — 2 stale, 1 never-synced across 3 version(s)`. The per-version
|
|
149
|
+
status badge for a never-synced version now reads `never-synced` (its real
|
|
150
|
+
status) instead of the unrelated `cold` label, and all three badges
|
|
151
|
+
(`stale`/`never-synced`/`unwired`) share one fixed-width column so the rows
|
|
152
|
+
align. Source: `apps/cli/src/commands/doctor.ts` (`runCheckGate`).
|
|
153
|
+
|
|
154
|
+
- **`agents secrets export --host --remote-backend file` no longer requires
|
|
155
|
+
`AGENTS_SECRETS_PASSPHRASE`.** Since the file store became passphrase-free
|
|
156
|
+
(auto-provisioning a 0600 machine-local key under `~/.agents/.secrets-key/`), the
|
|
157
|
+
remote `import --backend file` reads headlessly under that key with no passphrase
|
|
158
|
+
and no Touch ID — but the export path still hard-failed unless a passphrase was
|
|
159
|
+
set, and forcing one made the remote bundle require that shared passphrase to
|
|
160
|
+
read, defeating the headless use it was for. The passphrase is now **optional**:
|
|
161
|
+
with none set, the remote command carries no `AGENTS_SECRETS_PASSPHRASE`
|
|
162
|
+
read/export prologue and the .env is the only stdin, so the remote keys the bundle
|
|
163
|
+
under its own machine-local key (headless reads); set `AGENTS_SECRETS_PASSPHRASE`
|
|
164
|
+
locally only to opt into a shared off-disk key, which is still forwarded over ssh
|
|
165
|
+
stdin (never argv). This unblocks hands-off Linux-driven releases that provision
|
|
166
|
+
`apple.com` on a headless macOS sign host. Windows targets are still refused
|
|
167
|
+
cleanly. Source: `apps/cli/src/lib/secrets/remote.ts`,
|
|
168
|
+
`apps/cli/src/commands/secrets.ts`.
|
|
169
|
+
|
|
170
|
+
- **Docs: the per-command secrets Touch-ID contract is now written down and
|
|
171
|
+
accurate.** The `view --reveal` (1.22.14) and `exec` (1.22.21) interactive-unlock
|
|
172
|
+
changes shipped in code + CHANGELOG only; the reference docs still implied every
|
|
173
|
+
value command behaves the same. `docs/secrets.md` and the `secrets` skill now
|
|
174
|
+
carry an explicit Touch-ID matrix (deliberate `view --reveal`/`exec` at a real
|
|
175
|
+
terminal → one sheet on a locked bundle; `get`/`export` automation primitives →
|
|
176
|
+
never prompt, fail closed to `agents secrets unlock`; anything an agent launches →
|
|
177
|
+
broker-only), `specifications.md` adds **SEC-13b** + a `Prompts?` column on the
|
|
178
|
+
materialization table + `GWT-S2b`, and the skill's stale hold-window facts are
|
|
179
|
+
corrected (7-day default, screen-lock does not drop the hold). Docs-only; no
|
|
180
|
+
behavior change. Source: `apps/cli/docs/secrets.md`,
|
|
181
|
+
`apps/cli/docs/specifications.md`, `skills/secrets/SKILL.md`.
|
|
182
|
+
|
|
183
|
+
- **Resume session identities through one live-first path across the fleet.**
|
|
184
|
+
`agents sessions resume <selector>` accepts a full UUID, unique UUID prefix,
|
|
185
|
+
durable tmux name (`ag-codex-c1f3d813`), or unique alias suffix (`c1f3d813`). It
|
|
186
|
+
resolves the owning device, rechecks whether the process and pane are alive,
|
|
187
|
+
attaches a live pane, and otherwise resumes the harness-native conversation with
|
|
188
|
+
its recorded version, cwd, and mode. Retained dead tmux panes no longer count as
|
|
189
|
+
attachable, and native resume no longer collides with an existing live wrapper.
|
|
190
|
+
Source: `apps/cli/src/commands/focus.ts`,
|
|
191
|
+
`apps/cli/src/commands/sessions-resume.ts`, `apps/cli/src/lib/exec.ts`.
|
|
192
|
+
|
|
193
|
+
- **`agents fork` is now `agents sessions fork`.** Forking is a session operation, so
|
|
194
|
+
it lives under the `sessions` group next to `resume`/`focus` — `agents sessions fork
|
|
195
|
+
<id>` branches a conversation into a new, independent copy you can continue separately,
|
|
196
|
+
leaving the original untouched. The old top-level `agents fork` keeps working as a
|
|
197
|
+
hidden alias, so nothing that scripted it breaks. For a harness without a native copy
|
|
198
|
+
(anything but Claude today), the command now fails loud and names the manual branch —
|
|
199
|
+
start a fresh agent and seed it with `/continue <id>` — instead of only saying
|
|
200
|
+
"unsupported". Source: `apps/cli/src/commands/fork.ts`,
|
|
201
|
+
`apps/cli/src/commands/sessions.ts`.
|
|
202
|
+
|
|
203
|
+
- **Muse Code global binary is visible to `agents view` / `isVersionInstalled`.**
|
|
204
|
+
Muse installs a single self-updating launcher at `~/.local/bin/muse` (same
|
|
205
|
+
shape as droid), but `getBinaryPath` still resolved a version-home
|
|
206
|
+
`node_modules/.bin/muse` that never exists. After `agents import muse` /
|
|
207
|
+
`agents add muse`, version dirs now resolve to that global path so managed
|
|
208
|
+
view, collapse, and live-version bookkeeping match what actually executes;
|
|
209
|
+
install no longer writes a self-referential shim symlink. Source:
|
|
210
|
+
`apps/cli/src/lib/versions.ts`.
|
|
211
|
+
|
|
212
|
+
## 1.22.22
|
|
213
|
+
|
|
214
|
+
- **New: `agents insights` — how you work, split by the Claude account that did the
|
|
215
|
+
work.** Tool and language mix, friction (interruptions, tool-error classes, your own
|
|
216
|
+
reply latency), what you changed (line deltas, files, commits), an hour-of-day
|
|
217
|
+
rhythm, and how often two accounts ran at once. Modelled on Claude Code's
|
|
218
|
+
`/insights`, with the difference that motivated it: that command reads one account's
|
|
219
|
+
directory, while `balanced` rotation sprays sessions across every signed-in account,
|
|
220
|
+
so it describes a fraction of the work and credits all of it to one org. Source:
|
|
221
|
+
`apps/cli/src/commands/insights.ts`, `apps/cli/src/lib/session/insights.ts`.
|
|
222
|
+
|
|
223
|
+
Deterministic and offline by default. `--narrative` is opt-in and adds a written read
|
|
224
|
+
by piping the *aggregate* — never raw transcripts, unlike `/insights` — through a
|
|
225
|
+
headless `claude -p`. Facets are cached per session in a new `session_insights` table
|
|
226
|
+
keyed on `(file_mtime_ms, file_size)`, so the first run parses every transcript once
|
|
227
|
+
and later runs re-read only what changed.
|
|
228
|
+
|
|
229
|
+
- **The Claude parser can surface interruption markers on request.** `[Request
|
|
230
|
+
interrupted` text was dropped outright, so the signal was unrecoverable downstream.
|
|
231
|
+
`parseSession(..., { includeInterrupts: true })` now emits a dedicated `interrupt`
|
|
232
|
+
event. It stays OFF by default because the event array is a versioned consumer
|
|
233
|
+
contract: `agents sessions <id> --json` serializes it verbatim, `computeSummaryStats`
|
|
234
|
+
folds every timestamp into the session duration, and the live-state reader inspects a
|
|
235
|
+
fixed window of trailing events. `agents insights` is the only caller that opts in.
|
|
236
|
+
Source: `apps/cli/src/lib/session/parse.ts`.
|
|
237
|
+
|
|
238
|
+
- **`digest.ts` now classifies droid's `Create` as a file write.** Its tool-vocabulary
|
|
239
|
+
set claimed cross-harness coverage but omitted it, so droid file creations classified
|
|
240
|
+
as nothing. Source: `apps/cli/src/lib/session/digest.ts`.
|
|
241
|
+
|
|
242
|
+
- **Codex usage bars no longer show a previous account's numbers after you switch accounts.** `agents view` derives a Codex version's usage from that home's session transcripts, which carry no account identity and are not removed on logout — so after logging a version out and into a different ChatGPT account, the bar kept showing the old account's last-seen percentage (e.g. "S: 99%") until the new account ran a session. Usage is now scoped to the current login: only sessions written at/after the id_token's `auth_time` (the OIDC authentication time) count, and a signed-out home reports no usage. `auth_time` is used rather than the auth.json file mtime because Codex rewrites auth.json on every token refresh, but a refresh does not re-authenticate, so `auth_time` stays at the real login — an actively-refreshing account keeps its bar, only a real re-login or account switch moves the floor. Source: `apps/cli/src/lib/usage.ts`.
|
|
243
|
+
|
|
244
|
+
- **The interactive session picker's detailed preview no longer collapses to empty.**
|
|
245
|
+
`agents sessions`, `agents sessions <query>`, and `agents sessions --active` open the
|
|
246
|
+
picker with the rich preview pane (prompt, files, hooks, errors, tests, last response)
|
|
247
|
+
on by default, but the preview had no guaranteed height: a 15-row list
|
|
248
|
+
(`PICKER_RECENT_COUNT`) on a short terminal consumed the whole viewport, the computed
|
|
249
|
+
`availablePreviewRows` went to zero, and the pane silently vanished — worse when
|
|
250
|
+
fleet-unreachable warnings and the hidden-session footer had scrolled lines above the
|
|
251
|
+
prompt. The picker now caps the visible list page so the preview keeps a floor of
|
|
252
|
+
`PREVIEW_MIN_ROWS` (6) rows, and accounts for the lines printed above the prompt so
|
|
253
|
+
those notices and the preview stay on screen together. Applied consistently across
|
|
254
|
+
`itemPicker`, `dynamicPicker`, and `multiItemPicker`, so the bare browser, the query
|
|
255
|
+
picker, and the `--active` browser all behave the same; the space/tab preview toggle
|
|
256
|
+
is unchanged. Source: `apps/cli/src/lib/picker.ts`,
|
|
257
|
+
`apps/cli/src/commands/sessions-picker.ts`, `apps/cli/src/commands/sessions.ts`.
|
|
258
|
+
|
|
3
259
|
## 1.22.21
|
|
4
260
|
|
|
5
261
|
- **`agents secrets exec <bundle> -- <cmd>` now resolves a locked keychain bundle
|
package/README.md
CHANGED
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
|
|
33
33
|
<a href="https://factory.ai" title="Factory AI Droid"><strong>Droid</strong></a>
|
|
34
34
|
|
|
35
|
+
<a href="https://dev.meta.ai/docs/muse-code" title="Meta Muse Code"><strong>Muse</strong></a>
|
|
36
|
+
|
|
35
37
|
<a href="https://omp.sh" title="Oh My Pi"><strong>Pi</strong></a>
|
|
36
38
|
</p>
|
|
37
39
|
|
|
@@ -218,7 +220,11 @@ otherwise the first permission option offered by the server. The same last-resor
|
|
|
218
220
|
warning applies.
|
|
219
221
|
|
|
220
222
|
Codex has no native smart-classifier mode, so `agents run codex --mode auto` resolves
|
|
221
|
-
to sandboxed `edit` and can still prompt. `
|
|
223
|
+
to sandboxed `edit` and can still prompt. When `--mode` is omitted for Codex, the same
|
|
224
|
+
safe writable mode is used: the workspace, `~/.agents`, and regenerable toolchain caches
|
|
225
|
+
are writable; network access is enabled; and approvals are requested on demand. An
|
|
226
|
+
explicit `--mode plan` keeps the filesystem read-only while leaving network access on.
|
|
227
|
+
`agents run codex --mode skip` is different:
|
|
222
228
|
it bypasses approvals **and** removes the sandbox. `full` remains an alias for `skip`,
|
|
223
229
|
but new scripts should use the explicit `skip` name.
|
|
224
230
|
|
|
@@ -684,7 +690,7 @@ tools:
|
|
|
684
690
|
---
|
|
685
691
|
```
|
|
686
692
|
|
|
687
|
-
Workflows that need to write — post PR comments, edit files, send Slack — should run with `--mode edit`, or `--mode auto` on Claude Code and GitHub Copilot. Reserve `--mode skip` (legacy alias: `full`) for last-resort bypasses. `agents run` defaults to `--mode plan`
|
|
693
|
+
Workflows that need to write — post PR comments, edit files, send Slack — should run with `--mode edit`, or `--mode auto` on Claude Code and GitHub Copilot. Reserve `--mode skip` (legacy alias: `full`) for last-resort bypasses. `agents run` defaults to `--mode plan` for other harnesses; Codex defaults to its safe writable profile. An explicit Codex `--mode plan` is read-only with network access.
|
|
688
694
|
|
|
689
695
|
Resolution is project > user > system: a `<repo>/.agents/workflows/<name>/` overrides a same-named workflow in `~/.agents/workflows/`. Commit project workflows with your repo so teammates get the same pipeline.
|
|
690
696
|
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1022,10 +1022,13 @@ function runCheckGate(opts, cwd) {
|
|
|
1022
1022
|
const orphanNote = drift.orphanVersionCount > 0
|
|
1023
1023
|
? chalk.gray(` (${drift.orphanVersionCount} version(s) carry orphans — run \`agents prune cleanup\`)`)
|
|
1024
1024
|
: '';
|
|
1025
|
-
console.log(`${chalk.green('ok')}
|
|
1025
|
+
console.log(`${chalk.gray('check:')} ${chalk.green('ok')} — ${drift.syncRows.length} version(s) in sync${orphanNote}`);
|
|
1026
1026
|
process.exit(0);
|
|
1027
1027
|
}
|
|
1028
|
-
// Drift: one-line verdict always, per-version detail unless --quiet.
|
|
1028
|
+
// Drift: one-line verdict always, per-version detail unless --quiet. The
|
|
1029
|
+
// `check:` prefix and total count make every verdict line grep/parse-alike
|
|
1030
|
+
// whether it's clean or drifted, instead of the bare `drift <parts>` shape
|
|
1031
|
+
// that gave CI logs no anchor to search on.
|
|
1029
1032
|
const parts = [];
|
|
1030
1033
|
if (drift.staleCount > 0)
|
|
1031
1034
|
parts.push(`${drift.staleCount} stale`);
|
|
@@ -1035,16 +1038,21 @@ function runCheckGate(opts, cwd) {
|
|
|
1035
1038
|
parts.push(`${drift.unwiredHookVersions} with unwired hooks`);
|
|
1036
1039
|
if (drift.sourceBehind.length > 0)
|
|
1037
1040
|
parts.push(`${drift.sourceBehind.length} source layer(s) behind origin`);
|
|
1038
|
-
console.error(`${chalk.red('drift')}
|
|
1041
|
+
console.error(`${chalk.gray('check:')} ${chalk.red('drift')} — ${parts.join(', ')} across ${drift.syncRows.length} version(s)`);
|
|
1039
1042
|
if (!opts.quiet) {
|
|
1043
|
+
// Fixed-width, left-aligned so rows form a real column; the badge text is
|
|
1044
|
+
// the actual status name ('never-synced'), not the unrelated 'cold' label
|
|
1045
|
+
// that used to hide it. Width is 'never-synced'.length + a 2-space gap so
|
|
1046
|
+
// the longest badge still separates cleanly from the label that follows.
|
|
1047
|
+
const STATUS_BADGE_WIDTH = 'never-synced'.length + 2;
|
|
1040
1048
|
for (const row of drift.syncRows) {
|
|
1041
1049
|
const unwired = (row.unwiredHooks ?? 0) > 0;
|
|
1042
1050
|
if (row.status === 'fresh' && !unwired)
|
|
1043
1051
|
continue;
|
|
1044
|
-
const tag = row.status === 'stale' ? chalk.yellow('stale')
|
|
1045
|
-
: row.status === 'never-synced' ? chalk.gray('
|
|
1046
|
-
: chalk.red('unwired'); // fresh but hooks not wired into settings.json
|
|
1047
|
-
console.error(` ${tag}
|
|
1052
|
+
const tag = row.status === 'stale' ? chalk.yellow('stale'.padEnd(STATUS_BADGE_WIDTH))
|
|
1053
|
+
: row.status === 'never-synced' ? chalk.gray('never-synced'.padEnd(STATUS_BADGE_WIDTH))
|
|
1054
|
+
: chalk.red('unwired'.padEnd(STATUS_BADGE_WIDTH)); // fresh but hooks not wired into settings.json
|
|
1055
|
+
console.error(` ${tag}${checkLabel(row)}`);
|
|
1048
1056
|
for (const line of row.divergence ?? []) {
|
|
1049
1057
|
console.error(chalk.gray(` ${line}`));
|
|
1050
1058
|
}
|
package/dist/commands/exec.js
CHANGED
|
@@ -516,7 +516,7 @@ export function registerRunCommand(program) {
|
|
|
516
516
|
const runCmd = program
|
|
517
517
|
.command('run <agent> [prompt]')
|
|
518
518
|
.description('Execute an agent. Pass a prompt for headless runs; omit it to launch the agent interactively.')
|
|
519
|
-
.option('-m, --mode <mode>', 'How much the agent can do: plan (read-only), edit (can write files), auto (smart classifier auto-approves safe ops, prompts for risky), skip (bypass all permission prompts). \'full\' accepted as alias for skip.', 'plan')
|
|
519
|
+
.option('-m, --mode <mode>', 'How much the agent can do: plan (read-only), edit (can write files), auto (smart classifier auto-approves safe ops, prompts for risky), skip (bypass all permission prompts). Omitted Codex mode defaults to safe writable edit; other harnesses default to plan. \'full\' accepted as alias for skip.', 'plan')
|
|
520
520
|
.option('-e, --effort <effort>', 'Reasoning effort: low | medium | high | xhigh | max | auto (claude and codex only)', 'auto')
|
|
521
521
|
.option('--model <model>', 'Cost tier (cheap|default|best|ultra) or a concrete model id; tiers resolve per harness+version to a supported model')
|
|
522
522
|
.option('--env <key=value>', 'Pass environment variable to the agent (repeatable, e.g., --env DEBUG=1 --env API_KEY=xyz)', (val, prev) => [...prev, val], [])
|
|
@@ -882,8 +882,10 @@ export function registerRunCommand(program) {
|
|
|
882
882
|
// the normal new-run default as a real value, so consult its provenance
|
|
883
883
|
// rather than mistaking the default for an explicit override.
|
|
884
884
|
if (command.getOptionValueSource('mode') === 'default') {
|
|
885
|
-
if (resolvedResumeSource.mode)
|
|
885
|
+
if (resolvedResumeSource.mode) {
|
|
886
886
|
options.mode = resolvedResumeSource.mode;
|
|
887
|
+
command.setOptionValueWithSource('mode', resolvedResumeSource.mode, 'implied');
|
|
888
|
+
}
|
|
887
889
|
else if (!options.quiet)
|
|
888
890
|
process.stderr.write(chalk.yellow(`[agents] session ${resolvedResumeSource.shortId} predates stored launch modes; using --mode ${options.mode}\n`));
|
|
889
891
|
}
|
|
@@ -1540,13 +1542,15 @@ export function registerRunCommand(program) {
|
|
|
1540
1542
|
});
|
|
1541
1543
|
}
|
|
1542
1544
|
const isRaw = options.raw || options.tmux === false || options.disableTmux === true;
|
|
1545
|
+
const { modeForRemoteDispatch } = await import('../lib/codex-policy.js');
|
|
1546
|
+
const forwardedMode = modeForRemoteDispatch(options.mode, command.getOptionValueSource('mode'));
|
|
1543
1547
|
const exitCode = await runInteractiveOnHost(host, {
|
|
1544
1548
|
agent: runAgent,
|
|
1545
1549
|
version: resumeId ? undefined : runVersion,
|
|
1546
1550
|
strategy: resumeId ? undefined : runStrategy,
|
|
1547
1551
|
fallback: options.fallback,
|
|
1548
1552
|
prompt,
|
|
1549
|
-
mode:
|
|
1553
|
+
mode: forwardedMode,
|
|
1550
1554
|
model: options.model,
|
|
1551
1555
|
effort: options.effort,
|
|
1552
1556
|
env: hostEnv,
|
|
@@ -1625,13 +1629,15 @@ export function registerRunCommand(program) {
|
|
|
1625
1629
|
}
|
|
1626
1630
|
// Session-id mint, detached dispatch, and local session-index
|
|
1627
1631
|
// registration all live in the shared helper (lib/hosts/run-target.ts).
|
|
1632
|
+
const { modeForRemoteDispatch } = await import('../lib/codex-policy.js');
|
|
1633
|
+
const forwardedMode = modeForRemoteDispatch(options.mode, command.getOptionValueSource('mode'));
|
|
1628
1634
|
const { task, exitCode } = await dispatchPromptToHost(host, {
|
|
1629
1635
|
agent: runAgent,
|
|
1630
1636
|
version: resumeId ? undefined : runVersion,
|
|
1631
1637
|
strategy: resumeId ? undefined : runStrategy,
|
|
1632
1638
|
fallback: options.fallback,
|
|
1633
1639
|
prompt,
|
|
1634
|
-
mode:
|
|
1640
|
+
mode: forwardedMode,
|
|
1635
1641
|
model: options.model,
|
|
1636
1642
|
effort: options.effort,
|
|
1637
1643
|
env: options.env,
|
|
@@ -1770,7 +1776,7 @@ export function registerRunCommand(program) {
|
|
|
1770
1776
|
await warnUnpushedWork(resumeExec.cwd ?? process.cwd());
|
|
1771
1777
|
process.exit(resumeExit);
|
|
1772
1778
|
}
|
|
1773
|
-
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, headlessPlanStallCommand, nativeResume, resolveInteractive, inferredInteractiveWithoutTty }, { ALL_AGENT_IDS, ACCOUNT_INSPECTION_AGENT_IDS, agentLabel, supportsAccountInspection }, { profileExists, resolveProfileForRun }, { readAndResolveBundleEnv, describeBundle, assertRemoteBundleFlagsUnsupported }, { splitBundleRef, resolveHostSshTarget, remoteResolveEnv }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, rotationFailoverChain, shouldArmRotationFailover, RUN_STRATEGIES, collectHarnessCandidates, pickHarnessWeighted, classifyHarnessCandidates, formatHarnessPickBanner, formatNoHealthyHarnessError, formatNoHealthyAccountError }, { getGlobalDefault, getVersionHomePath, resolveVersion, resolveVersionAlias, ensureAgentRunnable }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef, resolveAllowedSubagents, pruneStaleWorkflowSubagents, ensureSubagentDispatchTool }, { resolveRunDefaults }, { getMcpServersByName, buildWorkflowMcpConfig }, { supports, capableAgents }, { shareRuntimeEnv },] = await Promise.all([
|
|
1779
|
+
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, implicitModeFor, headlessPlanStallCommand, nativeResume, resolveInteractive, inferredInteractiveWithoutTty }, { ALL_AGENT_IDS, ACCOUNT_INSPECTION_AGENT_IDS, agentLabel, supportsAccountInspection }, { profileExists, resolveProfileForRun }, { readAndResolveBundleEnv, describeBundle, assertRemoteBundleFlagsUnsupported }, { splitBundleRef, resolveHostSshTarget, remoteResolveEnv }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, rotationFailoverChain, shouldArmRotationFailover, RUN_STRATEGIES, collectHarnessCandidates, pickHarnessWeighted, classifyHarnessCandidates, formatHarnessPickBanner, formatNoHealthyHarnessError, formatNoHealthyAccountError }, { getGlobalDefault, getVersionHomePath, resolveVersion, resolveVersionAlias, ensureAgentRunnable }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef, resolveAllowedSubagents, pruneStaleWorkflowSubagents, ensureSubagentDispatchTool }, { resolveRunDefaults }, { getMcpServersByName, buildWorkflowMcpConfig }, { supports, capableAgents }, { shareRuntimeEnv },] = await Promise.all([
|
|
1774
1780
|
import('../lib/exec.js'),
|
|
1775
1781
|
import('../lib/agents.js'),
|
|
1776
1782
|
import('../lib/profiles.js'),
|
|
@@ -2407,7 +2413,14 @@ export function registerRunCommand(program) {
|
|
|
2407
2413
|
// `skip` still hard-fails when unsupported — pretending we bypassed
|
|
2408
2414
|
// permissions would be unsafe.
|
|
2409
2415
|
const modeIsDefault = modeSource === 'default';
|
|
2410
|
-
|
|
2416
|
+
let requestedMode = normalizeMode(mode);
|
|
2417
|
+
// Codex's intrinsic omitted-mode default is safe writable: workspace plus
|
|
2418
|
+
// common caches, network enabled, approvals on request. An explicit
|
|
2419
|
+
// --mode plan and a configured run default remain read-only.
|
|
2420
|
+
const { modeWasImplicit } = await import('../lib/codex-policy.js');
|
|
2421
|
+
const wasModeImplicit = modeWasImplicit(modeSource, modeFromRunDefault);
|
|
2422
|
+
if (wasModeImplicit)
|
|
2423
|
+
requestedMode = implicitModeFor(agent);
|
|
2411
2424
|
let resolvedMode;
|
|
2412
2425
|
try {
|
|
2413
2426
|
resolvedMode = resolveMode(agent, requestedMode);
|
|
@@ -2531,6 +2544,7 @@ export function registerRunCommand(program) {
|
|
|
2531
2544
|
prompt,
|
|
2532
2545
|
interactive: options.interactive || forceInteractive,
|
|
2533
2546
|
mode: requestedMode,
|
|
2547
|
+
modeWasImplicit: wasModeImplicit,
|
|
2534
2548
|
effort,
|
|
2535
2549
|
cwd: options.cwd,
|
|
2536
2550
|
model,
|
package/dist/commands/focus.d.ts
CHANGED
|
@@ -16,6 +16,29 @@ import type { Command } from 'commander';
|
|
|
16
16
|
import { type UnreachableFallback } from './go.js';
|
|
17
17
|
import type { ActiveSession } from '../lib/session/active.js';
|
|
18
18
|
import type { SessionMeta } from '../lib/session/types.js';
|
|
19
|
+
import { type LiveStatusFilter } from './sessions.js';
|
|
20
|
+
import { openSurfaces, type Backend } from '../lib/terminal/index.js';
|
|
21
|
+
/** Options for `sessions focus` — device/host scope + the `--active` live-state filters. */
|
|
22
|
+
export interface FocusOptions {
|
|
23
|
+
local?: boolean;
|
|
24
|
+
attachOnly?: boolean;
|
|
25
|
+
host?: string[];
|
|
26
|
+
device?: string[];
|
|
27
|
+
working?: boolean;
|
|
28
|
+
idle?: boolean;
|
|
29
|
+
waiting?: boolean;
|
|
30
|
+
orphan?: boolean;
|
|
31
|
+
orphaned?: boolean;
|
|
32
|
+
crashed?: boolean;
|
|
33
|
+
closed?: boolean;
|
|
34
|
+
abandoned?: boolean;
|
|
35
|
+
queued?: boolean;
|
|
36
|
+
unknown?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** `--device` is an alias of `--host`; both are repeatable. Merge into one host list. */
|
|
39
|
+
export declare function mergeFocusHosts(opts: FocusOptions): string[];
|
|
40
|
+
/** Picker header that reflects the active filter + device, e.g. "Focus orphaned sessions on yosemite-s0:". */
|
|
41
|
+
export declare function focusHeader(statuses: LiveStatusFilter[], hosts: string[]): string;
|
|
19
42
|
export declare function registerFocusCommand(program: Command): void;
|
|
20
43
|
/**
|
|
21
44
|
* Which fallback fires when a session has no attach rail. `--attach-only` (the old
|
|
@@ -23,9 +46,58 @@ export declare function registerFocusCommand(program: Command): void;
|
|
|
23
46
|
* without touching `jumpTo`'s side effects.
|
|
24
47
|
*/
|
|
25
48
|
export declare function selectFallback(attachOnly: boolean | undefined): UnreachableFallback;
|
|
26
|
-
export declare function focusAction(id: string | undefined, opts:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
49
|
+
export declare function focusAction(id: string | undefined, opts: FocusOptions): Promise<void>;
|
|
50
|
+
/** A retained pane is not attachable merely because tmux can still display it. */
|
|
51
|
+
export declare function isAttachableLiveSession(session: ActiveSession): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* How a single selected live session opens in its new tab.
|
|
54
|
+
* - `attach` — a live tmux pane joined in the tab (a second client, no fork),
|
|
55
|
+
* local (`sh -c`) or remote (`ssh -tt`). tmux is the only rail that can be
|
|
56
|
+
* *joined* without forking (see the file header).
|
|
57
|
+
* - `resume` — no attach rail, so resume a copy in the tab (the original keeps
|
|
58
|
+
* running); never a silent drop.
|
|
59
|
+
* - `skip` — not resumable AND no rail: reported, not dropped.
|
|
60
|
+
*/
|
|
61
|
+
export type FocusSurfacePlan = {
|
|
62
|
+
kind: 'attach';
|
|
63
|
+
command: string[];
|
|
64
|
+
note: string;
|
|
65
|
+
} | {
|
|
66
|
+
kind: 'resume';
|
|
67
|
+
command: string[];
|
|
68
|
+
note: string;
|
|
69
|
+
} | {
|
|
70
|
+
kind: 'skip';
|
|
71
|
+
note: string;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Shell that resolves a tmux pane to its session and attaches it — the exact form
|
|
75
|
+
* `jumpTo` uses, minus the pre-select-window nicety, so a batch tab joins the live
|
|
76
|
+
* session (a second client) without forking. Reused for local and (wrapped in ssh)
|
|
77
|
+
* remote panes.
|
|
78
|
+
*/
|
|
79
|
+
export declare function tmuxAttachScript(mux: {
|
|
80
|
+
socket?: string;
|
|
81
|
+
pane: string;
|
|
82
|
+
}): string;
|
|
83
|
+
/**
|
|
84
|
+
* Decide how one live session opens as a tab. Pure over the session + a
|
|
85
|
+
* `resumeCommandFor` resolver (injected so the local version-pinned resume command
|
|
86
|
+
* and the tests stay decoupled from `discoverSessions`).
|
|
87
|
+
*/
|
|
88
|
+
export declare function planFocusSurface(s: ActiveSession, self: string, resumeCommandFor: (s: ActiveSession) => string[] | null): FocusSurfacePlan;
|
|
89
|
+
/** The engine seam — real `openSurfaces`, overridable in tests to assert the tab requests. */
|
|
90
|
+
export type OpenSurfacesFn = typeof openSurfaces;
|
|
91
|
+
/** Test seams for `openFocusTabs`: inject the engine boundary + force a backend. */
|
|
92
|
+
export interface OpenFocusTabsDeps {
|
|
93
|
+
open?: OpenSurfacesFn;
|
|
94
|
+
/** Skip `resolveBackend` (which needs a live terminal) when set — tests pass 'tmux'. */
|
|
95
|
+
backend?: Backend | 'inplace';
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Open each selected live session as a tab: attach its live pane where one exists,
|
|
99
|
+
* else resume a copy. Reuses `resume`'s backend resolution + flood guard.
|
|
100
|
+
*/
|
|
101
|
+
export declare function openFocusTabs(targets: ActiveSession[], self: string, deps?: OpenFocusTabsDeps): Promise<void>;
|
|
30
102
|
/** Minimal SessionMeta for a live session, enough for `buildResumeCommand` + placement. */
|
|
31
103
|
export declare function metaFromActive(s: ActiveSession): SessionMeta;
|