Compare commits

...
Author SHA1 Message Date
dev60-review 8a7f003249 docs(release): name 0.7.15's draft-aware limit on a busy Codex seat
On Codex, a send to a busy seat in the opt-in draft-aware typing mode can
be pasted without Enter, neither delivered nor kept as held, with its text
left in the input. It was seen on 0.7.12 and 0.7.14 and isn't fixed in this
release. Add it to the known limits beside the wide-footer case, in the
changelog, what's new and the capability map.
2026-11-13 21:36:42 +00:00
dev60-review 44eb0fc8aa docs(capability-map): scope two 0.7.15 lines as the notes do
The slow-operation sentence names the nodes records it means, and a Slack
notification posted before the arrival note is recorded can lead to a
top-level answer, rather than always doing so.
2026-11-13 21:07:32 +00:00
dev60-review 3b75325407 docs(release): state 0.7.15's changes as their sources do
Nine notes sentences said more than the changes' own descriptions and source:
snapshot refreshes share process listings only where each seat's order and
freshness allow; the draft-aware Enter result holds for the tested schedule,
not every interleaving; a held paste is retained, not promised delivery;
restore tries a saved conversation for an eligible seat and a failed start
keeps its status; the execution view's read keeps a synchronous fallback;
a runner's predecessor loses its grant when the replacement activates, and
older grants stay tied to the occupant until then; the daemon start wait is a
readiness budget that synchronous work can run past; the slow-operation log
names three selected nodes steps; and a Slack answer can still post
top-level when the arrival note is written late or not at all.
2026-11-13 21:00:55 +00:00
dev60-review b7485e0968 docs(capability-map): teach what 0.7.15 adds
Moves the map's marker to capability-delta-v0.7.15 and teaches, in the
sections an agent reads them from: Slack preview mode and replies into their
arrival thread, seat restart's way back to rig launch, the longer daemon start
wait and the slow-operation log's sites, restore of a seat with a resume token
and no startup context and what the overall result still doesn't name, the
draft-aware re-check and the wide-footer hold, the openrig plugin's Claude Code
requirement, the renewal of OpenRig's own Codex hook approvals, the TUI's
retained check-ins and rig_team's rigId hint, and work-install's missing
mission warning.
2026-11-13 20:44:04 +00:00
dev60-review f7cc92853c docs(release): describe 0.7.15
Add the [0.7.15] changelog section and its what's new, from each change's
merged description. The install commands on the README, getting started and
the quickstart name v0.7.15: the rigs skill and the install script.

The notes disclose that Slack previews on posts to a person are off by
default, when OpenRig renews its own Codex hook approvals, the longer daemon
start wait and its limit, and the restore results this release doesn't yet
name. The linked Codex config file limit carries from 0.7.14.
2026-11-13 20:44:04 +00:00
dev60-review ab0b5b2c0e docs(release): carry 0.7.14's notes and capability teaching
The 0.7.14 changelog section, what's new and the packaged capability map's
0.7.14 teaching were published from the release line only. Carry them
unchanged from v0.7.14 so 0.7.15's notes and map build on them, as 0.7.14
carried 0.7.13's.
2026-11-13 20:36:58 +00:00
dev60-review 2ad1dbd259 Revert "fix(daemon): resolve Codex hook folders without following their files (#3078) (#3079)"
This reverts commit 4270bb3c14.
2026-11-13 20:32:39 +00:00
dev60-review b88eb36768 chore(release): prepare 0.7.15
Set 0.7.15 in the root, cli, daemon, ui and acp packages and the lockfile,
the ACP launcher's @openrig/cli dependency and registry listing, and the
openrig plugin's four manifests, which carry the CLI's release version.

The packaged-plugin baseline is refreshed from the published 0.7.14
archive (openrig-cli-0.7.14.tgz, sha256 866fb734b32c18c7a938d6f1edbda44c
51fce1c87ee4c44839a8a3083d2aba25). openrig-core's packaged skills changed
since 0.7.14 (#3110, #3111), so it moves to 0.1.30 and existing installs
receive them.
2026-11-13 20:32:29 +00:00
22 changed files with 1528 additions and 278 deletions

No files matched your search

+420
View File
@@ -8,6 +8,426 @@ deprecations, and behavioral changes. Breaking changes are called out explicitly
---
## [0.7.15]
OpenRig 0.7.15 is a patch for steadier, more truthfully reported teams. A busy
daemon does less repeated synchronous work on a few read paths and names more
of what it was doing when it stalls. `rig daemon start` waits longer for a slow
start, and restore tries a saved conversation when an eligible seat has no
saved startup context. There are fixes across delivery, Slack replies, Codex seats and
connected assistants, many contributed by people using OpenRig. See Known
limits for what this release doesn't fix.
### Before you upgrade
- **Stop the daemon, back up the database, install 0.7.15, then start the
daemon.** Your agents keep running through a daemon restart; don't use
`rig down`, which ends them.
```sh
rig daemon stop
cp "$(rig config get db.path)" "$(rig config get db.path).before-0.7.15"
npm install -g @openrig/cli@0.7.15 # or your existing installation method
rig daemon start
```
- **One migration runs before the daemon listens.** Migration 165 adds four
columns and two indexes to `activity_grants`, binding a runner's activity
credentials to its actual launch. Going back to 0.7.14 after it isn't
established; keep the backup. ([#3060](https://github.com/mvschwarz/openrig/pull/3060))
- **A slow start:** `rig daemon start` now gives the daemon child it started a
180-second readiness budget, instead of 20 s, and prints
"Waiting for daemon child <PID> readiness (…s elapsed; 180s limit)" about
every 20 s. A quick start still returns at once, and a child that exits ends
the wait. Let the wait finish. The budget bounds scheduling: synchronous work
can run past the budget. A child still not ready when that budget is
exhausted is reported pending as before: "Daemon startup is pending after the readiness deadline".
Readiness remains unconfirmed; the child keeps running and the startup
reservation is retained. Inspect the address and log it prints, and don't
start it again until its restart steps are done: stop the named child PID and
wait for it to exit, inspect `daemon-start.lock`, and only when both recorded
launcher and child PIDs are absent, archive the lock and run
`rig daemon start`. `rig daemon stop` cannot stop that unpublished child. ([#3118](https://github.com/mvschwarz/openrig/pull/3118))
- **The plugins:** the `openrig` plugin carries the CLI's version, 0.7.15.
openrig-core moves to 0.1.30, because its skills changed.
### Under load
- The wake ladder reads its prompt-refusal candidates from the open rows,
instead of scanning the whole transition log on every tick. It returns the
same rows. This removes one repeated scan; it isn't a measured fix for every
stall. ([#3098](https://github.com/mvschwarz/openrig/pull/3098))
- The execution view's candidate read steps through the queue in windows and,
on its cooperative path, yields to the event loop between them, so a mission
filter that matches few rows no longer holds the loop for a whole history
walk. One large row, or the history count, can still take time. Inside a
transaction, on a memory or temporary database, or on a non-WAL file, the
candidate read keeps its synchronous fallback. ([#3122](https://github.com/mvschwarz/openrig/pull/3122))
- Snapshot refreshes share process listings where each seat's order and the
2 s freshness allow; each seat still gets its two ordered observations and
its own fallback. The reduction is derived from the source, not measured. ([#3154](https://github.com/mvschwarz/openrig/pull/3154))
- The slow-operation log adds three queue sites and three selected steps of an
ordinary nodes read: `nodes.inventory.build`,
`nodes.inventory.activity_sync` and `nodes.inventory.terminal_work`. These
nodes records are written after a slow call completes; a missing record does
not rule out unfinished work or work outside those steps. This changes no
behaviour. ([#3100](https://github.com/mvschwarz/openrig/pull/3100), [#3138](https://github.com/mvschwarz/openrig/pull/3138))
### Restore and seats
- For an eligible seat in a rig with pods that restore starts in its own pane,
a resume token with no saved startup context now lets restore try the saved
conversation with an empty plan. It records the `startup_context` gap. A
successful resume with that gap makes the rig read `partially_restored`; a
missing conversation or failed start keeps its own status. No saved startup
plan is replayed or saved startup-context row written; the adapter's ordinary
bootstrap and existing skill restoration still run. An external CLI
attachment is kept, no-token seats still wait for a decision, and legacy
seats are unchanged. `restore-check` reads an eligible seat with a token
yellow; for one with no saved context and no eligible token, it points to the
read-only `rig up <rig> --existing --plan` preview. ([#3177](https://github.com/mvschwarz/openrig/pull/3177))
- When `rig seat restart --keep-conversation` refuses a recoverable seat whose
tmux session is gone, it names the way back: `rig launch '<rig>' '<node>'`
(add `--plan` to preview). ([#3135](https://github.com/mvschwarz/openrig/pull/3135))
- `restore-check` and a full restore warn when a snapshot recorded a named
Codex profile that the current seat has cleared; restore uses the current
selection. Nothing is refused. ([#3155](https://github.com/mvschwarz/openrig/pull/3155))
- A context-usage watchdog reads its own seat's current occupant, not an
archived generation's. A context watch whose seat can't be pinned, or has no
recorded tenure, skips that tick with its reason instead of ending. ([#3027](https://github.com/mvschwarz/openrig/pull/3027), [#3139](https://github.com/mvschwarz/openrig/pull/3139))
### Delivery and people
- Right before an automatic paste into a seat with the draft-aware guard,
OpenRig re-checks: it holds the paste after recent client activity, re-reads
the input after a short wait, and holds when tmux can't be read. The message
is held and retained; `rig seat held-messages` shows it. An ordinary guarded
paste waits about 300 ms longer. This narrows [#3076](https://github.com/mvschwarz/openrig/issues/3076)'s window; it doesn't close it. ([#3097](https://github.com/mvschwarz/openrig/pull/3097))
- A terminal seat whose tmux session runs under another name gets the exact
text of a send, and a queued wake no longer types OpenRig's From/To envelope
into its shell. ([#3074](https://github.com/mvschwarz/openrig/pull/3074))
- When the arrival note is recorded before the answer is routed, an answer to
a reply in an older Slack thread posts back into that thread, not as a new
top-level message; see Known limits for when it posts top-level. Only the
conversation's newest thread answers a parked question, as before. ([#3120](https://github.com/mvschwarz/openrig/pull/3120))
- Slack previews on posts to a person are a setting: `rig slack setup --unfurl on|off`,
and `rig slack status` shows which is in effect from the connector's next
rewire. The default is off; see "Behaviour that changed". ([#3157](https://github.com/mvschwarz/openrig/pull/3157))
### Connected assistants
- An assistant that passes its team's display name as `rig_team`'s `rigId` is
told to leave `rigId` out to see its own team, and a hosted member with no
wake trigger is told it picks up work when its person asks, or on a schedule
they set. ([#3104](https://github.com/mvschwarz/openrig/pull/3104))
- An external grant that works its own inbox is told only about the tools it's
offered. ([#3140](https://github.com/mvschwarz/openrig/pull/3140))
- The TUI keeps connected assistants' check-in lines when a rig's seat
inventory can't be read, with the error beside them. ([#3105](https://github.com/mvschwarz/openrig/pull/3105))
- The `openrig` plugin needs Claude Code 2.1.119 or later. On earlier versions,
with the plugin's OpenRig seat setting left blank, Claude Code drops its MCP
server: the skill loads, but the agent has no OpenRig tools. ([#3137](https://github.com/mvschwarz/openrig/pull/3137))
### Codex, runners and harnesses
- **OpenRig renews its own changed Codex hook approvals.** When a new
openrig-core changes one of OpenRig's six Codex hook declarations, the launch
that installs it renews those approvals from OpenRig's own record instead of
stopping at Codex's hook review. This runs at daemon start in the daemon's
Codex home, when hooks are on and a Codex seat selected openrig-core, and at
each such seat's launch in its own Codex home. It renews only when the
installed plugin matches the shipped one and every current approval matches
OpenRig's saved record. A person's own choice in any layer OpenRig reads
keeps the review; profile files passed with `-p` aren't read. No real Codex
seat ran in its tests. ([#3143](https://github.com/mvschwarz/openrig/pull/3143))
- A Codex seat's resume command in the node inventory and detail panel is
built from what a real resume uses, with a label when the recorded launch
differs. Workspace options that aren't recorded per launch can still read as
a match. ([#3043](https://github.com/mvschwarz/openrig/pull/3043))
- Runner activity grants are bound to the actual launch, and the
replacement's activation revokes its predecessor's grant. A grant made before
this release stays tied to the seat's occupant until then. A grant that can't
be bound never blocks a launch or restore. This attributes reports to
the run OpenRig selected; it doesn't prove a runner's identity. ([#3060](https://github.com/mvschwarz/openrig/pull/3060))
- Terminal views find tmux again when its cached path is gone, instead of
tiles exiting 126 until a daemon restart. ([#3129](https://github.com/mvschwarz/openrig/pull/3129))
- A `SKILL.md` with CRLF line endings is discovered and reaches a seat's
managed plugin loadout. ([#3153](https://github.com/mvschwarz/openrig/pull/3153))
- `rig context work-install --mission <missing>` warns that the mission
doesn't exist, and `missionRoot` reads null; the exit code is unchanged. ([#3141](https://github.com/mvschwarz/openrig/pull/3141))
- OpenRig's own transitions (`daemon@kernel` and `system:*`) no longer count
toward ceremony amplification, and the post-restore audit message matches
the restore skill. ([#3124](https://github.com/mvschwarz/openrig/pull/3124), [#3127](https://github.com/mvschwarz/openrig/pull/3127))
### Behaviour that changed
- **Slack previews:** posts to a person no longer show link and media previews
by default. An existing install stops showing them after its next connector
rewire or daemon restart. Run `rig slack setup --unfurl on` to turn them back
on; earlier previews aren't removed. ([#3157](https://github.com/mvschwarz/openrig/pull/3157))
- **Restore of an eligible seat with a resume token and no startup context**
now tries its saved conversation, and a successful resume reads
`partially_restored`. It used to stop with "Harness not started: no startup
context" and read the rig not restorable. ([#3177](https://github.com/mvschwarz/openrig/pull/3177))
### Known limits
- **Daemon start:** the longer readiness budget doesn't identify or cure the
cause of a slow start, synchronous work can run past the budget, and a child
still not ready when that budget is exhausted stays pending. ([#3118](https://github.com/mvschwarz/openrig/pull/3118))
- **Daemon memory:** 0.7.14's comparison under load established neither equal
memory nor a regression against 0.7.13. This release makes no memory claim.
- **Restore's overall result:** a rig can still read `fully_restored` while a
seat restore didn't start (one claimed or added after its roster or snapshot
was recorded), or a seat kept in its external CLI attachment, goes unnamed.
A seat restore left as a bare shell after "Harness not started" still counts
as running in `rig ps`.
- **Draft-aware delivery ([#3076](https://github.com/mvschwarz/openrig/issues/3076)):** typing after the guard's final check and before
the paste can still leave the notice beside the person's draft. In the tested
append-mode schedule Enter was refused and nothing was submitted or deleted;
this is not a guarantee for every interleaving. The prior conditional-send
Enter-window case is unchanged: characters typed during OpenRig's last checks
before Enter submitted with OpenRig's text.
- **Draft-aware delivery with a wide Claude Code footer:** with Claude Code's
full-width auto-update-failure footer on screen (seen at 200x50), a seat in
the opt-in draft-aware typing mode whose input is empty can hold an ordinary
send instead of delivering it. The message is kept, not lost, and
`rig seat held-messages` shows it. The clipped footer at 80x24 doesn't do
this.
- **Draft-aware delivery to a busy Codex seat:** on Codex (seen with 0.167.1
and 0.167.2), a send to a seat in the opt-in draft-aware typing mode while
it's busy can be pasted into its input without Enter. The message isn't
delivered, and it isn't kept for `rig seat held-messages`. A deliberate send
reports failed, and its text stays in the seat's input box. This was seen on
0.7.12 and 0.7.14, and there's no fix for it in this release.
- **Linked Codex config files:** when a Codex seat's `config.toml` or
`hooks.json` is a symbolic link, OpenRig records its hook trust under the
link's target, as in 0.7.14. A change to key it by the file's own name isn't
in this release, and such a seat may be asked to review hooks again. With a
linked `config.toml`, OpenRig can also miss a setting stored for it that
turns OpenRig's hooks off, and set those hooks up anyway when it moves them
into `hooks.json`. ([#3078](https://github.com/mvschwarz/openrig/issues/3078))
- **Slack reply threads:** recording the arrival note is best effort: a failed
write is not retried and that answer posts top-level. Creating the message
and recording the arrival note are separate writes, so a notification can
also be posted before the note is recorded, with the same top-level outcome.
The person's message is kept. ([#3120](https://github.com/mvschwarz/openrig/pull/3120))
- **From 0.7.14:** [its known limits](https://github.com/mvschwarz/openrig/releases/tag/v0.7.14)
still apply, except where this release changes them. [#3105](https://github.com/mvschwarz/openrig/pull/3105) fixes the
TUI tables' dropped check-in line, and [#3097](https://github.com/mvschwarz/openrig/pull/3097) narrows the draft-aware paste
window described above. 0.7.14's other limits weren't re-checked for this
release unless named here.
### Credits
Thanks to @dajiaohuang, @Kayhusk, @billbenetti, @Aummadour, @smota,
@vanducng, @zichen0116 and @Nikhila-Dhevi-R for merged contributions. Thanks
also for the reports behind these fixes: @Aummadour ([#2989](https://github.com/mvschwarz/openrig/issues/2989)), @vanducng
([#3128](https://github.com/mvschwarz/openrig/issues/3128)), @Alejandro-SJ ([#2990](https://github.com/mvschwarz/openrig/issues/2990)), @billbenetti ([#2976](https://github.com/mvschwarz/openrig/issues/2976)) and @Kayhusk
([#3131](https://github.com/mvschwarz/openrig/issues/3131)).
## [0.7.14]
OpenRig 0.7.14 is a patch for steadier circulation on a busy team. A burst of node
reads now takes turns. In the change's own tests, a health check or a queue read
then waited for one step of it instead of the whole burst; a comparison under
load didn't attribute that gain to this change. Bounded queue listings over a
long retained history use indexes instead of scanning it. An assistant connected to
your team can reach a seat by the name its team shows, connected assistants
appear in `rig ps` and the TUI, and setup guidance tells the agent to fit a
first team to your logins. There are fixes across delivery, seats and the TUI,
many contributed by people using OpenRig. See Known limits for what this
release doesn't fix.
### Before you upgrade
- **Stop the daemon, back up the database, install 0.7.14, then start the
daemon.** Your agents keep running through a daemon restart; don't use
`rig down`, which ends them.
```sh
rig daemon stop
cp "$(rig config get db.path)" "$(rig config get db.path).before-0.7.14"
npm install -g @openrig/cli@0.7.14 # or your existing installation method
rig daemon start
```
- **Three migrations run before the daemon listens.** Migration 163 adds the
`network_installations` table and four columns to `network_bindings`.
Migrations 164 and 166 build indexes. The author's measurement of 166 took
3.47 s on 28,976 rows, not a real installation's size, so a large database can
take longer. The write overhead, and going back to 0.7.13 after these three
migrations, aren't established; keep the backup. ([#3007](https://github.com/mvschwarz/openrig/pull/3007), [#3008](https://github.com/mvschwarz/openrig/pull/3008), [#3090](https://github.com/mvschwarz/openrig/pull/3090))
- **If the start is reported pending:** the CLI waits 20 s, then may print
"Daemon startup is pending after the readiness deadline". Readiness remains
unconfirmed. It leaves the child running and the startup reservation
retained, without recording the daemon. Inspect the address and log it
prints. The child may later answer, and status can then report it running,
but that does not finish this start's recording or release its reservation.
Don't start it again until the steps below are done. The message's restart
steps stop that child and clear the reservation: stop the named child PID and
wait for it to exit, inspect `daemon-start.lock`, and only when both recorded
launcher and child PIDs are absent, archive the lock and run
`rig daemon start`. On a large database the new start can be reported pending
again; reaching a recorded start this way hasn't been tested.
`rig daemon stop` cannot stop that unpublished child.
- **The `openrig` plugin carries the CLI's version, 0.7.14; openrig-core stays
0.1.29.**
### Under load
- Node reads (`rig ps --nodes`, the TUI, several agents at once) take turns,
one step at a time. In the change's own tests, health checks and queue reads
were no longer held behind a whole burst. A comparison under load didn't
attribute a gain to this change: health checks and queue reads were faster
for the release as a whole, `rig ps --nodes` showed no change, and sends were
inconclusive. ([#3101](https://github.com/mvschwarz/openrig/pull/3101))
- Bounded queue listings over retained history use indexes for their ordering
and for live and archived owner notifications. ([#3090](https://github.com/mvschwarz/openrig/pull/3090))
- Codex seats re-checked in one readiness poll share process listings where
each seat's observation order and the 2 s freshness allow; each keeps its own
later observation and its own fallback. ([#3061](https://github.com/mvschwarz/openrig/pull/3061))
- New tmux servers raise their soft open-file limit to 8,192 or the hard limit
where they can, keeping an already higher limit. This makes descriptor
exhaustion less likely as panes and viewers add up; servers already running
keep their limits. ([#3070](https://github.com/mvschwarz/openrig/pull/3070))
- A delegation parent gets one standing notice per child seat for unclaimed
rows, not one per row, and held work gets its notice back when its typing
hold clears. ([#3049](https://github.com/mvschwarz/openrig/pull/3049), [#3082](https://github.com/mvschwarz/openrig/pull/3082))
### Your team and the people it works for
- An assistant connected over MCP reaches a seat by its plain name, stored
under the seat's actual session; an unknown name, or one several seats match,
says so. ([#3088](https://github.com/mvschwarz/openrig/pull/3088))
- Connected assistants appear, with their check-in, in `rig ps`, `rig_team` and
the TUI's tables, and their person sees plain tool titles. ([#3057](https://github.com/mvschwarz/openrig/pull/3057), [#3058](https://github.com/mvschwarz/openrig/pull/3058), [#3092](https://github.com/mvschwarz/openrig/pull/3092))
- `rig doctor` and setup say a login is needed when a seat you start runs on
that harness. The operator is taught to check each seat's runtime against
your logins before launch, name any missing sign-in and let you choose.
([#3077](https://github.com/mvschwarz/openrig/pull/3077))
- When the readable person registry holds nobody, `rig daemon start` and
`rig gateway participants` tell the setup agent to offer registration. The
kernel operator is taught to include that offer in its greeting and register
only on your yes. ([#3041](https://github.com/mvschwarz/openrig/pull/3041), [#3071](https://github.com/mvschwarz/openrig/pull/3071))
- A team your agent hands work to knows how to ask you a question, and your
answer wakes it. A click on an ask with several questions records that
choice; the ask stays open until the last one. ([#3059](https://github.com/mvschwarz/openrig/pull/3059), [#3045](https://github.com/mvschwarz/openrig/pull/3045))
- A pairing request's description of itself is quoted wherever it's shown. ([#3067](https://github.com/mvschwarz/openrig/pull/3067))
- `rig queue list` takes exact `--tag`, `--mission` and `--slice` filters, and
several tags must all match. ([#3066](https://github.com/mvschwarz/openrig/pull/3066))
- A skill-only edition of the `openrig` plugin, for Claude's plugin directory,
follows handed-on work and keeps its reply route. ([#3056](https://github.com/mvschwarz/openrig/pull/3056), [#3075](https://github.com/mvschwarz/openrig/pull/3075))
### Fixes
- An ordinary send is refused as needing input while an open Claude Code or
Codex `/model` picker is positively identified; nothing is typed. Close the
picker before trying to deliver the message. This is shown by the change's
own tests; it hasn't yet been checked with the real Claude Code and Codex
programs on this release. ([#3083](https://github.com/mvschwarz/openrig/pull/3083))
- On a resume of a Codex seat's own conversation in a rig with pods, including
`rig up --existing`, OpenRig avoids adding missing guidance blocks to the
repository's tracked `AGENTS.md` when Codex is 0.164.0 or later, no guidance
file or developer instructions are configured, and the configuration and
block sources can be read. ([#3039](https://github.com/mvschwarz/openrig/pull/3039))
- With `recovery.auto_drive_provider_prompts` on, Claude Code's gated-grants
trust check is never answered for you, and the plain trust prompt is answered
"Yes". ([#3073](https://github.com/mvschwarz/openrig/pull/3073))
- `rig terminal open --provider herdr` reuses a live Herdr workspace, and Rex
tiles show the seat's agent and its activity. ([#3068](https://github.com/mvschwarz/openrig/pull/3068), [#2992](https://github.com/mvschwarz/openrig/pull/2992))
- `rig broadcast` prints a held recipient as retained, not sent. ([#3054](https://github.com/mvschwarz/openrig/pull/3054))
- Account erasure also removes OpenRig's own wakes and notices for that
account's seats. ([#3089](https://github.com/mvschwarz/openrig/pull/3089))
- A snapshot without sessions returns its `invalid_snapshot_data` blocker
instead of throwing a TypeError; the restore still stops there. A retried
no-launch import recovers the original rig by its operation ID. ([#3069](https://github.com/mvschwarz/openrig/pull/3069), [#3042](https://github.com/mvschwarz/openrig/pull/3042))
- A rebuilt briefing limits optional superseded recap paths to the newest
three by default when one folder covers that history; the current recap and
all artifact records remain. Other layouts keep the full list, and
`output.rebuild_recaps` set to 0 lists all paths. ([#3055](https://github.com/mvschwarz/openrig/pull/3055))
- `rig compact --state` names the input check that stopped a managed
compaction, and the TUI's graph shows a seat the daemon reads as unknown as
unknown. ([#3053](https://github.com/mvschwarz/openrig/pull/3053), [#3046](https://github.com/mvschwarz/openrig/pull/3046))
- Setting a context-pressure threshold explains when an environment override
inverts the pair, and `rig workflow validate` warns that harness pins aren't
checked against seats. ([#2567](https://github.com/mvschwarz/openrig/pull/2567), [#3102](https://github.com/mvschwarz/openrig/pull/3102))
- The config browser's entries say each setting's type, choices, range, format
and write route. ([#3050](https://github.com/mvschwarz/openrig/pull/3050))
### Experimental
- `rig network` is experimental, unsupported and at your own risk. Its connector
isn't inert: the daemon starts it, sweeps leftover credential folders and
schedules local rounds even with no binding. ([#3007](https://github.com/mvschwarz/openrig/pull/3007), [#3084](https://github.com/mvschwarz/openrig/pull/3084))
- A grant service can hold back only new work under its fair-use limit, and an
app can read what needs a person, and the answers to it, by link. ([#3048](https://github.com/mvschwarz/openrig/pull/3048), [#3008](https://github.com/mvschwarz/openrig/pull/3008))
### Behaviour that changed
- **Node reads:** a nodes response is no longer one snapshot across its steps,
and in a burst each nodes response comes later: about 43 ms against 21 ms
median in a 24-request burst. One very large rig is still one step. ([#3101](https://github.com/mvschwarz/openrig/pull/3101))
- **An assistant naming a seat now hears three errors:** `destination_ambiguous`
(several seats match; the candidates are listed), `destination_not_found` (no
seat it can reach has that name) and `destination_unchecked` (the team
couldn't be read; try again). ([#3088](https://github.com/mvschwarz/openrig/pull/3088))
### Known limits
- **Queue listings:** the new indexes aren't a cure for every stall. Listings of
active work showed no gain, one case was slower, and writes weren't measured. ([#3090](https://github.com/mvschwarz/openrig/pull/3090))
- **Daemon memory:** in a before-and-after comparison under load, the daemon's
resident anonymous memory and its memory when idle afterwards had medians
about 23-25 MiB higher on 0.7.14 than on 0.7.13, but the two versions' runs
overlapped. Neither equal memory nor a regression is established.
- **Draft-aware delivery ([#3076](https://github.com/mvschwarz/openrig/issues/3076)):** an automatic OpenRig notice can still be left beside
a draft started during delivery. In the reproduced paste-window cases the person's draft
stayed intact and no Enter was sent, but OpenRig's text stayed in the input.
Those cases don't cover a later window. On a conditional send, one that names
the conversation it expects, an isolated test with a scripted terminal showed
characters typed while OpenRig ran its last checks before Enter submitted
together with OpenRig's text, and the send reported success. The same test
reproduces it on 0.7.13. It hasn't been tested with a real client or model.
- **The TUI's tables** can drop a connected assistant's check-in line when its
rig's seat inventory can't be read; `rig roster` still prints it. ([#3105](https://github.com/mvschwarz/openrig/pull/3105) isn't in this release)
- **Repository guidance on restore:** rigs without pods, old or unknown Codex
versions, configured guidance and unreadable configuration keep the existing
file path. Fresh, fork and handover launches are outside this resume fix.
Existing managed blocks may still refresh, and the seat-skills line still
belongs to the file writer; check `git status`. A block whose source can't be
read is reported missing.
- **Linked Codex config files:** when a Codex seat's `config.toml` or
`hooks.json` is a symbolic link, OpenRig records its hook trust under the
link's target, as in 0.7.13. A change to key it by the file's own name isn't
in this release, and such a seat may be asked to review hooks again. With a
linked `config.toml`, OpenRig can also miss a setting stored for it that
turns OpenRig's hooks off, and set those hooks up anyway when it moves them
into `hooks.json`. ([#3078](https://github.com/mvschwarz/openrig/issues/3078))
- **From 0.7.13:** [its known limits](https://github.com/mvschwarz/openrig/releases/tag/v0.7.13)
still apply, except where this release fixes them. The runtime fixes cover
adding missing guidance on the eligible Codex resumes described above
([#3039](https://github.com/mvschwarz/openrig/pull/3039)) and the unquoted pairing description ([#3067](https://github.com/mvschwarz/openrig/pull/3067)). The fix for
recognized model pickers ([#3083](https://github.com/mvschwarz/openrig/pull/3083)) is shown by the change's own tests and
hasn't yet been checked with the real programs on this release.
This release also changes the operator-first registration ([#3041](https://github.com/mvschwarz/openrig/pull/3041),
[#3071](https://github.com/mvschwarz/openrig/pull/3071)) and pre-launch login guidance ([#3077](https://github.com/mvschwarz/openrig/pull/3077)). In a few test
runs with real agents, the operator offered registration once in its
greeting, on Claude Code and on Codex (one run each); a no registered nobody,
and a yes registered the default person. The person saw the offer in 3 of 4
runs; in the fourth, the setup agent didn't pass the operator's offer on.
The login guidance worked in both runs tested, once from the setup agent and
once from the operator. 0.7.13's other limits weren't re-checked for this
release unless named here.
### Credits
Thanks to @Kayhusk, @billbenetti, @cavalcanticampos, @Aummadour, @Tankard,
@w4rc0n and @Totopo27 for merged contributions. Thanks also for the reports
behind these fixes: @Kayhusk ([#3035](https://github.com/mvschwarz/openrig/issues/3035)), @Tankard ([#2583](https://github.com/mvschwarz/openrig/issues/2583)),
@dajiaohuang ([#2529](https://github.com/mvschwarz/openrig/issues/2529)), @billbenetti ([#2728](https://github.com/mvschwarz/openrig/issues/2728)), @emilio-gagliardi ([#3087](https://github.com/mvschwarz/openrig/issues/3087)), @korallis
([#161](https://github.com/mvschwarz/openrig/issues/161)) and @w4rc0n ([#3017](https://github.com/mvschwarz/openrig/issues/3017)).
## [0.7.13]
OpenRig 0.7.13 is a patch for people running their own agent teams. When you answer a runner's permission request, you now get a
+5 -5
View File
@@ -26,9 +26,9 @@ use:
```sh
# Claude Code
npx skills add https://github.com/mvschwarz/openrig/tree/v0.7.12/skills/rigs --skill rigs --agent claude-code
npx skills add https://github.com/mvschwarz/openrig/tree/v0.7.15/skills/rigs --skill rigs --agent claude-code
# Codex
npx skills add https://github.com/mvschwarz/openrig/tree/v0.7.12/skills/rigs --skill rigs --agent codex
npx skills add https://github.com/mvschwarz/openrig/tree/v0.7.15/skills/rigs --skill rigs --agent codex
```
Then type `/rigs` in Claude Code, or `$rigs` in Codex, and say what you want built. The skill has your agent tell
@@ -68,11 +68,11 @@ Requires Node.js 22 or 24 and tmux, on macOS or Linux, and one working Claude Co
### Install it yourself
This runs OpenRig's install script from the `v0.7.12` release. It checks Node.js and npm, installs the latest published `@openrig/cli` with `npm install -g`, runs the Node.js and SQLite check, then runs `rig setup --dry-run` and `rig setup`. The first line prints that plan and changes nothing:
This runs OpenRig's install script from the `v0.7.15` release. It checks Node.js and npm, installs the latest published `@openrig/cli` with `npm install -g`, runs the Node.js and SQLite check, then runs `rig setup --dry-run` and `rig setup`. The first line prints that plan and changes nothing:
```bash
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.7.12/scripts/install.sh | sh -s -- --dry-run
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.7.12/scripts/install.sh | sh
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.7.15/scripts/install.sh | sh -s -- --dry-run
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.7.15/scripts/install.sh | sh
```
If the last step isn't ready, the installer reports `INSTALLED, SETUP NOT COMPLETE` and points to the details from `rig setup` above. Follow that output and any reported Fix, then rerun `rig setup`; the CLI does not need reinstalling. If the only remaining failures are provider sign-ins, sign in to each selected provider as below and continue.
+4 -4
View File
@@ -185,10 +185,10 @@ agent doing the setup. Both use this release's tag:
```sh
# Claude Code
npx skills add "https://github.com/mvschwarz/openrig/tree/v0.7.12/skills/rigs" --skill rigs --agent claude-code
npx skills add "https://github.com/mvschwarz/openrig/tree/v0.7.15/skills/rigs" --skill rigs --agent claude-code
# Codex
npx skills add "https://github.com/mvschwarz/openrig/tree/v0.7.12/skills/rigs" --skill rigs --agent codex
npx skills add "https://github.com/mvschwarz/openrig/tree/v0.7.15/skills/rigs" --skill rigs --agent codex
```
If OpenRig is already installed, use the release tag matching your installed `rig --version` instead: replace the tag
@@ -203,11 +203,11 @@ You can also read [`skills/rigs/SKILL.md`](../../skills/rigs/SKILL.md) directly.
script will do and changes nothing:
```sh
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.7.12/scripts/install.sh | sh -s -- --dry-run
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.7.15/scripts/install.sh | sh -s -- --dry-run
```
The same command without `--dry-run`,
`curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.7.12/scripts/install.sh | sh`, installs the
`curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.7.15/scripts/install.sh | sh`, installs the
latest published `@openrig/cli` with `npm install -g`, runs the Node.js and SQLite check if the installed checker is
present (otherwise it reports the check skipped), then runs `rig setup --dry-run` and `rig setup`. `rig setup` checks both Claude Code and
Codex and may install a missing one, as described below. A failed step prints
+2 -2
View File
@@ -12,9 +12,9 @@ In your project folder, add OpenRig's `rigs` skill to the agent you use:
```sh
# Claude Code
npx skills add https://github.com/mvschwarz/openrig/tree/v0.7.12/skills/rigs --skill rigs --agent claude-code
npx skills add https://github.com/mvschwarz/openrig/tree/v0.7.15/skills/rigs --skill rigs --agent claude-code
# Codex
npx skills add https://github.com/mvschwarz/openrig/tree/v0.7.12/skills/rigs --skill rigs --agent codex
npx skills add https://github.com/mvschwarz/openrig/tree/v0.7.15/skills/rigs --skill rigs --agent codex
```
These use this release's tag, for a new install. If OpenRig is already installed, replace the tag after `tree/` with
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -1,12 +1,12 @@
{
"name": "openrig",
"version": "0.7.1",
"version": "0.7.15",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openrig",
"version": "0.7.1",
"version": "0.7.15",
"license": "Apache-2.0",
"workspaces": [
"packages/daemon",
@@ -7298,7 +7298,7 @@
},
"packages/cli": {
"name": "@openrig/cli",
"version": "0.7.1",
"version": "0.7.15",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -7336,7 +7336,7 @@
},
"packages/daemon": {
"name": "@openrig/daemon",
"version": "0.7.1",
"version": "0.7.15",
"license": "Apache-2.0",
"dependencies": {
"@hono/node-server": "^1.13.0",
@@ -7380,7 +7380,7 @@
},
"packages/ui": {
"name": "@openrig/ui",
"version": "0.7.1",
"version": "0.7.15",
"license": "Apache-2.0",
"dependencies": {
"@fontsource-variable/jetbrains-mono": "^5.2.8",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "openrig",
"version": "0.7.1",
"version": "0.7.15",
"private": true,
"license": "Apache-2.0",
"type": "module",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@openrig/acp",
"version": "0.7.1",
"version": "0.7.15",
"description": "OpenRig as an agent in ACP apps: starts rig acp serve from the @openrig/cli it pins.",
"type": "module",
"bin": {
@@ -15,7 +15,7 @@
"node": ">=22"
},
"dependencies": {
"@openrig/cli": "0.7.1"
"@openrig/cli": "0.7.15"
},
"license": "Apache-2.0",
"author": {
+2 -2
View File
@@ -1,7 +1,7 @@
{
"id": "openrig",
"name": "OpenRig",
"version": "0.7.1",
"version": "0.7.15",
"description": "Send work to your OpenRig team of agents and follow it through to the result",
"repository": "https://github.com/mvschwarz/openrig",
"website": "https://openrig.dev",
@@ -12,7 +12,7 @@
"license_url": "https://github.com/mvschwarz/openrig/blob/main/LICENSE",
"distribution": {
"npx": {
"package": "@openrig/[email protected]"
"package": "@openrig/[email protected]5"
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@openrig/cli",
"version": "0.7.1",
"version": "0.7.15",
"type": "module",
"description": "Build your own network of agents from Claude Code, Codex and Pi: persistent teams with roles, shared context and owned work.",
"keywords": [
@@ -1,6 +1,6 @@
{
"name": "openrig-core",
"version": "0.1.29",
"version": "0.1.30",
"description": "OpenRig canonical skills and hooks for cross-runtime agent topology coordination, continuity, and operating discipline.",
"author": {
"name": "Mike Schwarz",
@@ -1,6 +1,6 @@
{
"name": "openrig-core",
"version": "0.1.29",
"version": "0.1.30",
"description": "OpenRig canonical skills and hooks for cross-runtime agent topology coordination, continuity, and operating discipline.",
"author": {
"name": "Mike Schwarz",
@@ -1,6 +1,6 @@
{
"name": "openrig",
"version": "0.1.0",
"version": "0.7.15",
"description": "Work with your OpenRig team from your coding agent: hand it work, follow that work, answer its questions and read what it returns.",
"author": {
"name": "Mike Schwarz",
@@ -1,6 +1,6 @@
{
"name": "openrig",
"version": "0.1.0",
"version": "0.7.15",
"description": "Work with your OpenRig team from your coding agent: hand it work, follow that work, answer its questions and read what it returns.",
"author": {
"name": "Mike Schwarz",
@@ -1,6 +1,6 @@
{
"name": "openrig",
"version": "0.1.0",
"version": "0.7.15",
"description": "Work with your OpenRig team from your coding agent: hand it work, follow that work, answer its questions and read what it returns.",
"logo": "assets/logo.svg"
}
@@ -1,7 +1,7 @@
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "openrig",
"version": "0.1.0",
"version": "0.7.15",
"description": "Work with your OpenRig team from your coding agent: hand it work, follow that work, answer its questions and read what it returns.",
"author": {
"name": "Mike Schwarz",
@@ -11,7 +11,7 @@ not know exists.** So the point of what follows is not skill. It is recognition:
lands, something rhymes, and you go check instead of building.
Ninety-three top-level verbs ship (capability canon refreshed through
`capability-delta-v0.7.13`). This marker describes the pack's teaching, not publication or live
`capability-delta-v0.7.15`). This marker describes the pack's teaching, not publication or live
adoption. Read this once for shape, and let it make you suspicious that a thing already exists.
What changed in the installed version, and what to do about it, is one read away:
`rig context get reference/whats-new.md`.
@@ -33,7 +33,10 @@ one command away. Your memory of the fleet is a claim about the past.
*including the broken ones*. **The bare form is the question you usually want:** `--active`
filters to running and **hides detached, exited and attention states** — which are exactly the
cells you were scanning for. A seat whose tmux session is confirmed gone reads `detached`
(activity reason `session_missing`), and `rig ps --nodes` says to restore or relaunch it.
(activity reason `session_missing`), and `rig ps --nodes` says to restore or relaunch it. Since 0.7.14 `rig ps` ends with connected
assistants and their check-in line, and node reads take turns through their three synchronous
steps, so other reads and sends aren't held behind a burst of them; a nodes response is no longer
one snapshot across those steps, and in a burst each one comes later.
A verified exited process in a retained pane reads `pane_exited`, instead of running and ready;
outstanding work uses the existing recovery-finding route. An unknown observation is not an exit.
`rig ps --nodes` also shows the model each seat was last seen using (`modelObservation`, with its
@@ -109,7 +112,8 @@ one command away. Your memory of the fleet is a claim about the past.
Closed work's content and metadata can also be erased on a schedule
(`retention.closed_content_days`, `retention.closed_metadata_days`; `0`, the default, keeps them),
with an optional `retention.erasure_ledger`, for a retention policy or an account deletion. It's
opt-in, and a retried create can't bring erased work back.
opt-in, and a retried create can't bring erased work back. Since 0.7.14 an account's erasure also
removes OpenRig's own wakes and notices for that account's seats.
- **Check the exit code.** Many commands used to print nothing and exit 0 when a daemon read
failed; now they exit 1 or 2. Empty output from a failed read is not empty state.
@@ -124,7 +128,9 @@ unseen — which makes messages the one delivery channel that never gets skipped
consequential delivery gets an effect check at the far end rather than a blind retry.
`--verify` exits 1 when it can't confirm what its Enter did (`submission unconfirmed`, or text
staged but not submitted): the message may still have arrived, so inspect with `rig capture`
before any resend, and don't let a script resend on that exit code alone. A
before any resend, and don't let a script resend on that exit code alone. Since 0.7.14 an ordinary
send is refused as needing input while an open Claude Code or Codex `/model` picker is
positively identified; nothing is typed. A
producer-link advisory reading `no_activity_signal` means activity could not be determined;
confirm by effect, never relaunch on it. A send or wake to a seat whose latest hook is a
question waiting for a person is refused with `target_needs_input`, even when its screen
@@ -175,7 +181,8 @@ unseen — which makes messages the one delivery channel that never gets skipped
and the box was empty after Enter; it only reports.
- **`rig broadcast`** — one fact to everyone at once, rather than relaying it N times and getting
the wording wrong on the fourth. **Blast radius is real** — on a large rig this lands in every
seat's turn. A broadcast that reached no one says so.
seat's turn. A broadcast that reached no one says so, and a recipient a seat's setting held
reads as retained, not sent.
- **`rig capture <seat>`** — read what is on their screen right now. **`--rig` / `--pod`** captures
every seat in one call. `--lines N` is the last N lines (stderr says how many earlier lines were
cut); `--history-plus-pane` gives the older N history lines plus the whole pane. A short capture
@@ -255,7 +262,7 @@ unseen — which makes messages the one delivery channel that never gets skipped
default. **Opening `rig terminal open saved:kernel --window` is the step after install or start**:
say you're opening it, run it on the daemon's desktop, then tell the person where it opened. Don't
wait for a phrase like “show me my agents”; the same command brings their agents back later. No
YAML, herdr when installed or plain tmux. The layout follows the terminal's width: on a narrow
YAML, herdr when installed or plain tmux. Rex tiles show each seat's agent and its activity. The layout follows the terminal's width: on a narrow
one the operator gets the first tab; on a wide one the dashboard and operator sit side by side
and the advisor gets its own tab. A `kernel` view the person saved still wins.
Tiles show their seat's screen without attaching, so whole lines show at any tile width; a
@@ -345,7 +352,12 @@ unseen — which makes messages the one delivery channel that never gets skipped
never posted. By default, an emoji reaction on a message posted for an ask reaches the asking seat
as a task tagged `human-reaction`: a signal to interpret, not an answer, and the ask stays open.
`rig slack setup --reactions off` disables that routing; the default is `route`, and regenerating
the app manifest preserves the setting.
the app manifest preserves the setting. Since 0.7.15 link and media previews on posts to a person
are off by default: `rig slack setup --unfurl on` turns them back on from the connector's next
rewire, and `rig slack status` shows the mode. When the arrival note is recorded before the
answer is routed, an answer to a reply in an older thread posts back into that thread; a failed
note write posts it top-level; a notification posted before the note is recorded can also lead
to a top-level answer.
It needs the `reactions:read` scope and the `reaction_added` event (reinstall the app);
`rig slack verify` warns when the scope is missing. A long ask is posted in numbered thread parts; one that
still can't be posted comes back to the asking seat as a `slack-undeliverable` task. Replies
@@ -480,7 +492,8 @@ relay is a reason to inspect the available routing, not to surrender judgment to
- **`rig workflow validate`** — will this spec instantiate at all, before a run finds out for you. A
step with no allowed exit that can finish it or route onward is reported as `step_cannot_finish`.
An allowed mapped exit is valid; in a dependency graph, `handoff` can finish a sink without a next
step.
step. Since 0.7.14 each step with a `harness:` pin gets a `harness_pin_unchecked` warning:
validation doesn't check pins against seats.
- **`rig workflow compile <mission>`** / **`instantiate-lifecycle`** — derive one executable graph
from `project.yaml` → `mission.yaml` → `slice.yaml`, inspect it without writing, then start an
eligible graph with an opaque replay key. Typed acceptance candidate, verdict, and evidence
@@ -632,6 +645,9 @@ not have to tear one down to change it, and you rarely have to start from nothin
steps): hand it work, follow it, answer the team's questions in their own words, and read the
result. VS Code, Copilot CLI and Cursor load it with no seat: give the app its own
`rig mcp serve --front-door` server with `OPENRIG_SESSION_NAME` set and turn the plugin's off.
A skill-only edition for Claude's plugin directory, in `plugins/claude-directory/`, follows
handed-on work and keeps its reply route. The plugin needs Claude Code 2.1.119 or later: on
earlier versions, with its OpenRig seat setting blank, the agent has no OpenRig tools.
- **A Claude member can bring its own setup** in the rig spec: `agent_file`,
`append_system_prompt_file`, `plugin_dirs` and `advisor` (an Advisor model). They're copied into
that seat's own storage, never the project, so one seat in a shared repository can have its own
@@ -715,6 +731,8 @@ not have to tear one down to change it, and you rarely have to start from nothin
(or, when it names none, the only one Codex has logged since it started); OpenRig can't tell
which conversation Codex is on now. If the conversation was changed inside the running Codex,
the restart may refuse or resume the earlier one, so look at the pane (`rig capture <seat>`) first.
Since 0.7.15, when it refuses a recoverable seat whose tmux session is gone (`session_not_live`),
it names the way back: `rig launch '<rig>' '<node>'` (add `--plan` to preview).
- **`rig add`** / **`rig expand`** — graft one more member, or a whole new pod, onto a rig that is
already running. `rig add`, `rig grow` and `rig remove` take the rig's name or ID; a name two active rigs share is refused
with `rig_ambiguous` and the candidates. `rig grow --new-pod` and `rig expand` are one ingress; choose by input shape.
@@ -782,6 +800,14 @@ version, from checking the environment to a useful report to the OpenRig team.
Stop distinguishes process/listener exit from a clean asynchronous drain: inspect the matching
`daemon-shutdown.json` and log when completion is incomplete or unverified. No target is a
distinct no-op, and a timeout never proves down. Use command help for exact limits and recovery.
Since 0.7.15 start gives the child it started a 180-second readiness budget, with a progress
line about every 20 s: let it finish rather than starting again. Synchronous work can run past
the budget, and it isn't a cure for a slow start.
When the daemon stalls, read `$OPENRIG_HOME/logs/slow-operations.jsonl` before guessing: it names
slow synchronous steps by site, since 0.7.15 including three queue sites and three selected steps
of an ordinary nodes read (`nodes.inventory.build`, `nodes.inventory.activity_sync`,
`nodes.inventory.terminal_work`). These nodes records are written after a slow call completes; a
missing record doesn't rule out unfinished work or work outside those steps.
On Linux, when the daemon runs as a systemd user service, the seats' tmux server starts in its
own user scope, so stopping or restarting the daemon's unit keeps the seats; otherwise it starts
as before, with one log line giving the reason.
@@ -848,6 +874,12 @@ version, from checking the environment to a useful report to the OpenRig team.
Use `rig launch ... --snapshot-id <id>` when selection must be exact, and `rig restore status
<attempt> --rig <rig>` for the derived intended-set receipt after an asynchronous restore.
restore-check's per-seat `seat.<s>.queue-file` check is now `seat.<s>.queue-store`.
Since 0.7.15, for an eligible seat in a rig with pods that it starts in its own pane, restore
tries the saved conversation when the seat has a resume token but no saved startup context, and
records the `startup_context` gap; a successful resume reads `partially_restored`, and a missing
conversation or failed start keeps its own status. restore-check reads that seat yellow. Both warn when a snapshot's named Codex profile was cleared
from the current seat. A rig can still read `fully_restored` past a seat restore didn't start or
one left attached, and a seat left as a bare shell still counts as running in `rig ps`.
- **`rig restore-packet write` / `read` / `validate`** — a seat is about to die or must move
runtimes; capture what it knows into a portable artifact instead of losing it with the process.
- **Upgrading is agent-led** — load the shipped `openrig-upgrade` skill for the bounded inspect,
@@ -946,6 +978,12 @@ version, from checking the environment to a useful report to the OpenRig team.
re-nudges). `--enabled true|false` remains compatible: true selects hold, false selects off.
Known limit: in `draft-aware`, a multi-line message to a Claude seat can stay unsubmitted when the
paste changes Claude's footer hint, and later sends wait behind it; check the seat's composer.
Since 0.7.15 `draft-aware` re-checks right before an automatic paste and holds after recent
client activity or when tmux can't be read; typing after that last check still isn't seen. With
Claude Code's full-width auto-update-failure footer on screen, an empty `draft-aware` seat can
hold an ordinary send; `rig seat held-messages` shows it. On Codex (seen with 0.167.1 and
0.167.2), a send to a busy `draft-aware` seat can be pasted without Enter, neither delivered
nor held; its text stays in the input, and there's no fix for it in this release.
`rig seat status` shows the requested/effective mode; wait for pending activation to finish.
**`rig seat held-messages`** lists what was retained and **`rig seat retire-held-message`**
retires one; changing mode does not replay old held messages. A seat that starts, or a successor
@@ -962,7 +1000,8 @@ version, from checking the environment to a useful report to the OpenRig team.
- **`rig compact-plan`** → **`rig compact`** — who is near the context wall, then act on it.
**Ordering matters: running `compact` without the plan is guessing which seat needed it.**
Managed compaction waits for the seat's restore map before compacting; `rig compact --state`,
`--cancel` and `--skip-map` inspect, stop or skip that wait. If preparation stops at an ordinary
`--cancel` and `--skip-map` inspect, stop or skip that wait; since 0.7.14 `--state` also names the
input check behind a `draft_input_unknown` stop (`inputReadFailure`). If preparation stops at an ordinary
approval, a manual `rig compact` keeps waiting for someone to answer it, within its 180-second
budget; OpenRig never answers it, and the request times out after 240 seconds. The `claude-compaction-restore`
skill covers writing and reading the map; `claude-compact-in-place` no longer ships. After a
@@ -1018,6 +1057,11 @@ version, from checking the environment to a useful report to the OpenRig team.
cached hook keeps that trust, as with any hook trusted in Codex; the trust covers that
`CODEX_HOME`, so your own Codex sessions there load the plugin too, and its hooks do nothing
outside an OpenRig seat; adding your own Codex hook can trigger a re-review of one OpenRig hook.
Since 0.7.15, when a new openrig-core changes one of those six hook declarations, the boot or
launch that installs it renews OpenRig's approvals from its own record, in the daemon's Codex
home at boot and in a seat's own Codex home at its launch, instead of stopping at Codex's review.
A person's own choice in any layer OpenRig reads keeps the review. A linked `config.toml` or
`hooks.json` still has its trust recorded under the link's target.
- **Know what compaction costs before you reach for it.** On some runtimes what comes back has
enough context left to believe it knows everything and not enough to actually know anything — and
**the compacted agent is the only one who knows it happened**, while every other seat keeps
@@ -1098,7 +1142,8 @@ scheme, and nothing downstream can see it.
System World, topology, and Project World plan. Add `--runtime` to see the composed managed skill
loadout, `--apply-skills` to reconcile its owned harness projection, or `--deliver` to emit the
exact extant files in order while marking absent pieces visibly. Without the flags it remains
plan-only. Without `--project` it picks the project itself, in order: the only project, the
plan-only. Since 0.7.15 a `--mission` that doesn't exist adds a warning and reads
`missionRoot` null, instead of quietly serving the project's context. Without `--project` it picks the project itself, in order: the only project, the
project whose `rigs:` lists the seat's rig, the deepest project root containing the working
folder, then the only unclaimed project. It reports `position.selectedBy`. If a step is
ambiguous (two projects list the rig, or two roots tie), it stops there and asks for
@@ -1502,7 +1547,10 @@ your circumstances is configuration, and the ones that are not, another agent ca
0.7.13, `rig daemon start` ends with that offer when the person registry can be read and holds
nobody. The kernel operator's startup is also taught to run `rig gateway participants` before its
first greeting and offer registration when nobody is registered. The agent asks first and
registers only on the person's yes.
registers only on the person's yes. Since 0.7.14 setup guidance tells the agent to fit a first
team to the person's logins: a login `rig doctor` reports missing matters when a seat you'd start
runs on that harness, so say which one before launch
(`rig specs preview <team> --kind rig --json` shows each seat's runtime).
- **`rig usage series`** — what a seat's token curve has looked like over time: climbing steadily,
reset, or stopped reporting entirely. The last one is a signal, not a gap.
- **`rig usage top`** — which of this machine's seats used the most tokens over a window (`--window
@@ -1587,7 +1635,12 @@ your circumstances is configuration, and the ones that are not, another agent ca
Clients on MCP protocol revision 2026-07-28 and on the 2025 revisions share the same endpoint.
With a grant service (experimental and off by default; you serve it to the internet behind
your own tunnel), `--task-destination <member@rig>` names where a caller's `rig_queue_create`
goes when it gives no destination, and a seat's name alone resolves within the grant's team. A
goes when it gives no destination, and a seat's name alone resolves within the grant's team. Since
0.7.14 a plain name finds the seat's actual session (its own name, then its pod-member form, then
that name's slug, among seats the grant can address): an ambiguous name returns
`destination_ambiguous` with the candidates, an unknown one `destination_not_found`, and an
unreadable team `destination_unchecked`. Its person sees each tool's plain title, and under a
grant service's fair-use limit only new work is held back. A
grant that names a person adds `rig_question_list` and `rig_question_answer`, so that person
answers the team's questions from their assistant, recorded as their answer through that
connection.
@@ -1601,7 +1654,10 @@ your circumstances is configuration, and the ones that are not, another agent ca
member nothing can wake, or a rung member doesn't claim it in time, its person is told.
A hosted member's roster entry shows whether it's checking in: its expected cadence and its last
check-in, read as checking in, overdue, not yet checked in or checking in on request, and unknown
when that can't be read. It's a status, never a gate on delivery.
when that can't be read. It's a status, never a gate on delivery. Since 0.7.14 `rig ps`,
`rig_team` and the TUI's tables list connected assistants with that check-in line; since 0.7.15
the TUI keeps it when a rig's inventory can't be read. An assistant that passes its team's
display name as `rig_team`'s `rigId` is told to leave `rigId` out to see its own team.
**Internet-facing hosted members are experimental, unsupported, off by default and at your
own risk.** Local assistants can connect locally. Internet access needs your own TLS tunnel or
proxy and a narrowly scoped, revocable token; see `reference/hosted-members.md`. Task terminal
@@ -1659,7 +1715,8 @@ database. It is one more set of primitives on a machine full of them.
original byte size and the exact expansion command. Read the complete original record with
`rig queue show <qitemId> --full --json`; a preview or a compact list is not the whole body.
Compact `rig queue list` rows carry `summary`; the fields they leave out are absent, not empty,
and `fieldsElided` names them.
and `fieldsElided` names them. Since 0.7.14 `--tag` (repeatable; every tag must match),
`--mission` and `--slice` filter exactly, and bounded listings over retained history use indexes.
Delivery status is separate from content: inspect its receipt before claiming it was consumed.
- **"What am I waiting on?"** — the queue row's `waiting` view derives its owner, exact blocker
and blocker owner, last meaningful change, activity confidence and next backstop from live
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@openrig/daemon",
"version": "0.7.1",
"version": "0.7.15",
"private": true,
"license": "Apache-2.0",
"type": "module",
@@ -675,7 +675,7 @@ export class CodexRuntimeAdapter implements RuntimeAdapter {
return;
}
const command = `node "${relay}"`;
const keySource = codexHookSourcePath(hooksPath);
const keySource = canonicalCodexHookSource(hooksPath);
const stripped = upsertCodexFeaturesHooksEnabled(stripCodexActivityHooks(existing, command));
let before: unknown = null;
let expected: { hooks?: { state?: CodexHookState } } | null = null;
@@ -685,7 +685,7 @@ export class CodexRuntimeAdapter implements RuntimeAdapter {
return;
}
if (!expected) return;
const inlineSource = codexHookSourcePath(configPath);
const inlineSource = canonicalCodexHookSource(configPath);
let config = stripped;
for (const event of OPENRIG_ACTIVITY_HOOK_EVENTS) {
const groups = document.hooks[event] ?? [];
@@ -721,13 +721,22 @@ export class CodexRuntimeAdapter implements RuntimeAdapter {
/**
* OPR.0.4.3.33 — compute + splice Codex's `[hooks.state."<key>"] trusted_hash` for our 4
* authored activity hooks into `content`. Codex 0.167.2 resolves the source folder but
* keeps the file name, including a linked file, as returned by app-server hooks/list. The command
* authored activity hooks into `content`. `key_source` is the canonicalized config path
* (Codex keys trust by `std::fs::canonicalize(config.toml).display()`); we best-effort
* `realpathSync` the config path to match — on a miss (file not yet on real disk, or a mock
* fs in tests) we fall back to the plain absolute path, and any resulting key mismatch just
* degrades to the launch-time trust gate (Layer-2 floor), never a broken run. The command
* string is the value Codex deserializes from our TOML literal `'node "<relay>"'` — i.e.
* `node "<relay>"` WITHOUT the outer TOML quote delimiters. Timeout=5, matcher/status None.
*/
private applyCodexActivityHookTrust(content: string, configPath: string, relay: string): string {
const keySource = codexHookSourcePath(configPath);
let keySource = configPath;
try {
keySource = fs.realpathSync.native(configPath);
} catch {
// config.toml not on the real filesystem (first write / unit-test mock fs) — the plain
// absolute path is the honest best guess; a canonicalization delta is fail-safe (gate reappears).
}
const command = `node "${relay}"`;
// upsertCodexActivityHooks has just written our marked block. Codex keys by the
// group's position among this event's groups, not its position inside our block.
@@ -770,7 +779,7 @@ export class CodexRuntimeAdapter implements RuntimeAdapter {
try {
const original = this.fs.readFile(hooksPath);
const document = parseCodexHooksJson(original);
const keySource = codexHookSourcePath(hooksPath);
const keySource = canonicalCodexHookSource(hooksPath);
let config = updated ?? "";
const expected = parseToml(config) as { hooks?: { state?: CodexHookState } };
for (const event of OPENRIG_ACTIVITY_HOOK_EVENTS) {
@@ -2449,10 +2458,9 @@ function tomlSections(text: string, current?: string): TomlSection[] {
return sections;
}
function codexHookSourcePath(file: string): string {
// Resolve a linked home folder, but retain a linked config.toml/hooks.json file's own name.
try { return nodePath.join(fs.realpathSync.native(nodePath.dirname(file)), nodePath.basename(file)); }
catch { return file; } // The folder may not exist yet, including with an injected filesystem.
function canonicalCodexHookSource(path: string): string {
// Codex keys hook trust by the canonical source path; a file not on the real filesystem keeps its plain path.
try { return fs.realpathSync.native(path); } catch { return path; }
}
/** A hooks.json document as Codex reads it: `hooks` maps each event to matcher groups, each with a `hooks` list. */
+8 -40
View File
@@ -9,12 +9,11 @@
// values THIS reproduction emits and the perturbation tests prove the identity fields are folded in
// — but the DEFINITIVE correctness check is a byte-for-byte read-back of a real Codex `[hooks.state]`
// after `/hooks`→"Trust all" (the QA VM proof). Until that read-back confirms them, the exact
// key_source (resolved folder plus file name) and positional indices are PROVISIONAL. A mismatch is
// key_source (canonicalized config path) and positional indices are PROVISIONAL. A mismatch is
// fail-safe (gate reappears + Layer-2 keystroke floor), never a false-trusted run.
import { describe, it, expect, vi } from "vitest";
import nodeFs from "node:fs";
import nodePath from "node:path";
import nodeOs from "node:os";
import { parse as parseToml } from "smol-toml";
import {
CodexRuntimeAdapter,
@@ -30,8 +29,8 @@ const COMMAND = `node "${RELAY}"`;
const EVENTS = ["SessionStart", "UserPromptSubmit", "Stop", "PermissionRequest"] as const;
// PIN TO VM READ-BACK (qa gate) — PROVISIONAL from open-source reproduction (PR #20321 / commits
// 0452dca, ffcc9cc; codex-rs fingerprint.rs version_for_toml). Codex0.167.2 hooks/list confirms
// the source folder is resolved, while the config file keeps its own name even when it is linked.
// 0452dca, ffcc9cc; codex-rs fingerprint.rs version_for_toml). keySource here is the NON-canonical
// mock path `/home/test/.codex/config.toml`; the real key_source is std::fs::canonicalize(config.toml).
const PROVISIONAL_FIXTURE: Record<(typeof EVENTS)[number], { key: string; hash: string }> = {
SessionStart: {
key: `${CONFIG}:session_start:0:0`,
@@ -268,53 +267,22 @@ describe("OPR.0.4.3.33 — provisioning-seam coupling (ensureCodexActivityHooks
expect(fs._store[CONFIG]).toBe(before);
});
it("resolves the source folder while retaining the config file name", () => {
const canonicalHome = nodePath.join("/resolved", "codex");
const nativeRealpath = vi.spyOn(nodeFs.realpathSync, "native").mockReturnValue(canonicalHome);
it("uses native canonical path casing for Codex hook trust keys", () => {
const canonicalConfigPath = "C:\\Users\\Test\\.codex\\config.toml";
const nativeRealpath = vi.spyOn(nodeFs.realpathSync, "native").mockReturnValue(canonicalConfigPath);
try {
const fakeFs = mockCodexFs({ [RELAY]: "// relay" });
makeAdapter(fakeFs).ensureCodexActivityHooks();
const configPath = Object.keys(fakeFs._store).find((path) => path.endsWith("config.toml"))!;
const config = fakeFs._store[configPath]!;
expect(config).toContain(`[hooks.state.${JSON.stringify(`${nodePath.join(canonicalHome, "config.toml")}:session_start:0:0`)}]`);
expect(config).not.toContain(`[hooks.state.${JSON.stringify(`${configPath}:session_start:0:0`)}]`);
expect(nativeRealpath).toHaveBeenCalledWith(configPath);
expect(config).toContain(`[hooks.state.${JSON.stringify(`${canonicalConfigPath}:session_start:0:0`)}]`);
} finally {
nativeRealpath.mockRestore();
}
});
it.skipIf(process.platform === "win32").each([
{ json: false, linkedHome: false }, { json: true, linkedHome: false },
{ json: false, linkedHome: true }, { json: true, linkedHome: true },
])("trusts file names inside the resolved home (JSON: $json, linked home: $linkedHome)", ({ json, linkedHome }) => {
const root = nodeFs.mkdtempSync(nodePath.join(nodeOs.tmpdir(), "codex-linked-trust-"));
try {
const home = nodePath.join(root, "home"); nodeFs.mkdirSync(home);
const selectedHome = linkedHome ? nodePath.join(root, "linked-home") : home;
if (linkedHome) nodeFs.symlinkSync(home, selectedHome);
const source = nodePath.join(root, "source.toml"); nodeFs.writeFileSync(source, 'model = "fixture"\n');
nodeFs.symlinkSync(source, nodePath.join(home, "config.toml"));
const relay = nodePath.join(root, "relay.cjs"); nodeFs.writeFileSync(relay, "// never executed");
if (json) {
const hooks = nodePath.join(root, "source-hooks.json"); nodeFs.writeFileSync(hooks, '{"hooks":{}}\n');
nodeFs.symlinkSync(hooks, nodePath.join(home, "hooks.json"));
}
const adapter = new CodexRuntimeAdapter({ tmux: mockTmux(), activityRelayPath: relay, fsOps: {
exists: nodeFs.existsSync, readFile: p => nodeFs.readFileSync(p, "utf8"),
writeFile: (p,c) => nodeFs.writeFileSync(p,c), mkdirp: p => { nodeFs.mkdirSync(p,{ recursive:true }); },
} });
adapter.ensureCodexActivityHooks(selectedHome);
const hooks = parseToml(nodeFs.readFileSync(source,"utf8")).hooks;
if (!hooks || typeof hooks !== "object") throw new Error("missing hook table");
const states: unknown = Reflect.get(hooks, "state");
if (!states || typeof states !== "object") throw new Error("missing trust table");
expect(Object.keys(states).sort()).toEqual(["permission_request", "session_start", "stop", "user_prompt_submit"]
.map(event => `${nodePath.join(nodeFs.realpathSync.native(home), json ? "hooks.json" : "config.toml")}:${event}:0:0`).sort());
expect(nodeFs.lstatSync(nodePath.join(home, "config.toml")).isSymbolicLink()).toBe(true);
} finally { nodeFs.rmSync(root,{recursive:true,force:true}); }
});
it("is idempotent — re-running produces byte-identical config (no duplicate trust tables)", () => {
const fs = mockCodexFs({ [RELAY]: "// relay" });
const adapter = makeAdapter(fs);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@openrig/ui",
"version": "0.7.1",
"version": "0.7.15",
"private": true,
"license": "Apache-2.0",
"type": "module",
@@ -1,14 +1,14 @@
{
"release": "0.7.12",
"archiveSha256": "e90b2487fb744d7ca69e52e13b2aaef2a204fcfa76db00c373248acc0795eb52",
"release": "0.7.14",
"archiveSha256": "866fb734b32c18c7a938d6f1edbda44c51fce1c87ee4c44839a8a3083d2aba25",
"plugins": {
"openrig": {
"version": "0.1.0",
"sha256": "ee8a1300119576f36889cc6494988c69faa3dfb2c3172cfd856a7ecc3edc0506"
"version": "0.7.14",
"sha256": "42286256af8e2b6153086a95c1eebbcfd40eaf7b70086a88f263a5606d54e591"
},
"openrig-core": {
"version": "0.1.28",
"sha256": "59b2fb34be931a423583283696c22034fa417530ea682268a68156a70730fb5f"
"version": "0.1.29",
"sha256": "5e08deffe33feed6f7beff6df39d70402248632f87e3b63f074835b400fff54d"
}
}
}