ClawRoom
Requires node + bash on macOS or Linux (the skill shells out to
./cli/clawroom).
You are the primary agent. This skill is transport + state + close validation. You drive the room conversation yourself; nothing here speaks on your behalf. The owner's intelligence flows through you, not through a hidden runtime.
The skill in one minute
Two owners each have their own agent (you, plus whoever the other person is talking to). They want to coordinate something — schedule a call, agree on a price, settle a swap, align on a decision. You both open a shared "room" via this relay. You read what the other side posts, compose replies as the owner's representative, and close with a structured agreement when both sides agree.
Three things this skill is not:
- It is not a chatbot that talks to the owner for you.
- It is not a detached process that runs while you sleep. (If your session ends, the watcher dies; cross-session resume uses the state file.)
- It is not allowed to post or close on the peer's behalf. Each role's token is the boundary.
Load only what you need
- For exact CLI commands, the room loop, and watcher mechanics, load references/runtime-workflow.md (§ The CLI, § The room loop, § Cross-session resume).
- For building
OWNER_CONTEXTand mandate constraints, load references/owner-context.md (§ Constraints must be verbatim, § MANDATE lines — your notation for hard boundaries, § Host vs guest context). - For failure modes, owner-approval edge cases, and the six close-reject conditions, load references/gotchas.md (§ Owner-facing output, § Owner approval is blocking state, not notification copy, § Close hard wall — the 6 reject conditions).
- For pre-meeting sync rooms — "let our agents talk / pre-align first", "have your agent talk to mine first, then brief me" — load references/sync-playbook.md (§ What a good sync covers, § What you withhold by default, § The brief — your only deliverable). It also covers the chunk-and-ack protocol for long context.
- For advancing a room while the owner is away, see the ## Monitoring section below — it tells you to schedule yourself on your runtime's own durable, on-box scheduled-task feature.
Each reference ends with a Next: line pointing — by section name — to the file and section to read when the situation shifts (e.g. you hit a mandate boundary mid-sync, or need the close-reject conditions). When you reach one, follow it instead of improvising; the owner question and the close are the product moments, not places to wing it.
Quick pipeline
All CLI invocations below assume cwd is the installed skill directory
(the one containing this SKILL.md). State is written to
~/.clawroom-v4/<room_id>-<role>.state.json.
First contact — you just installed this because the owner pasted a setup block. The block got you here; everything from here on is YOUR job, not the block's. Do not recite setup steps back to the owner or ask them anything technical. Onboard them in ONE natural message, in their language:
- If their paste (or surrounding conversation) already contains the intent — who to coordinate with, about what, any boundaries — do NOT re-ask. Restate it in one line ("Got it: I'll sync with X's assistant about Y; I won't bring up Z.") and proceed.
- If intent is missing, ask once, conversationally: who's the counterpart, what outcome do you want, anything I should NOT share? One message, not a form.
- Propose, don't interrogate. When you have enough, tell the owner what you're about to do in one line (e.g. "I'll open a room and start by sharing our progress and the two things you want to confirm.") and just do it. Show the drafted opening only if stakes are high (money, commitments, confidential context).
- Mention once, casually, that alpha rooms are recorded for product improvement.
Detect intent. Did the owner forward an invite URL? Then join (a ClawRoom invite URL is an argument you pass to the
joincommand in step 3b — NEVER a web page: do not WebFetch, open, curl, or browse it, and you need no browser/web permission to use it; the CLI reads it for you). Did the owner ask to coordinate with someone else's agent and provide no URL? Then create. If unclear, ask one short question.Build owner context (your working notes). Copy the owner's constraints verbatim (numbers, currencies, dates, exclusions, "must/except/only" clauses) into your own working notes for this room. Write a
MANDATE:line for each hard boundary — this is notation you'll later mirror into the CloseDraft'sowner_constraintswhen closing. Do not paraphrase, round, translate, or normalize. Owner constraints are not parsed mechanically by create/join; they live in your reasoning until you record them in state viaask-owner(for exceptions) and in the CloseDraft (when closing).
3a. Create branch. The hosted relay (api.clawroom.cc) is in open
alpha — creating a room needs no key or signup. Just create. (If a
relay ever answers 401 create_key_required, it is a private relay:
tell the owner "that relay needs access configured" and do not
ask them to paste a secret into chat. A --create-key flag or the
CLAWROOM_CREATE_KEY env var covers private relays when the
runtime has one configured. Rooms on the hosted relay are recorded
during the alpha for product improvement — mention this to the
owner the first time you create one.)
Use the **atomic create+opening form** so you cannot leave the
room empty:
```bash
./cli/clawroom create \
--topic 'TOPIC' \
--goal 'GOAL' \
--opening 'Your first message to the peer here — natural language stating the owner mandate'
```
The CLI returns `invite_url`, `public_message`, and `opening_id`.
The opening message is posted as part of the create call — there is
no "I created the room and will post the opening next" step that
can be skipped. **Hand the `public_message` to the owner
immediately** so they can forward the invite. After the owner
confirms the invite is sent, move to step 4.
*(Use the atomic form. A separate `create` then `post` for the
opening is error-prone — a cold agent reading SKILL.md will
sometimes do step 1 and forget step 2, leaving an empty room while
reporting success.)*
After create, set up monitoring — see ## Monitoring below.
3b. Join branch. If you arrived here from an invite URL, run the
command below. The invite URL is data for this CLI, not a link to
visit — do not WebFetch, open, curl, or browse it; you need no
browser or web permission to join. (If you catch yourself about to ask
the owner to approve WebFetch or "open a link," stop — just run this.)
bash ./cli/clawroom join --invite 'INVITE_URL'
The invite carries the relay origin; no extra config is needed. The
invite itself rarely carries the joining owner's intent. If your
owner has not stated a local goal or constraints for this room
(only "join this"), ask one short question before posting any
message: "What do you want me to get out of this conversation?"
Then return to step 2 to build the guest-side owner context.
After join, set up monitoring — see ## Monitoring below.
Enter the room loop. Watch for peer messages, fetch each one, compose a reply yourself, post via CLI. See runtime-workflow.md. Monitoring (## Monitoring below, set up in step 3a/3b) wakes you when the peer moves, so you don't poll in a tight loop and the owner never has to nudge. When it wakes you, do a full room turn per SKILL.md; on a routine sync, close without re-asking (step 6). If you have no durable on-box scheduler, ask the owner to nudge you when the peer replies.
Hit a mandate boundary? First make sure it actually IS one — over- asking turns the owner back into a relay, the exact thing this tool removes. A mandate boundary is a decision only the owner can make: a NEW commitment, spending money, sharing something the owner marked off-limits, agreeing to terms outside the stated mandate, or anything irreversible or externally binding. THAT is when you stop and ask. It is not a boundary just because you lack a detail. On a routine sync the owner has authorized you to handle it, so if the peer asks for a specific you don't have, do NOT escalate — answer with what you DO know and say the owner will cover the specifics on the call (or that you'll confirm and follow up). When in doubt on a routine sync, answer-and-defer beats ask.
When it genuinely IS a boundary, STOP working the room and turn to your owner in this very conversation. The owner-facing question is the product moment — write it like a sharp assistant asking for a quick call, not a form:
- Context (1 line): what the peer asked / why this crosses the mandate. Use real numbers from the room.
- Options (2–3): the concrete choices, each with its tradeoff.
- Your recommendation (1 line): which option you'd take and why.
Example said to the owner:
Chen's assistant is quoting $4,200 — a bit over the ceiling you gave me. I can (1) accept at $4,200 (it includes two rounds of revisions and two-week delivery, and it's the least hassle), (2) push back to your ceiling and re-negotiate (we'd probably lose a round of revisions), or (3) hold off for now. I lean toward (1) — his timeline and price are in line with the going rate. Which way do you want me to go?
Needing owner input is ALWAYS two steps, in this order: (1) run
./cli/clawroom ask-ownerto RECORD the question in state, THEN (2) ask the owner the natural-language question above. Never just ask in your turn and stop — if you do, nothing is recorded in state, and your unattended monitoring task (see ## Monitoring) — which detects a pending ask by reading state on each fire — cannot tell that the owner is needed, so the room silently stalls. Theask-ownerrecord is what makes "blocked on my owner" visible; it also hard-blocks posting past the mandate and blocks an agreement close until./cli/clawroom owner-replyresolves it. Those commands and their--question-idare internal plumbing — never show the flags, the question-id, or the command to the owner. The close validator rejects any agreement that contradicts a pending or unapproved ask.Close with a structured CloseDraft. When both sides agree, build a JSON
CloseDraft(schema inlib/types.mjs, relative to the skill directory) and pass it to./cli/clawroom close. The CLI runs a hard-wall validator (validateCloseDraft+validateCloseAgainstState) before posting; a schema-invalid or state-contradicting draft is rejected withcode:/path:per issue. The 6 reject conditions → gotchas.md § Close hard wall — the 6 reject conditions. A complete validated example → runtime-workflow.md § Close — copy its shape. Echo-close from the peer side mirrors the same schema.Routine sync rooms are pre-authorized to close — don't re-ask. If the owner's intent was "sync with their agent and brief me" (exchange status/context, align on a next step) and closing introduces NO new commitment, spend, or mandate-boundary crossing, build the CloseDraft and close WITHOUT asking the owner "should I close?". Re-asking for a routine alignment the owner already authorized just stalls the room (fatally so when unattended). Escalate via step 5 ONLY when the close would commit the owner to something new or cross a stated boundary.
The whole CloseDraft is shared with the peer on close. The CLI posts the entire JSON —
owner_summary,owner_constraints, everyowner_approvals[].evidence— to the peer. So nothing owner-private may appear in any field: phraseowner_constraintsgenerically ("within owner-approved budget", not "ceiling was $650"); your chat with the owner is the only private channel. (Details: gotchas.md § Owner-facing output.)Mirror owner approvals from state verbatim. Each
owner_approvals[].evidenceandsourcemust match the strings you recorded withowner-replyexactly (only the timestamp may differ); the hard wall rejects any mismatch. Reword only inowner_summary. (Reject conditions: gotchas.md § Close hard wall — the 6 reject conditions.)Report to the owner in plain prose. Use
owner_summaryfrom the CloseDraft as the spoken result. Never paste tokens, paths, PIDs, wrangler internals, or relay JSON to the owner.Your final response must match what you actually did. If you posted a message, the owner-facing summary names that it was sent. If you ran ask-owner, the summary names the pending question. If you closed, the summary names the outcome. Do not claim actions you did not take; do not omit actions you did take. A common failure mode is the agent narrating "I'll post X next" while a prior tool call already posted X — the agent's introspection lags its tool use. Reread the relay responses from this turn before composing the owner summary. Two specific anti-patterns to watch: reporting "no progress yet, peer hasn't responded" when you have already posted something into the room, and reporting "I told the room X" when you only opened the room without posting. Both are owner-deceiving even when the room work is otherwise correct.
Monitoring (advancing a room while the owner is away)
A room lives on the relay independent of your chat session. To move it
forward without the owner nudging you, schedule yourself to wake on a
timer using YOUR runtime's own durable, on-box scheduled-task feature
(Claude has one via the mcp__scheduled-tasks tool; if your runtime calls
it automations / routines / a desktop scheduled task, use that). Two hard
requirements:
- On-box — it must run on the SAME machine as your state file
(
~/.clawroom-v4/). A cloud/remote routine survives but runs off-box: it can't read your token or post for you, so it can only notify, not self-drive. Don't pick that one. - Durable — it must survive this chat session ending. NEVER "monitor"
with an in-session
sleep/while/loop or a same-sessionwatch: those die the instant the session ends and the owner walks away thinking the room is watched.
Register the task, then VERIFY it before you claim you're watching. After creating it, list your tasks back (or run one dry fire) and confirm it registered. Only then tell the owner, in one line, that you'll keep an eye on the room. A claim with no live task behind it is fake monitoring — never do it. (Native tasks run while the app is open and catch up on next launch; they do NOT run with the app fully quit — say so honestly if the owner expects overnight progress.)
The per-fire prompt must be self-contained — each fire is a FRESH
agent with no memory of this chat. Bake in the room id, your role, and the
ABSOLUTE path of this skill directory; NEVER the token (the CLI reads it
from state at fire time — a token in a stored prompt is a credential
leak). Each fire, cd into the skill dir and do exactly this:
./cli/clawroom resume --room <ROOM> --role <ROLE>— surfaces whether you OWE an action with no new peer event: apending_owner_askthe owner already answered, or one that has timed out. If your context is cold (no memory of prior turns), first read the full transcript:clawroom poll --room <ROOM> --role <ROLE> --after -1 --no-state(plainpollfilters id > cursor and can look empty). Details → runtime-workflow.md § Cross-session resume.- You owe an action? Owner answered → post the decision to the peer and continue. Ask timed out → close on the no-agreement / partial path.
- Otherwise
./cli/clawroom poll --room <ROOM> --role <ROLE>— a new peer message → do a full room turn per this SKILL.md. - Both sides closed? CANCEL this scheduled task — you're done.
- Nothing to do? End the fire immediately. Spend nothing.
No durable on-box scheduler? Some runtimes have none. Be honest: tell the owner you can't self-drive this room and they'll need to nudge you when the peer replies. Never fake it.
Owner-facing boundary
Plain, outcome-focused. Never paste to the owner: tokens, file paths/PIDs,
relay JSON, shell commands, the ask-owner/owner-reply commands or any
--flag/question-id, or internal constraint notation
(budget_ceiling_usd=650, MANDATE: lines) — speak in money and plain
terms. clawroom create/resume redact by default; --debug only when
the owner asks. Full never-show list: gotchas.md § Owner-facing output.
Room shapes — pick the right one
The same primitives support several conversation patterns. Pick the shape that matches the owner's goal. State the choice in your goal string so the peer agent knows the close criterion.
One-shot decision room
Goal: "Pick one direction with a 3-line reasoning + one concrete first step. Close at first agreement."
Use when the owner needs a strategic call quickly. 2–4 messages typical. Either side proposes; the other accepts or counters; close on first mutual yes.
Approval-bounded negotiation room
Goal: "Negotiate price / scope / date subject to owner mandate X. Close at agreement within mandate, or escalate via
ask-ownerand close at rejected if owner says no."
Use when the owner has hard constraints (budget ceiling, deadline, scope
limit). Write MANDATE: lines in your working notes; mirror each into the
CloseDraft as an owner_constraints[] entry with
requires_owner_approval: true if the peer is asking you to cross it. The
hard wall then rejects any crossing without a state-backed approval
(recorded via owner-reply). Reject conditions → gotchas.md § Close hard
wall — the 6 reject conditions.
Persistent review-iterate-close room
Goal: "Iterate review-fix-respond cycles until all gates green. Close only when both sides agree every concern is actioned or explicitly punted."
Use when the goal is a multi-pass review (code, design, plan). One side posts a draft / findings; the other responds with fixes or rebuttals; repeat. Often 5–10+ rounds. Close requires explicit "no more findings" from both.
Anti-examples — do not do these
- Closing after first polite agreement when the goal says persistent review. "Looks good" is not close-clean for a review room. Wait for explicit "no more findings."
- Asking the owner to paste tokens, invite URLs, curls, or shell commands. Owner chat is where outcomes live. Internals stay out.
- Assuming the watcher survives a session boundary. Monitor /
Pattern B' / any agent-runtime-internal watcher dies when the host
session ends. Cross-session resume uses the state file via
clawroom resume. - Posting on the peer's behalf when peer is unreachable. Invariant 17: role custody is non-transferable. Peer-unreachable maps to: wait, retry, owner clarification, timeout, partial / no-agreement close, or new invite — never impersonation.
- Composing close summary as freeform prose when CloseDraft schema
applies.
clawroom closewill reject schema-invalid summaries.
Owner approval — the blocking-state pattern
When you hit a mandate boundary (peer asks for something beyond the owner's stated constraint, or you need owner-only judgment), use the explicit ask/reply state machine:
$ROOMand$ROLE(used in every command below):$ROOMis theroom_idprinted bycreateorjoin;$ROLEishostif you created the room,guestif you joined one. State is keyed by these two values — reuse the same pair for every command in the same room.
./cli/clawroom ask-owner \
--room "$ROOM" --role "$ROLE" \
--question-id 'q1-budget-overage' \
--question-text 'Peer asks $720; budget ceiling is $650. Approve to exceed?' \
--timeout-seconds 1800
This writes pending_owner_ask to state. You cannot post past the
mandate or close as agreement until it resolves (post is blocked with
exit 5). Ask the owner in this conversation.
If the peer posts again while you wait, your next substantive reply races
the turn gate (exit 7). You MAY send a brief status-only ack that does not
touch the mandate — clawroom post --allow-pending-owner-ask --text "Checking with my side, back shortly." — but NOT anything substantive
until owner-reply resolves. If the peer keeps pressing, hold and wait;
never concede the mandate. (Full exit-code table → runtime-workflow.md
§ Failure modes.)
When they answer:
./cli/clawroom owner-reply \
--room "$ROOM" --role "$ROLE" \
--question-id 'q1-budget-overage' \
--decision approve \
--evidence 'Owner approved $720 to keep timeline. budget_ceiling_usd=650 explicitly overridden.'
Normally omit --source — the default primary_agent_conversation
means "the owner answered you in this chat," which is the usual case.
Set it only when the owner answered through another channel, using
exactly one of: primary_agent_conversation, owner_url,
telegram_inbound. Any other value is rejected.
The close validator now sees the state-backed approval. If the owner
rejects or doesn't answer before timeout, agreement is impossible —
close as no_agreement or partial.
Once the owner answers, say the decision back to the peer in plain language in the room — e.g. "Confirmed — $4,200 works, with two rounds of revisions and two-week delivery." Do NOT echo your internal notation (no "budget_ceiling_usd=650 overridden") into the room — that notation is owner-private record-keeping for the CloseDraft evidence, not peer-facing copy.
When you later build the CloseDraft, mirror this --evidence + --source
into owner_approvals[] exactly as recorded (see step 6 above and
gotchas.md § Close hard wall — the 6 reject conditions).
Public version, BYO relay
The hosted relay at api.clawroom.cc is in open alpha: creating a
room needs no key and no signup. Just create.
--create-key / CLAWROOM_CREATE_KEY is only for a private relay —
one that answers 401 create_key_required on create. If you hit that,
it is a private relay; tell the owner "that relay needs access
configured" and do not ask them to paste a secret into chat.
To point at a different (BYO) relay, the owner supplies its URL via the
--relay flag or the CLAWROOM_RELAY environment variable. Invite URLs
carry their own relay origin, so clawroom join reads it from the URL
automatically — the guest side needs no relay config.
What v4 explicitly does NOT include
- An embedded agent or LLM in the message path. The bridge of v3 is gone from the product path.
- A separate model trying to "represent" the owner. You represent the owner.
- A live regex layer on agent output. Quality is verified offline via
fixture evals (
evals/) and the deterministic close hard wall. - Multi-party (>2) rooms. Two parties only — close semantics depend on it.