mirror of
https://github.com/mvschwarz/openrig-world.git
synced 2026-11-10 17:30:45 -10:00
workshop: Pi skill snapshots regenerated at OpenRig 0.7.2 (#68)
Workshop's Pi seats don't load skills from the openrig-core plugin, so their snapshots are regenerated for 0.7.2: tools/generate-pi-skills.py against v0.7.2 (8b180689), plugin 0.1.19. A Workshop user with Pi seats now gets agents that know 0.7.1's and 0.7.2's commands: per-seat accounts and provider moves, rig mcp serve with tokens, rig seat status, a seat's own skill storage, Steering and the Slack subscription. The lock moves from c99450f3 / 0.1.17 to 8b180689 / 0.1.19 with a new manifest hash. Five skill files change in each agent folder (openrig-user, messaging-the-human, forming-an-openrig-mental-model, delegating-work, and claude-compaction-restore's precompact hook); the same 20 skills, and the review-pair folder is a byte copy. 12 files, +592 -148.
This commit is contained in:
1 parent
8621b39fd7
commit
d1d46383eb
12 files changed
+584
-140
No files matched your search
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"productRepository": "https://github.com/mvschwarz/openrig",
|
||||
"productCommit": "c99450f322646acd6bcd93e11a3bd1474f1d7751",
|
||||
"productCommit": "8b180689e7004b5506fb9f76802e65572c6f6622",
|
||||
"pluginPath": "packages/daemon/assets/plugins/openrig-core",
|
||||
"pluginVersion": "0.1.17",
|
||||
"pluginManifestSha256": "724c5061998abdd7ca42575628ccb526fafdeab7b27992084fffe0d1b2ecffde",
|
||||
"pluginVersion": "0.1.19",
|
||||
"pluginManifestSha256": "e7b5b404934415ae9299c8e3f0e135f60f70eec03d4fb3469bb0513d612fc29c",
|
||||
"piUnselectedSkills": [
|
||||
"claude-compaction-restore"
|
||||
],
|
||||
@@ -70,7 +70,7 @@
|
||||
"path": "skills/claude-compaction-restore/scripts/precompact-hook.mjs",
|
||||
"source": "packages/daemon/assets/plugins/openrig-core/skills/claude-compaction-restore/scripts/precompact-hook.mjs",
|
||||
"mode": "100755",
|
||||
"sha256": "478599ce28f4bdcd1f1e091c687726b598271d834288dcfd80bb2f9adfa2e37a"
|
||||
"sha256": "712e97c8beb02eb6c2815884a960778a225d8f6238dfe04a9b0547c34eaa131d"
|
||||
},
|
||||
{
|
||||
"path": "skills/claude-compaction-restore/scripts/restore-from-jsonl.mjs",
|
||||
@@ -94,7 +94,7 @@
|
||||
"path": "skills/delegating-work/SKILL.md",
|
||||
"source": "packages/daemon/assets/plugins/openrig-core/skills/delegating-work/SKILL.md",
|
||||
"mode": "100644",
|
||||
"sha256": "f40346ec08f0ba22d8dccab0dca8442a8d3783173275d36a4e913185e0e29fbd"
|
||||
"sha256": "fc5638949ce7600446a04e59dbe94996ea910e94beef1fa71cb7a31fa140fce6"
|
||||
},
|
||||
{
|
||||
"path": "skills/developing-openrig/SKILL.md",
|
||||
@@ -106,7 +106,7 @@
|
||||
"path": "skills/forming-an-openrig-mental-model/SKILL.md",
|
||||
"source": "packages/daemon/assets/plugins/openrig-core/skills/forming-an-openrig-mental-model/SKILL.md",
|
||||
"mode": "100644",
|
||||
"sha256": "68307c60a5d8d5d93cbd86e28399b73f336a7f13ebe6e30e9a7b22e818dc7022"
|
||||
"sha256": "cd7bfdcc5a87485140cfcac94bc2c703aec5c5b220a3397e139c4fd6db4339e6"
|
||||
},
|
||||
{
|
||||
"path": "skills/loading-addressable-markdown/SKILL.md",
|
||||
@@ -124,7 +124,7 @@
|
||||
"path": "skills/messaging-the-human/SKILL.md",
|
||||
"source": "packages/daemon/assets/plugins/openrig-core/skills/messaging-the-human/SKILL.md",
|
||||
"mode": "100644",
|
||||
"sha256": "d504477d1e7df04259d750187071ed96e0419f0e5df3358767d53c2ba409e84d"
|
||||
"sha256": "e94095e61cec0d32d96fb8c60df008d0c3ac3bd2fa789a5eb625f46f35945456"
|
||||
},
|
||||
{
|
||||
"path": "skills/mission-slice-sop/SKILL.md",
|
||||
@@ -208,7 +208,7 @@
|
||||
"path": "skills/openrig-user/SKILL.md",
|
||||
"source": "packages/daemon/assets/plugins/openrig-core/skills/openrig-user/SKILL.md",
|
||||
"mode": "100644",
|
||||
"sha256": "cf2fd3f2cf4d7c5182b63ae42e0c2eaf23fb6ddbbc61cef0b26d0d9536ca2476"
|
||||
"sha256": "01cb169050a3d4f51ad7b263772c6ed58f62683df820efeac1a88faadf9cd086"
|
||||
},
|
||||
{
|
||||
"path": "skills/orienting-to-an-inherited-seat/CHANGELOG.md",
|
||||
|
||||
+1
-1
@@ -265,7 +265,7 @@ try {
|
||||
|
||||
const customMessage = readClaudeCompactionMessage();
|
||||
|
||||
const result = spawnSync("node", args, { encoding: "utf8" });
|
||||
const result = spawnSync(process.execPath, args, { encoding: "utf8" });
|
||||
if (result.status !== 0) {
|
||||
const baseFailure = `Claude compaction restore packet generation failed: ${(result.stderr || result.stdout || "unknown error").trim()}. After compaction, load the claude-compaction-restore skill and run restore-from-jsonl manually.`;
|
||||
emit({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: delegating-work
|
||||
description: "Use when you have a task and must decide WHO does it — yourself, a spawned subagent, or another agent in the topology that already holds the context — or how to brief the agent you hand it to, which depends on the model it runs. The rule: how much would a blank slate have to read in to do this correctly? A lot → route to a context-holding agent; a little → subagent; unsure → a real agent or yourself. NOT the mechanics of fanning out (that's dispatching-parallel-agents / subagent-driven-development) — this is the who/whether decision, and the failure mode that makes teams abandon subagents."
|
||||
description: "Use when you have a task and must decide WHO does it — yourself, a spawned subagent, or another agent in the topology that already holds the context — or how to brief the agent you hand it to, which depends on the model it runs. The rule: how much would a blank slate have to read in to do this correctly? A lot → route to a context-holding agent; a little → subagent; unsure → a real agent or yourself. NOT the mechanics of fanning out (that's dispatching-parallel-agents / subagent-driven-development) — this is the who/whether decision, and the failure mode that makes teams abandon subagents. Also use it to decide how far to trust a delegated result."
|
||||
metadata:
|
||||
openrig:
|
||||
stage: field-captured
|
||||
|
||||
+13
-8
@@ -163,9 +163,12 @@ cost when you reach for one.
|
||||
### Where skills come from in OpenRig
|
||||
|
||||
- **Per-agent loadout:** your AgentSpec's `profile.uses.skills: [...]`
|
||||
determines what skills get projected into your runtime skill folder
|
||||
(`.claude/skills/` or `.agents/skills/`) before your harness boots. This
|
||||
is the **structural composition** layer.
|
||||
determines what skills get projected into your seat's own skill storage
|
||||
before your harness boots. A Claude seat gets them through OpenRig's
|
||||
generated plugin, named `openrig:<id>`; a Codex seat through its skill
|
||||
catalog. A seat without that storage gets them in the project's
|
||||
`.claude/skills/` or `.agents/skills/`. This is the **structural
|
||||
composition** layer.
|
||||
- **Cross-pod sharing:** AgentSpecs can `imports: [shared]` to access a
|
||||
shared skill pool. Built-in agents commonly do this.
|
||||
- **Belt-and-suspenders:** the spec projects skill files; startup guidance
|
||||
@@ -176,7 +179,8 @@ cost when you reach for one.
|
||||
|
||||
| Home | Purpose |
|
||||
|---|---|
|
||||
| `<rig-cwd>/.claude/skills/`, `<rig-cwd>/.agents/skills/` | Where the harness actually loads from. Populated by `rig up`. |
|
||||
| `<OpenRig home>/state/seats/<rig id>/<logical seat>/<runtime>/` | A team seat's own skills, outside the project. Claude loads them through the generated plugin (`plugin/skills/`); Codex reads them by path from the seat's `catalog.md` (`skills/`). Populated at launch. |
|
||||
| `<rig-cwd>/.claude/skills/`, `<rig-cwd>/.agents/skills/` | The project's own skills, which the harness also loads. A seat without its own storage gets its skills here. |
|
||||
| `~/.claude/skills/`, `~/.agents/skills/` | User-level harness skill directories. Inspect the current projection and harness configuration to determine which skills are installed and where they came from. |
|
||||
| `packages/daemon/{specs/agents/shared/skills,assets/plugins/*/skills}/` (source checkout) | Product skills that ship with OpenRig — the spec pool + the bundled plugin assets (openrig-user, openrig-architect, forming-an-openrig-mental-model, queue-handoff, claude-compaction-restore, …). |
|
||||
| the skills authoring workspace | Skill authoring source (not runtime-loaded). |
|
||||
@@ -239,9 +243,10 @@ If you're booting into a new seat in an OpenRig rig:
|
||||
`guidance/role.md` for your specific seat.
|
||||
3. **Read the rig's `CULTURE.md`** if it has one — the team operating
|
||||
manual.
|
||||
4. **Check what skills you have** — list `.claude/skills/` or
|
||||
`.agents/skills/` in your cwd. Each skill has a frontmatter description
|
||||
that tells you when to reach for it.
|
||||
4. **Check what skills you have** — `rig skill loadout --seat self` shows
|
||||
your seat's own skills; `.claude/skills/` or `.agents/skills/` in your
|
||||
cwd hold the project's. Each skill has a frontmatter description that
|
||||
tells you when to reach for it.
|
||||
5. **Check your peers** — `rig ps --nodes` and the queue first (who is up,
|
||||
who holds what). Capture a peer (`rig capture <peer-session>`) only when
|
||||
you actually need to see its screen.
|
||||
@@ -302,7 +307,7 @@ that fit your actual work.
|
||||
| "I should manage Codex's compaction the way I manage Claude's" | No. Codex auto-compacts cleanly; Claude doesn't. Different runtimes, different lifecycles. |
|
||||
| "MEMORY.md auto-loads, so I don't need to read it" | Maybe. Sometimes MEMORY.md auto-loads via system reminders; sometimes not. Don't assume. If your work touches the topics it covers, read it explicitly. |
|
||||
| "Skills inherit from a parent or compose like classes" | No. Skills are flat artifacts; composition happens via AgentSpec `profile.uses.skills` (structural) or soft cross-references in skill bodies (advisory). Not via OO-style inheritance. |
|
||||
| "The substrate `shared-docs/skills/` folder is the canonical runtime path" | No. The harness doesn't read there. It's an authoring workspace. Runtime loads from `.claude/skills/`, `.agents/skills/`, and product built-in. |
|
||||
| "The substrate `shared-docs/skills/` folder is the canonical runtime path" | No. The harness doesn't read there. It's an authoring workspace. Runtime loads from the seat's own skill storage, `.claude/skills/`, `.agents/skills/`, and product built-in. |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -71,6 +71,20 @@ rig queue create --destination <entityId>@external \
|
||||
--evidence-ref <durable-evidence> --verify --json
|
||||
```
|
||||
|
||||
An ask is on Steering and the TUI, where the person can answer it, choosing an
|
||||
option or typing a reply; it comes back to you exactly as a Slack answer does.
|
||||
Whether it also posts to Slack is the person's Slack subscription (`rig gateway
|
||||
human show <id>`, `slack-subscription`): by default (`every-ask`) every ask
|
||||
posts, and with `urgent-only` only an ask marked `--alert` does. So forgetting
|
||||
`--alert` never hides an ask from someone who reads Slack.
|
||||
|
||||
Add `--alert` when the ask is urgent: something only they can do, or an approval
|
||||
whose wait costs now. A sign-in, a credential or an approval that expires needs
|
||||
it; a question that can wait for their next look doesn't. An alerted ask stands
|
||||
out in Slack (an interrupt with a mention, within their quiet hours and
|
||||
availability), and it's the only kind that reaches someone on `urgent-only`.
|
||||
An update (`--human-intent update`) posts as before and takes no `--alert`.
|
||||
|
||||
Before asking a **decision**, check whether the person has a mode on with a
|
||||
decision room: what they said the team may decide for them meanwhile.
|
||||
|
||||
@@ -125,8 +139,8 @@ posts as a new message.
|
||||
A **decision** with a few clear choices can carry `--human-questions-file <path>`:
|
||||
a JSON array of 1–4 questions, each
|
||||
`{"id", "question", "options": [{"id", "label", "recommended"?}]}` with 2–4
|
||||
options (labels up to 75 characters, at most one recommended). Slack shows each
|
||||
question as a row of buttons. Each click records that answer on the item, and
|
||||
options (labels up to 75 characters, at most one recommended). The TUI lists
|
||||
each question with its options, and Slack (when alerted) shows each as a row of buttons. Each click records that answer on the item, and
|
||||
the decision resolves once every question has one. You then receive one reply
|
||||
row listing the answers. In `humanAnswers`, clicked answers are option-id strings.
|
||||
A whole typed reply is an object:
|
||||
@@ -148,8 +162,9 @@ well would issue another notification for the same decision.
|
||||
|
||||
The row persists before bounded delivery verification. Read its qitem ID and
|
||||
verification result; `posted` proves connector posting, **not human readership**.
|
||||
`transport-failed`, `never-posted`, or a pending/indeterminate result leaves the
|
||||
row intact. Inspect that same row and its next action; never create a second row
|
||||
`not-alerted` means the person's Slack subscription is `urgent-only`, so the ask is
|
||||
on Steering and the TUI without a Slack post, as they chose; it isn't a failure. `transport-failed`, `never-posted`, or a pending/indeterminate result
|
||||
leaves the row intact. Inspect that same row and its next action; never create a second row
|
||||
or blindly resend because verification timed out.
|
||||
|
||||
```bash
|
||||
|
||||
@@ -94,10 +94,23 @@ rig queue handoff <qitem-id> \
|
||||
|
||||
A queue write (`create`, `claim`, `update`, `handoff`, `handoff-and-complete`) answers with the row it wrote; a
|
||||
handoff answers with the closed row and its successor. When that answer shows what you need to establish, use it.
|
||||
Read back (`rig queue show <id> --full --json`, or `rig queue transitions <id>`) when it doesn't, for example:
|
||||
|
||||
- the outcome was INDETERMINATE (a transport failure, or a 502 `remote_queue_write_failed`), or the write warned
|
||||
`qitem_body_not_saved` (an existing row came back). Reconcile by the original ID before any retry;
|
||||
`create` also prints `Queue create request ID: <id> (not proof of persistence)` on stderr *before* it sends the write,
|
||||
so an interrupted create can be reconciled. That ID names a request, not a row. The row is the `qitemId` on stdout
|
||||
when the command exits 0. A non-zero exit means no row was confirmed, even when stdout carries a `qitemId`: an
|
||||
indeterminate answer repeats the request ID so you can reconcile by it. Don't merge stderr into stdout and keep the
|
||||
first `qitem-…` you find there; a refused create prints its request ID first, too.
|
||||
|
||||
Read back (`rig queue show <id> --full --json`, or `rig queue transitions <id>`) when the answer doesn't show what you
|
||||
need, for example:
|
||||
|
||||
- the outcome was INDETERMINATE (a transport failure, or a 502 `remote_queue_write_failed` that may have reached the
|
||||
other host), or the write warned `qitem_body_not_saved` (an existing row came back). Reconcile by the original ID
|
||||
before any retry. A 502 whose `failureClass` is `unknown-host`, `unsupported-transport` or `registry` isn't
|
||||
indeterminate: the daemon refused before contacting any host and wrote nothing (`create` says "No row was
|
||||
created"). `unsupported-transport` means that host entry is SSH-declared. Use the http entry for the same machine
|
||||
from `rig host list`; if there isn't one, add it with that machine's daemon URL (`rig host add --id <id>
|
||||
--transport http --url <daemon-url>`), asking whoever runs that host for the URL;
|
||||
- the output was compact or truncated and the field you need isn't in it (the compact receipt shows `bodyBytes`, not
|
||||
the body, and its footer names what it left out);
|
||||
- you need a transition note, a closure or blocker detail, or a successor that the output you saw omits;
|
||||
@@ -457,6 +470,16 @@ clear updates the current binding and identity verdict without a clear event.
|
||||
Acknowledgment or responsiveness alone does not prove that the original
|
||||
conversation resumed.
|
||||
|
||||
Two startup cases also end in `attention_required`, and the result says what to do:
|
||||
- **Assisted startup** (a community harness preset with `startup_assisted: true`): the startup text and Enter were
|
||||
sent but submission isn't confirmed. Check the pane and submit any staged text, don't resend it, then run the
|
||||
reported `rig seat clear-attention <session> --reason "..."` once the seat has responded.
|
||||
- **A refused startup turn** (Claude answered a startup turn with a refusal such as an expired login): nothing more is
|
||||
typed into the seat, and the result names how many later startup inputs weren't sent. A fresh start that ran no
|
||||
one-shot action stays continuable: `rig seat continue <seat>` delivers the whole startup again, the refused turn
|
||||
included. A resumed or forked seat keeps its conversation and isn't offered continue; a fresh start that can't
|
||||
continue names `rig seat launch <seat> --fresh --stop --reason "<why>"`.
|
||||
|
||||
A 422 response names the uncleared class and its failed check. If the recorded
|
||||
native token needs correction and you know the actual token, use
|
||||
`rig seat set-resume-token <session> --token-stdin --reason <explanation>`, then
|
||||
@@ -818,12 +841,15 @@ rig send <session> "message" --host <id>
|
||||
rig send <session> "message" --json
|
||||
```
|
||||
|
||||
**The send-guard (v0.4.0) — the default is SAFE.** A default `rig send` is guarded: it will NOT submit into an interactive prompt / permission block on the target pane. Flags:
|
||||
**The send-guard (v0.4.0) — the default is SAFE.** A default `rig send` is guarded: it will NOT submit into an interactive prompt / permission block on the target pane. That includes Claude's MCP server approval menu (`N new MCP servers found in .mcp.json`, ending `Space to select · Enter to confirm · Esc to reject all`) while it is the current screen: the send is refused with reason `target_needs_input` and `Refused: '<session>' is at an interactive prompt (selection_prompt). A message must not select or approve it. … No text was sent.` Approving or rejecting those servers is the person's call; `--dangerously-interact` drives it only deliberately. Flags:
|
||||
- `--verify` — delivery evidence.
|
||||
- `--force` — **a back-compat no-op on the send DECISION**: it never bypasses the interactive-prompt/permission guard and never changes whether a message is delivered (a mid-task/busy pane already sends-with-advisory by default). *(It does NOT "bypass activity-risk checks" — that earlier teaching is retired.)* It is **not fully inert**, though — it is still parsed solely to be **rejected in combination with `--wait-for-idle`**: `rig send … --force --wait-for-idle <n>` prints `--wait-for-idle cannot be combined with --force`, exits 1, and sends nothing. So do not read "no-op" as "`--force --wait-for-idle` is harmless"; that pairing errors. *(Verified against current product main `d37a08ad`: the guard-bypass no-op is declared at `send.ts` and confirmed by runtime capture — a plain `--force` send delivers through the ordinary path; the `--wait-for-idle` rejection is enforced at `send.ts`, `routes/transport.ts`, and `session-transport.ts`, and confirmed by runtime capture — exit 1, nothing sent.)*
|
||||
- `--wait-for-idle <seconds>` — wait until the target is explicitly idle before sending. **Cannot be combined with `--force`** (that pairing is rejected: exit 1, nothing sent).
|
||||
- `--raw` — send exact text/keystrokes without the From/To messaging envelope (still guarded against interactive prompts).
|
||||
- `--dangerously-interact --reason "<why>"` — the ONLY override of the prompt/permission guard: deliberately drive an interactive prompt/permission block (implies `--raw`, requires `--reason`, audit-logged).
|
||||
On a guard-attached managed seat, a non-menu override stops and shows existing typed/pasted input (author unknown) with `override_input_present`; unreadable input stops with `override_input_unknown`. Recognized dim suggestions are shown separately and count as empty; a collapsed paste shows only its label, not hidden contents.
|
||||
Inspect the input, deliberately clear the native prompt with native/tmux controls, then retry. Manual clearing is outside OpenRig's lease and observation checks and is not recorded; human typing can still race a read and paste.
|
||||
When you deliberately need to type without that composer check (for example an unsupported input you inspected yourself), add `--type-without-checking` with `--dangerously-interact --reason "<why>"`. It does not clear input and may join or submit existing text. Ordinary sends and wakes keep their current rules.
|
||||
- `--host <id>` — send on a remote host declared in `~/.openrig/hosts.yaml` (ssh hosts shell out; http hosts go CLI-direct to the remote daemon).
|
||||
- `--from <session>` — deprecated and ignored; it does not select the sender. Sender identity comes from the current seat and transport provenance; cross-host envelopes use the durable local origin, not the supplied flag or a relay identity.
|
||||
- `--context <ref>` **(0.5.0)** — attach a composed context pack/piece by ref (see "Context packs and paced delivery"). Small piece → `send --context`; a real pack → `rig walk`. The noun `rig context` composes the ref; the verb delivers it.
|
||||
@@ -971,6 +997,68 @@ rig auth seats … --runtime codex # seat -> profile registry (metadata
|
||||
|
||||
**Hard secret boundary:** no token value is ever printed, logged, queued, streamed, or committed; status/validate report presence/mode/login-state only; seat labels are metadata, not live-account proof. MVP is `--runtime codex`; other runtimes use the same surface with a different `--runtime`, never a parallel command.
|
||||
|
||||
### Give a seat its own Claude or Codex account
|
||||
|
||||
A runtime's login lives in its home: Claude Code's in its config dir (`CLAUDE_CONFIG_DIR`, default `~/.claude`), Codex's
|
||||
in `CODEX_HOME` (default `~/.codex`). A seat that names its own home in `env:` runs on that home's login.
|
||||
|
||||
1. **Prepare the home once, natively,** in a terminal: `CLAUDE_CONFIG_DIR=/abs/path claude`, then `/login`; or
|
||||
`CODEX_HOME=/abs/path codex login`. The person signs in; never copy credential files between homes.
|
||||
2. **Name it in the member's `env:`** in `rig.yaml`, not with `rig up --env`, which leaves these two out
|
||||
(`CLAUDE_CONFIG_DIR: /abs/path` or `CODEX_HOME: /abs/path`), or in
|
||||
the rig's `env:` for every seat of that runtime. The member's value wins. It must be an absolute path: any other
|
||||
value is reported and ignored, and the seat keeps the daemon's home.
|
||||
3. **It takes effect at the seat's next launch** (a fresh launch, restore or handover); a running seat keeps the home
|
||||
it started with. A conversation saved under the old home doesn't resume from the new one.
|
||||
4. **Check it:** `rig seat status <seat>` shows the home and where it came from, and `rig provider status` lists it as
|
||||
the seat's configured account. Both say what's configured, not which login a running session holds.
|
||||
|
||||
Seats may share a home. OpenRig writes its own settings (Codex hooks, managed `config.toml` and plugin; Claude's
|
||||
first-run trust settings) into the seat's home.
|
||||
|
||||
### Move one Codex seat to another login
|
||||
|
||||
For a Codex seat on Linux. The login must already be saved in the Codex home the seat runs on (`rig seat status <seat>`
|
||||
shows it). Other operating systems, and moving a seat to another home, aren't supported by this command yet: say so instead
|
||||
of improvising. A Claude seat moves differently; see the next section.
|
||||
|
||||
1. **Save each login once, in that home:** `CODEX_HOME=<home> codex login` (the person signs in), then
|
||||
`CODEX_HOME=<home> rig auth save <name>`. `rig auth list --codex-home <home>` lists them.
|
||||
2. **Check:** `rig provider precheck --seat <seat> --to-account <name>` shows the home, what the seat is doing, and every
|
||||
peer on that home. A refusal says what's missing, and nothing changes.
|
||||
3. **Move:** `rig provider switch --seat <seat> --to-account <name>`. It saves the home's current login as
|
||||
`before-switch-<time>`, activates the chosen one, and restarts the seat in place on the same conversation. If the
|
||||
current login can't be saved first, nothing changes. Peers on the home aren't restarted. Each keeps its current login
|
||||
only until its next token refresh, when Codex stops it with "signed in to another account". Run the printed
|
||||
`rig seat restart <peer> --keep-conversation --reason "..."` for each to move it too. Don't sign in again in that home: that replaces
|
||||
the login you just switched to.
|
||||
4. **Confirm:** after the seat completes a real tool-using turn, run the printed
|
||||
`rig seat restart <seat> --keep-conversation --verify --expect-account <its Account line>`. Until then the move is
|
||||
unconfirmed. An uncertain restart is reported; never restart it a second time.
|
||||
|
||||
### Move one Claude seat to its own subscription token
|
||||
|
||||
The seat keeps its config dir and gets its own `CLAUDE_CODE_OAUTH_TOKEN`, read from a file as Claude starts. OpenRig keeps only
|
||||
the file's path and your label, never the token.
|
||||
|
||||
1. **Prepare the token once:** the person runs `claude setup-token`, which prints a one-year subscription token, and saves it to a
|
||||
file only their user can read (`chmod 600`). Never paste the token into a message, a row or a command line.
|
||||
2. **Check:** `rig provider precheck --seat <seat> --to-account <label> --token-file <absolute path>`. It refuses, changing
|
||||
nothing, if the file wouldn't apply, the seat isn't running, or something Claude Code ranks above the token is set (a cloud
|
||||
provider, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_API_KEY`, an `apiKeyHelper`). It names each one and how to remove it for that
|
||||
seat. When the token applies, the launch unsets those names for Claude's process, so the pane shell's own exports can't
|
||||
outrank it. Managed settings can't be read; the seat's `/status` shows what it actually uses.
|
||||
3. **Move:** `rig provider switch --seat <seat> --to-account <label> --token-file <absolute path>`. It takes a snapshot that
|
||||
resumes the seat's conversation, sets the reference, and relaunches the seat from that snapshot in its own pane: the pane
|
||||
and its scrollback stay, but work in flight when Claude ended doesn't carry over. Seats sharing the config dir keep their
|
||||
own login. `--stored-login` moves the seat back; if the seat still receives a `CLAUDE_CODE_OAUTH_TOKEN`, Claude uses that
|
||||
instead of the stored login, and the report names it.
|
||||
4. **Confirm:** after the seat completes a real tool-using turn, its `/status` shows token auth, with no account email. The
|
||||
label is yours, not an account Claude observed: say so. A token session can't use claude.ai connectors or Remote Control.
|
||||
The token is in Claude's environment, so the tools, hooks and MCP servers it starts inherit it; don't have it print its
|
||||
environment. A relaunch that didn't resume reads unknown, with its recovery command; never run the switch again for it.
|
||||
`rig seat status <seat>` shows the reference, and says when its file won't apply at the next launch.
|
||||
|
||||
### Manage subscriptions across a fleet (when the person asks you to)
|
||||
|
||||
**This applies only when the person has asked you to manage their subscriptions.** Otherwise, don't take this job on. Installing or upgrading OpenRig appoints no agent, launches no bundle and starts no
|
||||
@@ -981,8 +1069,8 @@ to keep work running, through the Fleet app. Until that bundle is available, the
|
||||
subscriptions can use this section. If that's you, you decide which subscription or home moves, and when. You decide
|
||||
from:
|
||||
- the usage and assignment readings, and their freshness;
|
||||
- your earlier move reports;
|
||||
- the person's standing preferences.
|
||||
- your earlier move reports (`rig provider moves`);
|
||||
- the person's account policy, or their standing preferences when there's no policy.
|
||||
|
||||
You say why, and you see the outcome through. OpenRig gives you the facts and the tools; it never decides for you,
|
||||
and there's no threshold to obey.
|
||||
@@ -996,45 +1084,78 @@ and there's no threshold to obey.
|
||||
- **The Observatory is optional:** it can show readings, but you don't need it to rotate.
|
||||
|
||||
**The unit is a Codex home, with every seat on it.** Moving some seats on a shared home to a different account, and
|
||||
moving Claude seats, aren't supported moves yet: say so instead of improvising.
|
||||
moving Claude seats, aren't supported moves yet: say so instead of improvising. A Codex seat with a home of its own
|
||||
moves alone, with `rig provider switch --seat` (above).
|
||||
|
||||
0. **Load the person's account policy.**
|
||||
- It's the context pack named `account-policy`: check `rig context list` for it, then read it with
|
||||
`rig context get account-policy`. Its manifest's `version` is the version you follow and report.
|
||||
- Follow it, and record the version in each report. A line the person approved ("move the build box's seats without
|
||||
asking", "never move the reviewers") is standing authorization: act on it, don't ask again.
|
||||
- A line grants only what the person actually gave. "Agents may do sign-ins" is usable only where a sign-in route
|
||||
and access already exist; otherwise ask, and record that the preference couldn't be used.
|
||||
- Something the policy doesn't cover: use your judgment or ask the way the policy says, record why, and propose a
|
||||
one-line addition.
|
||||
- **No policy** is never a reason to stop: work from this section, report the policy as `{ "none": true }`, and
|
||||
offer once to write one with them (below).
|
||||
1. **Know the subscriptions.**
|
||||
- Each subscription is reached through a saved profile on a home (`rig auth list` and `rig auth save`) on each
|
||||
machine. Which seats use which profile is `rig auth seats`, and that's metadata: not proof of a live account.
|
||||
- Check you can read each subscription's usage, and name any you can't.
|
||||
2. **Read before deciding.**
|
||||
- Use `rig provider status --all-hosts --json` (add `--refresh` for fresh native readings). Each allowance has
|
||||
its windows, reset times and when it was observed.
|
||||
its windows, reset times and when it was observed, and when it was last observed exhausted.
|
||||
- **One allowance seen on two machines is one allowance:** never add its headroom twice.
|
||||
- **A stale reading** is usable only with its age recorded, or after a refresh.
|
||||
- **An unknown or unavailable reading is never room.**
|
||||
- **An unknown or unavailable reading is never observed room.**
|
||||
- **After a known reset,** a window carries an `expectation` (expected full since that reset). You may plan on it,
|
||||
but it isn't measured: report it as expected, and take a fresh reading when you can.
|
||||
- **Equal reset times don't mean the same account.**
|
||||
- **A seat's link to a subscription is observed, configured or unknown.** Only an observed link shows the seat
|
||||
actually uses it; a configured one (a profile, a registry row) is a claim.
|
||||
- **A sign-in state of `unknown` isn't `needs_sign_in`.** An otherwise authorized move can go ahead, and its uptake
|
||||
evidence settles it. A known `needs_sign_in` is step 6.
|
||||
3. **Decide, and leave seats alone for a reason.**
|
||||
- **Balance, don't chase:** spread machines across the subscriptions with room rather than moving every machine onto
|
||||
the one that just reset. `rig usage top` on each machine shows which work hardest: a high-use machine is worth a
|
||||
subscription of its own, and lower-use machines can share one. Keep a backup where the policy says so.
|
||||
- `rig seat restart <seat> --keep-conversation --plan` shows what a restart keeps, and every peer on the same
|
||||
home. Moving the home moves them all.
|
||||
- Skip a seat that's mid-turn, running a service, or about to finish, and record why.
|
||||
- The person's standing preferences ("never move the reviewers") are yours to follow and record.
|
||||
4. **Move the home.**
|
||||
- On that machine, `CODEX_HOME=<that home> rig auth switch <profile> --runtime codex`. `rig auth` acts on
|
||||
`CODEX_HOME`, or on the default home, and `rig auth status` prints which home it means: check it before
|
||||
switching.
|
||||
- Then restart each chosen seat: `rig seat restart <seat> --keep-conversation --reason "<why>"`.
|
||||
- `rig auth switch <profile> --runtime codex --codex-home <that home>`, on that machine, or from here with
|
||||
`--host <that machine's SSH entry>`. Without `--codex-home`, `rig auth` acts on `CODEX_HOME`, or on the default
|
||||
home; `rig auth status --codex-home <home>` (with `--host` for another machine) prints which home it means:
|
||||
check it before switching. A remote switch always names its home, and copies a profile already saved there.
|
||||
- **Another machine** is reached through its SSH entry in `rig host list`. An entry registered only over HTTP
|
||||
can't run these commands; use the machine's SSH entry, or run them on that machine.
|
||||
- Then restart each chosen seat: `rig seat restart <seat> --keep-conversation --reason "<why>"`, with `--host <id>`
|
||||
for a seat on another machine (the restart runs there, and its audit names you). If the connection drops, the
|
||||
answer is `remote-outcome-unknown`: the switch or restart may have run there, so read what it names (for a
|
||||
restart, `--plan`'s `lastRestart`) before anything else.
|
||||
- After it completes a real tool-using turn, check it: `rig seat restart <seat> --keep-conversation --verify
|
||||
--expect-account <the target's Account line>`. `--verify` only checks; it never restarts. Only that check
|
||||
reports `moved`, and anything less is unconfirmed.
|
||||
- **A slow reply isn't a failure:** never restart a seat a second time. Follow up at your next look.
|
||||
5. **Report every move, partial or unconfirmed included,** to whoever asked for it (a queue item to their seat):
|
||||
- the home and machine;
|
||||
- from and to, as labels;
|
||||
- your reason;
|
||||
- each reading you acted on, with its observation time;
|
||||
- each seat's result (`moved`, `unconfirmed` or `unknown`) with its evidence or what's missing;
|
||||
5. **Report every move, partial or unconfirmed included,** with
|
||||
`rig provider moves report --file <move.json> --to <whoever asked>` (`--help` shows the shape). It becomes a queue
|
||||
item to them; the Fleet app reads these reports once its page is connected to them. It holds:
|
||||
- the home and machine, and from and to, as labels;
|
||||
- your reason, and the policy version you followed (or `{ "none": true }`);
|
||||
- each reading you acted on, `observed` with its observation time, `expected` with the reset it rests on, or
|
||||
`unavailable`;
|
||||
- each seat's result: `moved` only with the verify check's matched account and observed tool turn, kept as separate
|
||||
facts; otherwise `unconfirmed` or `unknown`, with what's missing;
|
||||
- the peers, for example "not restarted; account unverified".
|
||||
|
||||
A home switched with every seat left as a peer is still a move to report. A follow-up that confirms an earlier
|
||||
unconfirmed seat says which report it follows. Never put credentials in a report.
|
||||
unconfirmed seat is a new report with `--follows <the earlier report's qitemId>`. A report to a person's seat names
|
||||
its evidence with `--evidence-ref` (for example, the saved `--verify --json` output). Never put credentials in a
|
||||
report: a value in a recognized credential format is refused. **A refused report changes nothing about the move:**
|
||||
correct it and file it again; never repeat the switch or a restart because a report was refused. Asked "why did
|
||||
these seats move?", even after your own restart, answer from `rig provider moves --seat <seat>`. It reads the newest
|
||||
reports (50 by default, `--limit` up to 200) and then filters them, so it's a recent window, not a seat's whole
|
||||
history: raise `--limit` when you need older ones.
|
||||
6. **When a sign-in is needed:**
|
||||
- Ask the person once, durably: a queue item to their seat, naming the machine and the subscription.
|
||||
- Move nothing onto that subscription until it's done.
|
||||
@@ -1045,6 +1166,41 @@ moving Claude seats, aren't supported moves yet: say so instead of improvising.
|
||||
**What wakes you:** a watchdog, a usage-limit park on the queue, or the person asking. How often you look is your
|
||||
judgment within those.
|
||||
|
||||
**If there's no policy yet.** Start from what you can see, and ask only what you can't; keep it to a few minutes, and
|
||||
never run it as a form. "Whatever you think" or "skip" is an answer: write down the default you'll use.
|
||||
1. **Look first:** `rig auth list` and `rig auth seats` on each machine you can reach, `rig provider status
|
||||
--all-hosts --json`, and `rig usage top` on each machine. Draft what you can.
|
||||
2. **Confirm what you saw,** in one message ("three Codex subscriptions and one Claude one, on two machines; the build
|
||||
box uses about four times the tokens of the laptop; any I can't see?").
|
||||
3. **Ask how they want them used,** offering the common shape: the busiest machine gets its own subscription, the
|
||||
rest share, and one stays spare.
|
||||
4. **Ask what you may do on your own:** move a machine's Codex seats without asking; machines or seats to ask about
|
||||
first or never move; sign-ins (may agents, and through which browser or profile, or always ask). Say what's
|
||||
supported today: moves are by Codex home, Claude moves aren't supported yet, and a sign-in preference is used only
|
||||
where a sign-in route and access exist.
|
||||
5. **Ask how to reach them** (their seat's queue, a chat channel, the Fleet app), for what, and what to do when they
|
||||
don't answer. A channel they name is a preference: check the route with `messaging-the-human`, and use a queue
|
||||
item to their seat if it isn't ready.
|
||||
6. **Ask what to do when things go wrong:** nothing has room, a sign-in is needed while they're away, a move didn't
|
||||
confirm.
|
||||
7. **Show the draft in plain words,** let them correct it, and save it as a context pack named `account-policy` in
|
||||
the context library where you run (`rig config get context.root`): a folder with `manifest.yaml` (`version: "1"`,
|
||||
`name: account-policy`, `taxonomy: world`, a `purpose`, and `files: [{ path: POLICY.md, role: policy }]`) and
|
||||
`POLICY.md`, installed with `rig context add <folder>` and read back with `rig context get account-policy`. To share
|
||||
one policy across machines, use a Git repository: `rig context add <repo> --git`. Labels and references only:
|
||||
never tokens, passwords, cookies or recovery codes.
|
||||
|
||||
`POLICY.md` has these parts, in their words: subscriptions (label, provider, plan, how each is reached); how they
|
||||
want them used; what agents may do on their own; how to reach them; when things go wrong; and a dated **Changes**
|
||||
list. Fill it from what you saw and what they said, never from another person's setup.
|
||||
|
||||
**Changing the policy.** When they say what changed, copy `POLICY.md` aside as the before copy, edit it, add a dated
|
||||
line under **Changes**, and raise `version` in `manifest.yaml`. Keep one place where it's edited: edited where it's
|
||||
installed, run `rig context sync`; kept in a separate folder, replace it with `rig context rm account-policy` and
|
||||
`rig context add <folder>`; Git-backed, edit and commit in the checkout `rig context source inspect account-policy`
|
||||
reports, then `rig context source update account-policy` (it never pushes). Read it back with
|
||||
`rig context get account-policy`; your next decision follows the new version.
|
||||
|
||||
## Context packs and paced delivery (0.5.0)
|
||||
|
||||
**Compose context once, hand it to a seat cleanly.** A library primitive plus a set of delivery flags. The rule that keeps the grammar coherent — internalize this one: **the noun stores and composes; the verbs deliver.** `rig context` never sends anything; delivery is only ever `rig send` / `rig broadcast` / `rig walk` / `rig queue`.
|
||||
@@ -1289,6 +1445,8 @@ Notes:
|
||||
- for tmux-backed self-attach, `rig whoami --json` is the right verification
|
||||
- for raw/external self-attach, `rig ps --nodes --json` is currently the more reliable verification surface
|
||||
- if the current shell is outside tmux, pass `--display-name <name>` when you want a stable human session label recorded
|
||||
- to read a seat's identity or state later, use `rig whoami` or `rig seat status`, not `rig attach --json` output or a file holding a
|
||||
seat's environment. Don't print `OPENRIG_ACTIVITY_*` values: the activity token is a credential, and it doesn't identify the seat
|
||||
|
||||
### Adopt a topology and bind live sessions
|
||||
|
||||
@@ -1459,32 +1617,74 @@ rig specs rename <name-or-id> <new-name> [--json]
|
||||
## MCP
|
||||
|
||||
```bash
|
||||
rig mcp serve [--port <port>]
|
||||
rig mcp serve [--port <daemon-port>] # stdio: a local app starts the server itself
|
||||
rig mcp serve --http [--bind <host:port>] # Streamable HTTP at /mcp (default 127.0.0.1:7437)
|
||||
rig mcp token add <name> --seat <session> [--rig <name>...] [--root <name>...] [--workspace] [--owner]
|
||||
rig mcp token list [--json]
|
||||
rig mcp token revoke <name>
|
||||
```
|
||||
|
||||
Current shipped MCP tools:
|
||||
- `rig_up`
|
||||
- `rig_down`
|
||||
- `rig_ps`
|
||||
- `rig_status`
|
||||
- `rig_snapshot_create`
|
||||
- `rig_snapshot_list`
|
||||
- `rig_restore`
|
||||
- `rig_discover`
|
||||
- `rig_bind`
|
||||
- `rig_bundle_inspect`
|
||||
- `rig_agent_validate`
|
||||
- `rig_rig_validate`
|
||||
- `rig_rig_nodes`
|
||||
- `rig_send`
|
||||
- `rig_capture`
|
||||
- `rig_chatroom_send`
|
||||
- `rig_chatroom_watch`
|
||||
- `rig_add`
|
||||
- `rig_queue_create`, `rig_queue_show`, `rig_queue_list`, `rig_queue_claim`, `rig_queue_update`
|
||||
The 36 shipped tools fall into three groups. Over stdio, and over HTTP with no grants, every tool is offered. An HTTP
|
||||
grant offers the work tools; the workspace tools with `--workspace` or `--owner`; and the owner tools only with
|
||||
`--owner`, which offers every tool.
|
||||
- **Work (19):** orientation, communication and the durable work loop.
|
||||
- `rig_whoami`: the seat the server acts as (rig, pod, peers, edges) and the `caller`: authenticated (an HTTP
|
||||
credential, with its principal) or declared (`OPENRIG_SESSION_NAME` where the server was configured, which nothing
|
||||
verifies).
|
||||
- `rig_status`, `rig_ps`, `rig_rig_nodes`, and `rig_team`: a rig's lead (its one `delegates_to` root, the seat to
|
||||
delegate to) and each seat's role, delegation and activity.
|
||||
- `rig_send`, `rig_capture`, `rig_chatroom_send`, `rig_chatroom_watch`.
|
||||
- `rig_queue_create`, `rig_queue_show`, `rig_queue_list`, `rig_queue_claim`, `rig_queue_update`,
|
||||
`rig_queue_handoff`, `rig_queue_unclaim`:
|
||||
- `revises` on create asks for a linked revision of an earlier item;
|
||||
- show names a handed-off item's successor;
|
||||
- `handedOffFrom` on list follows a handoff;
|
||||
- handoff passes an item this seat holds on to the next seat in one step, as `rig queue handoff` does: the new item
|
||||
carries the work's lineage;
|
||||
- unclaim puts back a claimed item this seat won't finish.
|
||||
- `rig_artifact_read`: a finished item's artifact (its `evidenceRef`) or a named file, through the daemon's shared
|
||||
roots (`OPENRIG_FILES_ALLOWLIST`), in windows (`offset`, `nextOffset`). Outside those roots it answers unavailable
|
||||
rather than reading a local path.
|
||||
- `rig_human_decision_room`, then `rig_queue_decide`: on your own pending ask to a person, read the decision room of
|
||||
the mode they have on, and record a decision you made for them inside it. No tool writes a person's own answer.
|
||||
- **Workspace (4):** `rig_slices`, `rig_mission`, `rig_context_pack`, and `rig_context_get` (one context file or
|
||||
section, as `rig context get` reads it).
|
||||
- **Owner (13):** `rig_up`, `rig_down`, `rig_snapshot_create`, `rig_snapshot_list`, `rig_discover`, `rig_bind`,
|
||||
`rig_add`, `rig_bundle_inspect`, `rig_agent_validate`, `rig_rig_validate`, `rig_telemetry_events` (one bounded page
|
||||
of event metadata), and:
|
||||
- `rig_restore`, which starts a restore and returns its attempt;
|
||||
- `rig_restore_status`, that attempt's receipt.
|
||||
|
||||
The server acts as one seat: the `OPENRIG_SESSION_NAME` in its environment. Every request carries that identity, so
|
||||
no tool call names a sender. Without it, writes that need an actor are refused with `actor_required`.
|
||||
**Results:**
|
||||
- A structured result comes beside the text from the queue tools, `rig_whoami`, `rig_team`, the four workspace tools,
|
||||
`rig_artifact_read`, `rig_human_decision_room` and `rig_restore_status`. The other tools answer in text.
|
||||
- Every failure carries the same structured shape: `ok: false`, a `code` and a `message`.
|
||||
|
||||
**Identity:**
|
||||
- **Over stdio, and over HTTP with no grants:** the server acts as one seat, the `OPENRIG_SESSION_NAME` in its
|
||||
environment. Every request carries that identity, so no tool call names a sender. Without it, writes that need an
|
||||
actor are refused with `actor_required`.
|
||||
- **Over HTTP with grants:** each caller acts as its own grant's seat.
|
||||
|
||||
### Over HTTP: an assistant that can't start a local process
|
||||
|
||||
- **No grants (no token file):**
|
||||
- the listener stays on loopback and acts as `OPENRIG_SESSION_NAME`, declared, like stdio;
|
||||
- a request whose `Host` isn't loopback is refused.
|
||||
- **With grants:** every request needs `Authorization: Bearer <token>`, loopback included, because a tunnel or proxy
|
||||
delivers remote traffic to loopback.
|
||||
- **Identity:** each caller acts as its grant's seat.
|
||||
- **What it reaches:** only its rigs' seats and work, and its roots' artifacts. Anything else answers `out_of_scope`.
|
||||
- **Bad credentials:** a missing, invalid or revoked token gets a 401, never the environment's seat.
|
||||
- **A broken token file:** the server reads the file on every request. While it is missing or can't be read, every
|
||||
request is refused (`auth_config_unreadable`); restore a valid, readable file and requests are served again, with
|
||||
no restart. The other way back is a restart with no token file, which returns the server to the no-grants mode
|
||||
above (loopback only). A restart never skips a token file that is present: its grants still apply.
|
||||
- **Beyond loopback** (`--bind`), the server starts only with a grant. TLS comes from the tunnel or proxy, not from
|
||||
this server.
|
||||
- **`rig mcp token add`** prints the token once and stores only its hash.
|
||||
- **`revoke`:** the caller's next request is refused, and work it already handed over stays with its owners.
|
||||
- **Grants, binds and tunnels are the person's decisions.** Give them the commands, and don't run them on their behalf.
|
||||
|
||||
### Join a team from an agent with no terminal of its own (desktop apps)
|
||||
|
||||
@@ -1496,13 +1696,15 @@ use the `rig` CLI instead, the same way.
|
||||
Note the `OPENRIG_SESSION_NAME` it prints.
|
||||
2. **Give the person the MCP entry to add.** Don't edit their app's settings yourself. The entry runs
|
||||
`rig mcp serve`, with `OPENRIG_SESSION_NAME=<that seat>` in its environment. Most MCP clients take it as a
|
||||
command, arguments and an `env` map.
|
||||
command, arguments and an `env` map. An app that can't start a local process connects to `rig mcp serve --http`
|
||||
instead, with a grant for that seat (see "Over HTTP" above).
|
||||
3. **Hand off work** with `rig_queue_create`, and keep the `qitemId` it returns.
|
||||
4. **Read the answer** with `rig_queue_show <qitemId>`. The item's state, closure and transition notes are where the
|
||||
assignee records it. A call that times out has an unknown outcome: check `rig_queue_show` with the same id
|
||||
before you retry, and retry only with that id.
|
||||
assignee records it, and `rig_artifact_read` reads an artifact it names. A call that times out has an unknown
|
||||
outcome: check `rig_queue_show` with the same id before you retry, and retry only with that id.
|
||||
5. **Take work** with `rig_queue_list` (`owned: true`), `rig_queue_claim` and `rig_queue_update`. Close with the
|
||||
answer in `note`.
|
||||
answer in `note`. To pass it to the next seat, use `rig_queue_handoff`; to put back one you won't finish, use
|
||||
`rig_queue_unclaim`.
|
||||
|
||||
Limits today:
|
||||
- **OpenRig can't wake this agent itself.** `rig send` to an attached external node is refused, and a task's nudge to
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"productRepository": "https://github.com/mvschwarz/openrig",
|
||||
"productCommit": "c99450f322646acd6bcd93e11a3bd1474f1d7751",
|
||||
"productCommit": "8b180689e7004b5506fb9f76802e65572c6f6622",
|
||||
"pluginPath": "packages/daemon/assets/plugins/openrig-core",
|
||||
"pluginVersion": "0.1.17",
|
||||
"pluginManifestSha256": "724c5061998abdd7ca42575628ccb526fafdeab7b27992084fffe0d1b2ecffde",
|
||||
"pluginVersion": "0.1.19",
|
||||
"pluginManifestSha256": "e7b5b404934415ae9299c8e3f0e135f60f70eec03d4fb3469bb0513d612fc29c",
|
||||
"piUnselectedSkills": [
|
||||
"claude-compaction-restore"
|
||||
],
|
||||
@@ -70,7 +70,7 @@
|
||||
"path": "skills/claude-compaction-restore/scripts/precompact-hook.mjs",
|
||||
"source": "packages/daemon/assets/plugins/openrig-core/skills/claude-compaction-restore/scripts/precompact-hook.mjs",
|
||||
"mode": "100755",
|
||||
"sha256": "478599ce28f4bdcd1f1e091c687726b598271d834288dcfd80bb2f9adfa2e37a"
|
||||
"sha256": "712e97c8beb02eb6c2815884a960778a225d8f6238dfe04a9b0547c34eaa131d"
|
||||
},
|
||||
{
|
||||
"path": "skills/claude-compaction-restore/scripts/restore-from-jsonl.mjs",
|
||||
@@ -94,7 +94,7 @@
|
||||
"path": "skills/delegating-work/SKILL.md",
|
||||
"source": "packages/daemon/assets/plugins/openrig-core/skills/delegating-work/SKILL.md",
|
||||
"mode": "100644",
|
||||
"sha256": "f40346ec08f0ba22d8dccab0dca8442a8d3783173275d36a4e913185e0e29fbd"
|
||||
"sha256": "fc5638949ce7600446a04e59dbe94996ea910e94beef1fa71cb7a31fa140fce6"
|
||||
},
|
||||
{
|
||||
"path": "skills/developing-openrig/SKILL.md",
|
||||
@@ -106,7 +106,7 @@
|
||||
"path": "skills/forming-an-openrig-mental-model/SKILL.md",
|
||||
"source": "packages/daemon/assets/plugins/openrig-core/skills/forming-an-openrig-mental-model/SKILL.md",
|
||||
"mode": "100644",
|
||||
"sha256": "68307c60a5d8d5d93cbd86e28399b73f336a7f13ebe6e30e9a7b22e818dc7022"
|
||||
"sha256": "cd7bfdcc5a87485140cfcac94bc2c703aec5c5b220a3397e139c4fd6db4339e6"
|
||||
},
|
||||
{
|
||||
"path": "skills/loading-addressable-markdown/SKILL.md",
|
||||
@@ -124,7 +124,7 @@
|
||||
"path": "skills/messaging-the-human/SKILL.md",
|
||||
"source": "packages/daemon/assets/plugins/openrig-core/skills/messaging-the-human/SKILL.md",
|
||||
"mode": "100644",
|
||||
"sha256": "d504477d1e7df04259d750187071ed96e0419f0e5df3358767d53c2ba409e84d"
|
||||
"sha256": "e94095e61cec0d32d96fb8c60df008d0c3ac3bd2fa789a5eb625f46f35945456"
|
||||
},
|
||||
{
|
||||
"path": "skills/mission-slice-sop/SKILL.md",
|
||||
@@ -208,7 +208,7 @@
|
||||
"path": "skills/openrig-user/SKILL.md",
|
||||
"source": "packages/daemon/assets/plugins/openrig-core/skills/openrig-user/SKILL.md",
|
||||
"mode": "100644",
|
||||
"sha256": "cf2fd3f2cf4d7c5182b63ae42e0c2eaf23fb6ddbbc61cef0b26d0d9536ca2476"
|
||||
"sha256": "01cb169050a3d4f51ad7b263772c6ed58f62683df820efeac1a88faadf9cd086"
|
||||
},
|
||||
{
|
||||
"path": "skills/orienting-to-an-inherited-seat/CHANGELOG.md",
|
||||
|
||||
+1
-1
@@ -265,7 +265,7 @@ try {
|
||||
|
||||
const customMessage = readClaudeCompactionMessage();
|
||||
|
||||
const result = spawnSync("node", args, { encoding: "utf8" });
|
||||
const result = spawnSync(process.execPath, args, { encoding: "utf8" });
|
||||
if (result.status !== 0) {
|
||||
const baseFailure = `Claude compaction restore packet generation failed: ${(result.stderr || result.stdout || "unknown error").trim()}. After compaction, load the claude-compaction-restore skill and run restore-from-jsonl manually.`;
|
||||
emit({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: delegating-work
|
||||
description: "Use when you have a task and must decide WHO does it — yourself, a spawned subagent, or another agent in the topology that already holds the context — or how to brief the agent you hand it to, which depends on the model it runs. The rule: how much would a blank slate have to read in to do this correctly? A lot → route to a context-holding agent; a little → subagent; unsure → a real agent or yourself. NOT the mechanics of fanning out (that's dispatching-parallel-agents / subagent-driven-development) — this is the who/whether decision, and the failure mode that makes teams abandon subagents."
|
||||
description: "Use when you have a task and must decide WHO does it — yourself, a spawned subagent, or another agent in the topology that already holds the context — or how to brief the agent you hand it to, which depends on the model it runs. The rule: how much would a blank slate have to read in to do this correctly? A lot → route to a context-holding agent; a little → subagent; unsure → a real agent or yourself. NOT the mechanics of fanning out (that's dispatching-parallel-agents / subagent-driven-development) — this is the who/whether decision, and the failure mode that makes teams abandon subagents. Also use it to decide how far to trust a delegated result."
|
||||
metadata:
|
||||
openrig:
|
||||
stage: field-captured
|
||||
|
||||
@@ -163,9 +163,12 @@ cost when you reach for one.
|
||||
### Where skills come from in OpenRig
|
||||
|
||||
- **Per-agent loadout:** your AgentSpec's `profile.uses.skills: [...]`
|
||||
determines what skills get projected into your runtime skill folder
|
||||
(`.claude/skills/` or `.agents/skills/`) before your harness boots. This
|
||||
is the **structural composition** layer.
|
||||
determines what skills get projected into your seat's own skill storage
|
||||
before your harness boots. A Claude seat gets them through OpenRig's
|
||||
generated plugin, named `openrig:<id>`; a Codex seat through its skill
|
||||
catalog. A seat without that storage gets them in the project's
|
||||
`.claude/skills/` or `.agents/skills/`. This is the **structural
|
||||
composition** layer.
|
||||
- **Cross-pod sharing:** AgentSpecs can `imports: [shared]` to access a
|
||||
shared skill pool. Built-in agents commonly do this.
|
||||
- **Belt-and-suspenders:** the spec projects skill files; startup guidance
|
||||
@@ -176,7 +179,8 @@ cost when you reach for one.
|
||||
|
||||
| Home | Purpose |
|
||||
|---|---|
|
||||
| `<rig-cwd>/.claude/skills/`, `<rig-cwd>/.agents/skills/` | Where the harness actually loads from. Populated by `rig up`. |
|
||||
| `<OpenRig home>/state/seats/<rig id>/<logical seat>/<runtime>/` | A team seat's own skills, outside the project. Claude loads them through the generated plugin (`plugin/skills/`); Codex reads them by path from the seat's `catalog.md` (`skills/`). Populated at launch. |
|
||||
| `<rig-cwd>/.claude/skills/`, `<rig-cwd>/.agents/skills/` | The project's own skills, which the harness also loads. A seat without its own storage gets its skills here. |
|
||||
| `~/.claude/skills/`, `~/.agents/skills/` | User-level harness skill directories. Inspect the current projection and harness configuration to determine which skills are installed and where they came from. |
|
||||
| `packages/daemon/{specs/agents/shared/skills,assets/plugins/*/skills}/` (source checkout) | Product skills that ship with OpenRig — the spec pool + the bundled plugin assets (openrig-user, openrig-architect, forming-an-openrig-mental-model, queue-handoff, claude-compaction-restore, …). |
|
||||
| the skills authoring workspace | Skill authoring source (not runtime-loaded). |
|
||||
@@ -239,9 +243,10 @@ If you're booting into a new seat in an OpenRig rig:
|
||||
`guidance/role.md` for your specific seat.
|
||||
3. **Read the rig's `CULTURE.md`** if it has one — the team operating
|
||||
manual.
|
||||
4. **Check what skills you have** — list `.claude/skills/` or
|
||||
`.agents/skills/` in your cwd. Each skill has a frontmatter description
|
||||
that tells you when to reach for it.
|
||||
4. **Check what skills you have** — `rig skill loadout --seat self` shows
|
||||
your seat's own skills; `.claude/skills/` or `.agents/skills/` in your
|
||||
cwd hold the project's. Each skill has a frontmatter description that
|
||||
tells you when to reach for it.
|
||||
5. **Check your peers** — `rig ps --nodes` and the queue first (who is up,
|
||||
who holds what). Capture a peer (`rig capture <peer-session>`) only when
|
||||
you actually need to see its screen.
|
||||
@@ -302,7 +307,7 @@ that fit your actual work.
|
||||
| "I should manage Codex's compaction the way I manage Claude's" | No. Codex auto-compacts cleanly; Claude doesn't. Different runtimes, different lifecycles. |
|
||||
| "MEMORY.md auto-loads, so I don't need to read it" | Maybe. Sometimes MEMORY.md auto-loads via system reminders; sometimes not. Don't assume. If your work touches the topics it covers, read it explicitly. |
|
||||
| "Skills inherit from a parent or compose like classes" | No. Skills are flat artifacts; composition happens via AgentSpec `profile.uses.skills` (structural) or soft cross-references in skill bodies (advisory). Not via OO-style inheritance. |
|
||||
| "The substrate `shared-docs/skills/` folder is the canonical runtime path" | No. The harness doesn't read there. It's an authoring workspace. Runtime loads from `.claude/skills/`, `.agents/skills/`, and product built-in. |
|
||||
| "The substrate `shared-docs/skills/` folder is the canonical runtime path" | No. The harness doesn't read there. It's an authoring workspace. Runtime loads from the seat's own skill storage, `.claude/skills/`, `.agents/skills/`, and product built-in. |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -71,6 +71,20 @@ rig queue create --destination <entityId>@external \
|
||||
--evidence-ref <durable-evidence> --verify --json
|
||||
```
|
||||
|
||||
An ask is on Steering and the TUI, where the person can answer it, choosing an
|
||||
option or typing a reply; it comes back to you exactly as a Slack answer does.
|
||||
Whether it also posts to Slack is the person's Slack subscription (`rig gateway
|
||||
human show <id>`, `slack-subscription`): by default (`every-ask`) every ask
|
||||
posts, and with `urgent-only` only an ask marked `--alert` does. So forgetting
|
||||
`--alert` never hides an ask from someone who reads Slack.
|
||||
|
||||
Add `--alert` when the ask is urgent: something only they can do, or an approval
|
||||
whose wait costs now. A sign-in, a credential or an approval that expires needs
|
||||
it; a question that can wait for their next look doesn't. An alerted ask stands
|
||||
out in Slack (an interrupt with a mention, within their quiet hours and
|
||||
availability), and it's the only kind that reaches someone on `urgent-only`.
|
||||
An update (`--human-intent update`) posts as before and takes no `--alert`.
|
||||
|
||||
Before asking a **decision**, check whether the person has a mode on with a
|
||||
decision room: what they said the team may decide for them meanwhile.
|
||||
|
||||
@@ -125,8 +139,8 @@ posts as a new message.
|
||||
A **decision** with a few clear choices can carry `--human-questions-file <path>`:
|
||||
a JSON array of 1–4 questions, each
|
||||
`{"id", "question", "options": [{"id", "label", "recommended"?}]}` with 2–4
|
||||
options (labels up to 75 characters, at most one recommended). Slack shows each
|
||||
question as a row of buttons. Each click records that answer on the item, and
|
||||
options (labels up to 75 characters, at most one recommended). The TUI lists
|
||||
each question with its options, and Slack (when alerted) shows each as a row of buttons. Each click records that answer on the item, and
|
||||
the decision resolves once every question has one. You then receive one reply
|
||||
row listing the answers. In `humanAnswers`, clicked answers are option-id strings.
|
||||
A whole typed reply is an object:
|
||||
@@ -148,8 +162,9 @@ well would issue another notification for the same decision.
|
||||
|
||||
The row persists before bounded delivery verification. Read its qitem ID and
|
||||
verification result; `posted` proves connector posting, **not human readership**.
|
||||
`transport-failed`, `never-posted`, or a pending/indeterminate result leaves the
|
||||
row intact. Inspect that same row and its next action; never create a second row
|
||||
`not-alerted` means the person's Slack subscription is `urgent-only`, so the ask is
|
||||
on Steering and the TUI without a Slack post, as they chose; it isn't a failure. `transport-failed`, `never-posted`, or a pending/indeterminate result
|
||||
leaves the row intact. Inspect that same row and its next action; never create a second row
|
||||
or blindly resend because verification timed out.
|
||||
|
||||
```bash
|
||||
|
||||
@@ -94,10 +94,23 @@ rig queue handoff <qitem-id> \
|
||||
|
||||
A queue write (`create`, `claim`, `update`, `handoff`, `handoff-and-complete`) answers with the row it wrote; a
|
||||
handoff answers with the closed row and its successor. When that answer shows what you need to establish, use it.
|
||||
Read back (`rig queue show <id> --full --json`, or `rig queue transitions <id>`) when it doesn't, for example:
|
||||
|
||||
- the outcome was INDETERMINATE (a transport failure, or a 502 `remote_queue_write_failed`), or the write warned
|
||||
`qitem_body_not_saved` (an existing row came back). Reconcile by the original ID before any retry;
|
||||
`create` also prints `Queue create request ID: <id> (not proof of persistence)` on stderr *before* it sends the write,
|
||||
so an interrupted create can be reconciled. That ID names a request, not a row. The row is the `qitemId` on stdout
|
||||
when the command exits 0. A non-zero exit means no row was confirmed, even when stdout carries a `qitemId`: an
|
||||
indeterminate answer repeats the request ID so you can reconcile by it. Don't merge stderr into stdout and keep the
|
||||
first `qitem-…` you find there; a refused create prints its request ID first, too.
|
||||
|
||||
Read back (`rig queue show <id> --full --json`, or `rig queue transitions <id>`) when the answer doesn't show what you
|
||||
need, for example:
|
||||
|
||||
- the outcome was INDETERMINATE (a transport failure, or a 502 `remote_queue_write_failed` that may have reached the
|
||||
other host), or the write warned `qitem_body_not_saved` (an existing row came back). Reconcile by the original ID
|
||||
before any retry. A 502 whose `failureClass` is `unknown-host`, `unsupported-transport` or `registry` isn't
|
||||
indeterminate: the daemon refused before contacting any host and wrote nothing (`create` says "No row was
|
||||
created"). `unsupported-transport` means that host entry is SSH-declared. Use the http entry for the same machine
|
||||
from `rig host list`; if there isn't one, add it with that machine's daemon URL (`rig host add --id <id>
|
||||
--transport http --url <daemon-url>`), asking whoever runs that host for the URL;
|
||||
- the output was compact or truncated and the field you need isn't in it (the compact receipt shows `bodyBytes`, not
|
||||
the body, and its footer names what it left out);
|
||||
- you need a transition note, a closure or blocker detail, or a successor that the output you saw omits;
|
||||
@@ -457,6 +470,16 @@ clear updates the current binding and identity verdict without a clear event.
|
||||
Acknowledgment or responsiveness alone does not prove that the original
|
||||
conversation resumed.
|
||||
|
||||
Two startup cases also end in `attention_required`, and the result says what to do:
|
||||
- **Assisted startup** (a community harness preset with `startup_assisted: true`): the startup text and Enter were
|
||||
sent but submission isn't confirmed. Check the pane and submit any staged text, don't resend it, then run the
|
||||
reported `rig seat clear-attention <session> --reason "..."` once the seat has responded.
|
||||
- **A refused startup turn** (Claude answered a startup turn with a refusal such as an expired login): nothing more is
|
||||
typed into the seat, and the result names how many later startup inputs weren't sent. A fresh start that ran no
|
||||
one-shot action stays continuable: `rig seat continue <seat>` delivers the whole startup again, the refused turn
|
||||
included. A resumed or forked seat keeps its conversation and isn't offered continue; a fresh start that can't
|
||||
continue names `rig seat launch <seat> --fresh --stop --reason "<why>"`.
|
||||
|
||||
A 422 response names the uncleared class and its failed check. If the recorded
|
||||
native token needs correction and you know the actual token, use
|
||||
`rig seat set-resume-token <session> --token-stdin --reason <explanation>`, then
|
||||
@@ -818,12 +841,15 @@ rig send <session> "message" --host <id>
|
||||
rig send <session> "message" --json
|
||||
```
|
||||
|
||||
**The send-guard (v0.4.0) — the default is SAFE.** A default `rig send` is guarded: it will NOT submit into an interactive prompt / permission block on the target pane. Flags:
|
||||
**The send-guard (v0.4.0) — the default is SAFE.** A default `rig send` is guarded: it will NOT submit into an interactive prompt / permission block on the target pane. That includes Claude's MCP server approval menu (`N new MCP servers found in .mcp.json`, ending `Space to select · Enter to confirm · Esc to reject all`) while it is the current screen: the send is refused with reason `target_needs_input` and `Refused: '<session>' is at an interactive prompt (selection_prompt). A message must not select or approve it. … No text was sent.` Approving or rejecting those servers is the person's call; `--dangerously-interact` drives it only deliberately. Flags:
|
||||
- `--verify` — delivery evidence.
|
||||
- `--force` — **a back-compat no-op on the send DECISION**: it never bypasses the interactive-prompt/permission guard and never changes whether a message is delivered (a mid-task/busy pane already sends-with-advisory by default). *(It does NOT "bypass activity-risk checks" — that earlier teaching is retired.)* It is **not fully inert**, though — it is still parsed solely to be **rejected in combination with `--wait-for-idle`**: `rig send … --force --wait-for-idle <n>` prints `--wait-for-idle cannot be combined with --force`, exits 1, and sends nothing. So do not read "no-op" as "`--force --wait-for-idle` is harmless"; that pairing errors. *(Verified against current product main `d37a08ad`: the guard-bypass no-op is declared at `send.ts` and confirmed by runtime capture — a plain `--force` send delivers through the ordinary path; the `--wait-for-idle` rejection is enforced at `send.ts`, `routes/transport.ts`, and `session-transport.ts`, and confirmed by runtime capture — exit 1, nothing sent.)*
|
||||
- `--wait-for-idle <seconds>` — wait until the target is explicitly idle before sending. **Cannot be combined with `--force`** (that pairing is rejected: exit 1, nothing sent).
|
||||
- `--raw` — send exact text/keystrokes without the From/To messaging envelope (still guarded against interactive prompts).
|
||||
- `--dangerously-interact --reason "<why>"` — the ONLY override of the prompt/permission guard: deliberately drive an interactive prompt/permission block (implies `--raw`, requires `--reason`, audit-logged).
|
||||
On a guard-attached managed seat, a non-menu override stops and shows existing typed/pasted input (author unknown) with `override_input_present`; unreadable input stops with `override_input_unknown`. Recognized dim suggestions are shown separately and count as empty; a collapsed paste shows only its label, not hidden contents.
|
||||
Inspect the input, deliberately clear the native prompt with native/tmux controls, then retry. Manual clearing is outside OpenRig's lease and observation checks and is not recorded; human typing can still race a read and paste.
|
||||
When you deliberately need to type without that composer check (for example an unsupported input you inspected yourself), add `--type-without-checking` with `--dangerously-interact --reason "<why>"`. It does not clear input and may join or submit existing text. Ordinary sends and wakes keep their current rules.
|
||||
- `--host <id>` — send on a remote host declared in `~/.openrig/hosts.yaml` (ssh hosts shell out; http hosts go CLI-direct to the remote daemon).
|
||||
- `--from <session>` — deprecated and ignored; it does not select the sender. Sender identity comes from the current seat and transport provenance; cross-host envelopes use the durable local origin, not the supplied flag or a relay identity.
|
||||
- `--context <ref>` **(0.5.0)** — attach a composed context pack/piece by ref (see "Context packs and paced delivery"). Small piece → `send --context`; a real pack → `rig walk`. The noun `rig context` composes the ref; the verb delivers it.
|
||||
@@ -971,6 +997,68 @@ rig auth seats … --runtime codex # seat -> profile registry (metadata
|
||||
|
||||
**Hard secret boundary:** no token value is ever printed, logged, queued, streamed, or committed; status/validate report presence/mode/login-state only; seat labels are metadata, not live-account proof. MVP is `--runtime codex`; other runtimes use the same surface with a different `--runtime`, never a parallel command.
|
||||
|
||||
### Give a seat its own Claude or Codex account
|
||||
|
||||
A runtime's login lives in its home: Claude Code's in its config dir (`CLAUDE_CONFIG_DIR`, default `~/.claude`), Codex's
|
||||
in `CODEX_HOME` (default `~/.codex`). A seat that names its own home in `env:` runs on that home's login.
|
||||
|
||||
1. **Prepare the home once, natively,** in a terminal: `CLAUDE_CONFIG_DIR=/abs/path claude`, then `/login`; or
|
||||
`CODEX_HOME=/abs/path codex login`. The person signs in; never copy credential files between homes.
|
||||
2. **Name it in the member's `env:`** in `rig.yaml`, not with `rig up --env`, which leaves these two out
|
||||
(`CLAUDE_CONFIG_DIR: /abs/path` or `CODEX_HOME: /abs/path`), or in
|
||||
the rig's `env:` for every seat of that runtime. The member's value wins. It must be an absolute path: any other
|
||||
value is reported and ignored, and the seat keeps the daemon's home.
|
||||
3. **It takes effect at the seat's next launch** (a fresh launch, restore or handover); a running seat keeps the home
|
||||
it started with. A conversation saved under the old home doesn't resume from the new one.
|
||||
4. **Check it:** `rig seat status <seat>` shows the home and where it came from, and `rig provider status` lists it as
|
||||
the seat's configured account. Both say what's configured, not which login a running session holds.
|
||||
|
||||
Seats may share a home. OpenRig writes its own settings (Codex hooks, managed `config.toml` and plugin; Claude's
|
||||
first-run trust settings) into the seat's home.
|
||||
|
||||
### Move one Codex seat to another login
|
||||
|
||||
For a Codex seat on Linux. The login must already be saved in the Codex home the seat runs on (`rig seat status <seat>`
|
||||
shows it). Other operating systems, and moving a seat to another home, aren't supported by this command yet: say so instead
|
||||
of improvising. A Claude seat moves differently; see the next section.
|
||||
|
||||
1. **Save each login once, in that home:** `CODEX_HOME=<home> codex login` (the person signs in), then
|
||||
`CODEX_HOME=<home> rig auth save <name>`. `rig auth list --codex-home <home>` lists them.
|
||||
2. **Check:** `rig provider precheck --seat <seat> --to-account <name>` shows the home, what the seat is doing, and every
|
||||
peer on that home. A refusal says what's missing, and nothing changes.
|
||||
3. **Move:** `rig provider switch --seat <seat> --to-account <name>`. It saves the home's current login as
|
||||
`before-switch-<time>`, activates the chosen one, and restarts the seat in place on the same conversation. If the
|
||||
current login can't be saved first, nothing changes. Peers on the home aren't restarted. Each keeps its current login
|
||||
only until its next token refresh, when Codex stops it with "signed in to another account". Run the printed
|
||||
`rig seat restart <peer> --keep-conversation --reason "..."` for each to move it too. Don't sign in again in that home: that replaces
|
||||
the login you just switched to.
|
||||
4. **Confirm:** after the seat completes a real tool-using turn, run the printed
|
||||
`rig seat restart <seat> --keep-conversation --verify --expect-account <its Account line>`. Until then the move is
|
||||
unconfirmed. An uncertain restart is reported; never restart it a second time.
|
||||
|
||||
### Move one Claude seat to its own subscription token
|
||||
|
||||
The seat keeps its config dir and gets its own `CLAUDE_CODE_OAUTH_TOKEN`, read from a file as Claude starts. OpenRig keeps only
|
||||
the file's path and your label, never the token.
|
||||
|
||||
1. **Prepare the token once:** the person runs `claude setup-token`, which prints a one-year subscription token, and saves it to a
|
||||
file only their user can read (`chmod 600`). Never paste the token into a message, a row or a command line.
|
||||
2. **Check:** `rig provider precheck --seat <seat> --to-account <label> --token-file <absolute path>`. It refuses, changing
|
||||
nothing, if the file wouldn't apply, the seat isn't running, or something Claude Code ranks above the token is set (a cloud
|
||||
provider, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_API_KEY`, an `apiKeyHelper`). It names each one and how to remove it for that
|
||||
seat. When the token applies, the launch unsets those names for Claude's process, so the pane shell's own exports can't
|
||||
outrank it. Managed settings can't be read; the seat's `/status` shows what it actually uses.
|
||||
3. **Move:** `rig provider switch --seat <seat> --to-account <label> --token-file <absolute path>`. It takes a snapshot that
|
||||
resumes the seat's conversation, sets the reference, and relaunches the seat from that snapshot in its own pane: the pane
|
||||
and its scrollback stay, but work in flight when Claude ended doesn't carry over. Seats sharing the config dir keep their
|
||||
own login. `--stored-login` moves the seat back; if the seat still receives a `CLAUDE_CODE_OAUTH_TOKEN`, Claude uses that
|
||||
instead of the stored login, and the report names it.
|
||||
4. **Confirm:** after the seat completes a real tool-using turn, its `/status` shows token auth, with no account email. The
|
||||
label is yours, not an account Claude observed: say so. A token session can't use claude.ai connectors or Remote Control.
|
||||
The token is in Claude's environment, so the tools, hooks and MCP servers it starts inherit it; don't have it print its
|
||||
environment. A relaunch that didn't resume reads unknown, with its recovery command; never run the switch again for it.
|
||||
`rig seat status <seat>` shows the reference, and says when its file won't apply at the next launch.
|
||||
|
||||
### Manage subscriptions across a fleet (when the person asks you to)
|
||||
|
||||
**This applies only when the person has asked you to manage their subscriptions.** Otherwise, don't take this job on. Installing or upgrading OpenRig appoints no agent, launches no bundle and starts no
|
||||
@@ -981,8 +1069,8 @@ to keep work running, through the Fleet app. Until that bundle is available, the
|
||||
subscriptions can use this section. If that's you, you decide which subscription or home moves, and when. You decide
|
||||
from:
|
||||
- the usage and assignment readings, and their freshness;
|
||||
- your earlier move reports;
|
||||
- the person's standing preferences.
|
||||
- your earlier move reports (`rig provider moves`);
|
||||
- the person's account policy, or their standing preferences when there's no policy.
|
||||
|
||||
You say why, and you see the outcome through. OpenRig gives you the facts and the tools; it never decides for you,
|
||||
and there's no threshold to obey.
|
||||
@@ -996,45 +1084,78 @@ and there's no threshold to obey.
|
||||
- **The Observatory is optional:** it can show readings, but you don't need it to rotate.
|
||||
|
||||
**The unit is a Codex home, with every seat on it.** Moving some seats on a shared home to a different account, and
|
||||
moving Claude seats, aren't supported moves yet: say so instead of improvising.
|
||||
moving Claude seats, aren't supported moves yet: say so instead of improvising. A Codex seat with a home of its own
|
||||
moves alone, with `rig provider switch --seat` (above).
|
||||
|
||||
0. **Load the person's account policy.**
|
||||
- It's the context pack named `account-policy`: check `rig context list` for it, then read it with
|
||||
`rig context get account-policy`. Its manifest's `version` is the version you follow and report.
|
||||
- Follow it, and record the version in each report. A line the person approved ("move the build box's seats without
|
||||
asking", "never move the reviewers") is standing authorization: act on it, don't ask again.
|
||||
- A line grants only what the person actually gave. "Agents may do sign-ins" is usable only where a sign-in route
|
||||
and access already exist; otherwise ask, and record that the preference couldn't be used.
|
||||
- Something the policy doesn't cover: use your judgment or ask the way the policy says, record why, and propose a
|
||||
one-line addition.
|
||||
- **No policy** is never a reason to stop: work from this section, report the policy as `{ "none": true }`, and
|
||||
offer once to write one with them (below).
|
||||
1. **Know the subscriptions.**
|
||||
- Each subscription is reached through a saved profile on a home (`rig auth list` and `rig auth save`) on each
|
||||
machine. Which seats use which profile is `rig auth seats`, and that's metadata: not proof of a live account.
|
||||
- Check you can read each subscription's usage, and name any you can't.
|
||||
2. **Read before deciding.**
|
||||
- Use `rig provider status --all-hosts --json` (add `--refresh` for fresh native readings). Each allowance has
|
||||
its windows, reset times and when it was observed.
|
||||
its windows, reset times and when it was observed, and when it was last observed exhausted.
|
||||
- **One allowance seen on two machines is one allowance:** never add its headroom twice.
|
||||
- **A stale reading** is usable only with its age recorded, or after a refresh.
|
||||
- **An unknown or unavailable reading is never room.**
|
||||
- **An unknown or unavailable reading is never observed room.**
|
||||
- **After a known reset,** a window carries an `expectation` (expected full since that reset). You may plan on it,
|
||||
but it isn't measured: report it as expected, and take a fresh reading when you can.
|
||||
- **Equal reset times don't mean the same account.**
|
||||
- **A seat's link to a subscription is observed, configured or unknown.** Only an observed link shows the seat
|
||||
actually uses it; a configured one (a profile, a registry row) is a claim.
|
||||
- **A sign-in state of `unknown` isn't `needs_sign_in`.** An otherwise authorized move can go ahead, and its uptake
|
||||
evidence settles it. A known `needs_sign_in` is step 6.
|
||||
3. **Decide, and leave seats alone for a reason.**
|
||||
- **Balance, don't chase:** spread machines across the subscriptions with room rather than moving every machine onto
|
||||
the one that just reset. `rig usage top` on each machine shows which work hardest: a high-use machine is worth a
|
||||
subscription of its own, and lower-use machines can share one. Keep a backup where the policy says so.
|
||||
- `rig seat restart <seat> --keep-conversation --plan` shows what a restart keeps, and every peer on the same
|
||||
home. Moving the home moves them all.
|
||||
- Skip a seat that's mid-turn, running a service, or about to finish, and record why.
|
||||
- The person's standing preferences ("never move the reviewers") are yours to follow and record.
|
||||
4. **Move the home.**
|
||||
- On that machine, `CODEX_HOME=<that home> rig auth switch <profile> --runtime codex`. `rig auth` acts on
|
||||
`CODEX_HOME`, or on the default home, and `rig auth status` prints which home it means: check it before
|
||||
switching.
|
||||
- Then restart each chosen seat: `rig seat restart <seat> --keep-conversation --reason "<why>"`.
|
||||
- `rig auth switch <profile> --runtime codex --codex-home <that home>`, on that machine, or from here with
|
||||
`--host <that machine's SSH entry>`. Without `--codex-home`, `rig auth` acts on `CODEX_HOME`, or on the default
|
||||
home; `rig auth status --codex-home <home>` (with `--host` for another machine) prints which home it means:
|
||||
check it before switching. A remote switch always names its home, and copies a profile already saved there.
|
||||
- **Another machine** is reached through its SSH entry in `rig host list`. An entry registered only over HTTP
|
||||
can't run these commands; use the machine's SSH entry, or run them on that machine.
|
||||
- Then restart each chosen seat: `rig seat restart <seat> --keep-conversation --reason "<why>"`, with `--host <id>`
|
||||
for a seat on another machine (the restart runs there, and its audit names you). If the connection drops, the
|
||||
answer is `remote-outcome-unknown`: the switch or restart may have run there, so read what it names (for a
|
||||
restart, `--plan`'s `lastRestart`) before anything else.
|
||||
- After it completes a real tool-using turn, check it: `rig seat restart <seat> --keep-conversation --verify
|
||||
--expect-account <the target's Account line>`. `--verify` only checks; it never restarts. Only that check
|
||||
reports `moved`, and anything less is unconfirmed.
|
||||
- **A slow reply isn't a failure:** never restart a seat a second time. Follow up at your next look.
|
||||
5. **Report every move, partial or unconfirmed included,** to whoever asked for it (a queue item to their seat):
|
||||
- the home and machine;
|
||||
- from and to, as labels;
|
||||
- your reason;
|
||||
- each reading you acted on, with its observation time;
|
||||
- each seat's result (`moved`, `unconfirmed` or `unknown`) with its evidence or what's missing;
|
||||
5. **Report every move, partial or unconfirmed included,** with
|
||||
`rig provider moves report --file <move.json> --to <whoever asked>` (`--help` shows the shape). It becomes a queue
|
||||
item to them; the Fleet app reads these reports once its page is connected to them. It holds:
|
||||
- the home and machine, and from and to, as labels;
|
||||
- your reason, and the policy version you followed (or `{ "none": true }`);
|
||||
- each reading you acted on, `observed` with its observation time, `expected` with the reset it rests on, or
|
||||
`unavailable`;
|
||||
- each seat's result: `moved` only with the verify check's matched account and observed tool turn, kept as separate
|
||||
facts; otherwise `unconfirmed` or `unknown`, with what's missing;
|
||||
- the peers, for example "not restarted; account unverified".
|
||||
|
||||
A home switched with every seat left as a peer is still a move to report. A follow-up that confirms an earlier
|
||||
unconfirmed seat says which report it follows. Never put credentials in a report.
|
||||
unconfirmed seat is a new report with `--follows <the earlier report's qitemId>`. A report to a person's seat names
|
||||
its evidence with `--evidence-ref` (for example, the saved `--verify --json` output). Never put credentials in a
|
||||
report: a value in a recognized credential format is refused. **A refused report changes nothing about the move:**
|
||||
correct it and file it again; never repeat the switch or a restart because a report was refused. Asked "why did
|
||||
these seats move?", even after your own restart, answer from `rig provider moves --seat <seat>`. It reads the newest
|
||||
reports (50 by default, `--limit` up to 200) and then filters them, so it's a recent window, not a seat's whole
|
||||
history: raise `--limit` when you need older ones.
|
||||
6. **When a sign-in is needed:**
|
||||
- Ask the person once, durably: a queue item to their seat, naming the machine and the subscription.
|
||||
- Move nothing onto that subscription until it's done.
|
||||
@@ -1045,6 +1166,41 @@ moving Claude seats, aren't supported moves yet: say so instead of improvising.
|
||||
**What wakes you:** a watchdog, a usage-limit park on the queue, or the person asking. How often you look is your
|
||||
judgment within those.
|
||||
|
||||
**If there's no policy yet.** Start from what you can see, and ask only what you can't; keep it to a few minutes, and
|
||||
never run it as a form. "Whatever you think" or "skip" is an answer: write down the default you'll use.
|
||||
1. **Look first:** `rig auth list` and `rig auth seats` on each machine you can reach, `rig provider status
|
||||
--all-hosts --json`, and `rig usage top` on each machine. Draft what you can.
|
||||
2. **Confirm what you saw,** in one message ("three Codex subscriptions and one Claude one, on two machines; the build
|
||||
box uses about four times the tokens of the laptop; any I can't see?").
|
||||
3. **Ask how they want them used,** offering the common shape: the busiest machine gets its own subscription, the
|
||||
rest share, and one stays spare.
|
||||
4. **Ask what you may do on your own:** move a machine's Codex seats without asking; machines or seats to ask about
|
||||
first or never move; sign-ins (may agents, and through which browser or profile, or always ask). Say what's
|
||||
supported today: moves are by Codex home, Claude moves aren't supported yet, and a sign-in preference is used only
|
||||
where a sign-in route and access exist.
|
||||
5. **Ask how to reach them** (their seat's queue, a chat channel, the Fleet app), for what, and what to do when they
|
||||
don't answer. A channel they name is a preference: check the route with `messaging-the-human`, and use a queue
|
||||
item to their seat if it isn't ready.
|
||||
6. **Ask what to do when things go wrong:** nothing has room, a sign-in is needed while they're away, a move didn't
|
||||
confirm.
|
||||
7. **Show the draft in plain words,** let them correct it, and save it as a context pack named `account-policy` in
|
||||
the context library where you run (`rig config get context.root`): a folder with `manifest.yaml` (`version: "1"`,
|
||||
`name: account-policy`, `taxonomy: world`, a `purpose`, and `files: [{ path: POLICY.md, role: policy }]`) and
|
||||
`POLICY.md`, installed with `rig context add <folder>` and read back with `rig context get account-policy`. To share
|
||||
one policy across machines, use a Git repository: `rig context add <repo> --git`. Labels and references only:
|
||||
never tokens, passwords, cookies or recovery codes.
|
||||
|
||||
`POLICY.md` has these parts, in their words: subscriptions (label, provider, plan, how each is reached); how they
|
||||
want them used; what agents may do on their own; how to reach them; when things go wrong; and a dated **Changes**
|
||||
list. Fill it from what you saw and what they said, never from another person's setup.
|
||||
|
||||
**Changing the policy.** When they say what changed, copy `POLICY.md` aside as the before copy, edit it, add a dated
|
||||
line under **Changes**, and raise `version` in `manifest.yaml`. Keep one place where it's edited: edited where it's
|
||||
installed, run `rig context sync`; kept in a separate folder, replace it with `rig context rm account-policy` and
|
||||
`rig context add <folder>`; Git-backed, edit and commit in the checkout `rig context source inspect account-policy`
|
||||
reports, then `rig context source update account-policy` (it never pushes). Read it back with
|
||||
`rig context get account-policy`; your next decision follows the new version.
|
||||
|
||||
## Context packs and paced delivery (0.5.0)
|
||||
|
||||
**Compose context once, hand it to a seat cleanly.** A library primitive plus a set of delivery flags. The rule that keeps the grammar coherent — internalize this one: **the noun stores and composes; the verbs deliver.** `rig context` never sends anything; delivery is only ever `rig send` / `rig broadcast` / `rig walk` / `rig queue`.
|
||||
@@ -1289,6 +1445,8 @@ Notes:
|
||||
- for tmux-backed self-attach, `rig whoami --json` is the right verification
|
||||
- for raw/external self-attach, `rig ps --nodes --json` is currently the more reliable verification surface
|
||||
- if the current shell is outside tmux, pass `--display-name <name>` when you want a stable human session label recorded
|
||||
- to read a seat's identity or state later, use `rig whoami` or `rig seat status`, not `rig attach --json` output or a file holding a
|
||||
seat's environment. Don't print `OPENRIG_ACTIVITY_*` values: the activity token is a credential, and it doesn't identify the seat
|
||||
|
||||
### Adopt a topology and bind live sessions
|
||||
|
||||
@@ -1459,32 +1617,74 @@ rig specs rename <name-or-id> <new-name> [--json]
|
||||
## MCP
|
||||
|
||||
```bash
|
||||
rig mcp serve [--port <port>]
|
||||
rig mcp serve [--port <daemon-port>] # stdio: a local app starts the server itself
|
||||
rig mcp serve --http [--bind <host:port>] # Streamable HTTP at /mcp (default 127.0.0.1:7437)
|
||||
rig mcp token add <name> --seat <session> [--rig <name>...] [--root <name>...] [--workspace] [--owner]
|
||||
rig mcp token list [--json]
|
||||
rig mcp token revoke <name>
|
||||
```
|
||||
|
||||
Current shipped MCP tools:
|
||||
- `rig_up`
|
||||
- `rig_down`
|
||||
- `rig_ps`
|
||||
- `rig_status`
|
||||
- `rig_snapshot_create`
|
||||
- `rig_snapshot_list`
|
||||
- `rig_restore`
|
||||
- `rig_discover`
|
||||
- `rig_bind`
|
||||
- `rig_bundle_inspect`
|
||||
- `rig_agent_validate`
|
||||
- `rig_rig_validate`
|
||||
- `rig_rig_nodes`
|
||||
- `rig_send`
|
||||
- `rig_capture`
|
||||
- `rig_chatroom_send`
|
||||
- `rig_chatroom_watch`
|
||||
- `rig_add`
|
||||
- `rig_queue_create`, `rig_queue_show`, `rig_queue_list`, `rig_queue_claim`, `rig_queue_update`
|
||||
The 36 shipped tools fall into three groups. Over stdio, and over HTTP with no grants, every tool is offered. An HTTP
|
||||
grant offers the work tools; the workspace tools with `--workspace` or `--owner`; and the owner tools only with
|
||||
`--owner`, which offers every tool.
|
||||
- **Work (19):** orientation, communication and the durable work loop.
|
||||
- `rig_whoami`: the seat the server acts as (rig, pod, peers, edges) and the `caller`: authenticated (an HTTP
|
||||
credential, with its principal) or declared (`OPENRIG_SESSION_NAME` where the server was configured, which nothing
|
||||
verifies).
|
||||
- `rig_status`, `rig_ps`, `rig_rig_nodes`, and `rig_team`: a rig's lead (its one `delegates_to` root, the seat to
|
||||
delegate to) and each seat's role, delegation and activity.
|
||||
- `rig_send`, `rig_capture`, `rig_chatroom_send`, `rig_chatroom_watch`.
|
||||
- `rig_queue_create`, `rig_queue_show`, `rig_queue_list`, `rig_queue_claim`, `rig_queue_update`,
|
||||
`rig_queue_handoff`, `rig_queue_unclaim`:
|
||||
- `revises` on create asks for a linked revision of an earlier item;
|
||||
- show names a handed-off item's successor;
|
||||
- `handedOffFrom` on list follows a handoff;
|
||||
- handoff passes an item this seat holds on to the next seat in one step, as `rig queue handoff` does: the new item
|
||||
carries the work's lineage;
|
||||
- unclaim puts back a claimed item this seat won't finish.
|
||||
- `rig_artifact_read`: a finished item's artifact (its `evidenceRef`) or a named file, through the daemon's shared
|
||||
roots (`OPENRIG_FILES_ALLOWLIST`), in windows (`offset`, `nextOffset`). Outside those roots it answers unavailable
|
||||
rather than reading a local path.
|
||||
- `rig_human_decision_room`, then `rig_queue_decide`: on your own pending ask to a person, read the decision room of
|
||||
the mode they have on, and record a decision you made for them inside it. No tool writes a person's own answer.
|
||||
- **Workspace (4):** `rig_slices`, `rig_mission`, `rig_context_pack`, and `rig_context_get` (one context file or
|
||||
section, as `rig context get` reads it).
|
||||
- **Owner (13):** `rig_up`, `rig_down`, `rig_snapshot_create`, `rig_snapshot_list`, `rig_discover`, `rig_bind`,
|
||||
`rig_add`, `rig_bundle_inspect`, `rig_agent_validate`, `rig_rig_validate`, `rig_telemetry_events` (one bounded page
|
||||
of event metadata), and:
|
||||
- `rig_restore`, which starts a restore and returns its attempt;
|
||||
- `rig_restore_status`, that attempt's receipt.
|
||||
|
||||
The server acts as one seat: the `OPENRIG_SESSION_NAME` in its environment. Every request carries that identity, so
|
||||
no tool call names a sender. Without it, writes that need an actor are refused with `actor_required`.
|
||||
**Results:**
|
||||
- A structured result comes beside the text from the queue tools, `rig_whoami`, `rig_team`, the four workspace tools,
|
||||
`rig_artifact_read`, `rig_human_decision_room` and `rig_restore_status`. The other tools answer in text.
|
||||
- Every failure carries the same structured shape: `ok: false`, a `code` and a `message`.
|
||||
|
||||
**Identity:**
|
||||
- **Over stdio, and over HTTP with no grants:** the server acts as one seat, the `OPENRIG_SESSION_NAME` in its
|
||||
environment. Every request carries that identity, so no tool call names a sender. Without it, writes that need an
|
||||
actor are refused with `actor_required`.
|
||||
- **Over HTTP with grants:** each caller acts as its own grant's seat.
|
||||
|
||||
### Over HTTP: an assistant that can't start a local process
|
||||
|
||||
- **No grants (no token file):**
|
||||
- the listener stays on loopback and acts as `OPENRIG_SESSION_NAME`, declared, like stdio;
|
||||
- a request whose `Host` isn't loopback is refused.
|
||||
- **With grants:** every request needs `Authorization: Bearer <token>`, loopback included, because a tunnel or proxy
|
||||
delivers remote traffic to loopback.
|
||||
- **Identity:** each caller acts as its grant's seat.
|
||||
- **What it reaches:** only its rigs' seats and work, and its roots' artifacts. Anything else answers `out_of_scope`.
|
||||
- **Bad credentials:** a missing, invalid or revoked token gets a 401, never the environment's seat.
|
||||
- **A broken token file:** the server reads the file on every request. While it is missing or can't be read, every
|
||||
request is refused (`auth_config_unreadable`); restore a valid, readable file and requests are served again, with
|
||||
no restart. The other way back is a restart with no token file, which returns the server to the no-grants mode
|
||||
above (loopback only). A restart never skips a token file that is present: its grants still apply.
|
||||
- **Beyond loopback** (`--bind`), the server starts only with a grant. TLS comes from the tunnel or proxy, not from
|
||||
this server.
|
||||
- **`rig mcp token add`** prints the token once and stores only its hash.
|
||||
- **`revoke`:** the caller's next request is refused, and work it already handed over stays with its owners.
|
||||
- **Grants, binds and tunnels are the person's decisions.** Give them the commands, and don't run them on their behalf.
|
||||
|
||||
### Join a team from an agent with no terminal of its own (desktop apps)
|
||||
|
||||
@@ -1496,13 +1696,15 @@ use the `rig` CLI instead, the same way.
|
||||
Note the `OPENRIG_SESSION_NAME` it prints.
|
||||
2. **Give the person the MCP entry to add.** Don't edit their app's settings yourself. The entry runs
|
||||
`rig mcp serve`, with `OPENRIG_SESSION_NAME=<that seat>` in its environment. Most MCP clients take it as a
|
||||
command, arguments and an `env` map.
|
||||
command, arguments and an `env` map. An app that can't start a local process connects to `rig mcp serve --http`
|
||||
instead, with a grant for that seat (see "Over HTTP" above).
|
||||
3. **Hand off work** with `rig_queue_create`, and keep the `qitemId` it returns.
|
||||
4. **Read the answer** with `rig_queue_show <qitemId>`. The item's state, closure and transition notes are where the
|
||||
assignee records it. A call that times out has an unknown outcome: check `rig_queue_show` with the same id
|
||||
before you retry, and retry only with that id.
|
||||
assignee records it, and `rig_artifact_read` reads an artifact it names. A call that times out has an unknown
|
||||
outcome: check `rig_queue_show` with the same id before you retry, and retry only with that id.
|
||||
5. **Take work** with `rig_queue_list` (`owned: true`), `rig_queue_claim` and `rig_queue_update`. Close with the
|
||||
answer in `note`.
|
||||
answer in `note`. To pass it to the next seat, use `rig_queue_handoff`; to put back one you won't finish, use
|
||||
`rig_queue_unclaim`.
|
||||
|
||||
Limits today:
|
||||
- **OpenRig can't wake this agent itself.** `rig send` to an attached external node is refused, and a task's nudge to
|
||||
|
||||
Reference in new issue
Block a user