Ripieno

Ripieno

ripieno — in a concerto grosso, the full ensemble, as against the soloists. Several parts, sounding together, each still its own line.

A shared workspace in your editor where several people and several AI agents work on one codebase at once — self-hosted, with each person bringing their own agent through the supported local CLI or OpenAI-compatible provider paths.

Everything is attributed to a named actor: a specific agent belonging to a specific person, rather than whoever's machine happened to run it.

The room panel: three people in different colours, an agent reply citing files from two different members' workspaces, an action log naming which agent did what on whose machine, and an approval card asking permission to run a command.

Three people, three agents, one conversation. Note the last two blocks: the action log records that Mira's coder wrote to room.ts on @mellery while Kate's agent read a file on @swhitfield — the machine is part of the record, not an assumption. And the approval card is how a write reaches somebody else's disk at all: it does not, until they say so.

The naive version of this is a shared login, which gives the agent one anonymous view of a team: it cannot tell agreement from disagreement, whose preferences to honour, or whose filesystem the next write lands on. Here every message the agent receives carries its author as structure the relay maintains, and every workspace action runs on the asking member's own machine under their own permissions.

This project does not operate a hosted service. A relay sees every message and routes every tool call, so the current model is deliberately self-hosted. You run your own, or you run none at all — solo mode needs no separately deployed relay, account or token. See docs/self-hosting.md.

Two agents belonging to two different people can write to the same repository concurrently, and git log names each of them correctly:

Running npm run demo:provenance: three agents write to one repository concurrently, and git log's author column holds three different names.

$ npm run demo:provenance

$ git log --pretty='%<(16)%an  %s' -5

  Alex's agent      Mention the limits in the README
  Sam's reviewer    Write down both limits
  Mira's coder      Bound the transcript
  Sam's reviewer    Cover the rate limit
  Mira's coder      Add rate limit to relay

That column normally holds one name — whoever's machine ran the commit. It is real output: the demo runs the container's own code against a real repository, writing concurrently rather than in turn, because git serialises on .git/index.lock and losing that race is how five writes in six once vanished.

Status: what's real

Honest, because an unknown repository has no other way to earn it.

Works, used Solo mode, invite links, rooms over a deployed relay, several agents per member across supported providers, remote workspace tools, approval-gated side effects, action log, per-agent usage, verified GitHub identity, owner/member/viewer roles, and room history that survives a redeploy when the relay has persistent storage
Built and tested, never deployed The shared-workspace container (packages/workspace-host) — including real git integration tests, and it has never run anywhere but a test
Not shipped Hosted mode. Built against the same driver interface, compiles, unit tests pass — and it has never run against a live Managed Agents session, so it is not shipped and not described here as a feature

npm test builds the monorepo and runs every package that currently has a test script. Exact totals are intentionally left to the test runner so this page does not go stale as coverage changes. Security fixes found by adversarial review have regression coverage in the affected packages.

The more useful thing to say is where tests did not help. Several bugs were found only by two people using it, and several more by an outside reading of the whole repo — a role menu whose when clause named a view that does not exist, so the feature rendered for nobody; an empty activationEvents, so invite links only worked if the extension was already awake; a permission setting that promised to ask before writes and mapped to a mode that pre-approves them. None of those are reachable from a unit test of the code behind them, and all three had passing tests underneath. The newest tests go after exactly that: one runs a real relay, a real agent host and a real subprocess and asserts on the prompt the agent was handed (it immediately found that every CLI agent had been silently running Claude Code); another checks the extension manifest against itself.

Try it in one minute, alone

Nothing to deploy, no account, no token.

npm run setup

That installs dependencies, builds, packages the extension and installs it into your editor — VS Code, Insiders, Cursor, Windsurf, Antigravity, VSCodium or Positron, found on PATH or inside the application bundle, because the code command is not on PATH by default on macOS and most people never add it.

Two steps are left and both are genuinely manual: reload the window, then Ripieno: Join Room and type any room code. Leave ripieno.relayUrl empty and the extension runs a relay on this machine.

If no editor is found, setup prints the absolute path to the .vsix and the two clicks that install it by hand.

It is deliberately the same relay a team shares, not a reduced imitation — same rooms, same attributed transcript, same tool routing, same action log, and history that survives a reload. Solo is where people form their opinion of the product, and a cut-down version would teach them the wrong thing about it.

Choose Add agent… in the Rooms & Agents tree. Ripieno asks which account should power it, putting a detected local provider first. It generates the name, keeps the brief optional, uses the provider's default model and starts at the safest boundary Ripieno can actually enforce. Name, brief, model, response mode, workspace folder and permissions remain individually editable from the gear.

Use your ChatGPT account

Choose ChatGPT / Codex — the recommended option. Ripieno checks that Codex CLI is installed and signed in; if it is not, it opens Codex's own trusted sign-in flow in a terminal and continues after the login succeeds. Ripieno never sees or stores your ChatGPT credentials. Once the account is ready, the agent joins immediately with a normal name (agent, then agent 2), the current workspace, the provider's default model, no special brief and Read project access. An existing detached agent gets a single Attach agent step instead of another setup wizard.

Use the gear beside any agent to change its name, add or remove a brief, point it at another folder, choose a model where the provider supports it, choose automatic replies or only-when-named response mode, or change permissions. The same menu deletes an agent after confirmation and forgets its saved session and credentials. The plain-language boundaries describe concrete enforcement: Conversation only for API chat agents, Read project for sandboxed Codex, Ask before changes for Claude's approval bridge, and Trusted workspace for Codex workspace-write. A separately confirmed Full computer access option stays explicitly named because it removes the workspace boundary. The Codex boundaries follow OpenAI's sandbox model: workspace write is the normal local boundary, while full access removes it.

This creates a new coding agent; it cannot import a ChatGPT web conversation or custom GPT. Install Codex CLI and run codex login to sign in with ChatGPT, or use an API key. API-key usage is billed separately through the OpenAI Platform at API rates rather than from ChatGPT plan credits. See OpenAI's Codex authentication guide.

Room commands

Type / in the room composer to see the commands Ripieno handles locally:

Codex and Gemini receive the selected model through their CLI model option; Claude and OpenAI-compatible agents receive it through their existing provider configuration. Custom CLI agents keep model selection in their own arguments.

Adding people

Set ripieno.relayUrl to a relay you can both reach, then Copy Invite Link. The link carries the relay, the room and the token; clicking it confirms what is being joined before anything happens, and the token goes to the editor's SecretStorage rather than settings.json — settings are one git add . from being published.

Run a relay anywhere Node runs:

RIPIENO_HOST=0.0.0.0 RIPIENO_TOKEN=$(openssl rand -hex 24) RIPIENO_DATA_DIR=./data npm start

RIPIENO_TOKEN gates who may reach it. RIPIENO_DATA_DIR is where room history lives — without it a restart empties every room. Put TLS in front and give members its wss:// URL; remote plaintext connections are refused so credentials cannot cross the network unencrypted. The standalone relay verifies GitHub identities by default even if it binds to loopback behind a proxy; RIPIENO_REQUIRE_GITHUB=0 is an explicit opt-out for a trusted private deployment. The extension's embedded solo relay remains tokenless and local.

Bring your own agent

Each member attaches their own agent, running on their own machine, and pays for it however they already do. There is no per-seat cost here and nothing to sign up for — the room does not resell anyone's inference.

You have Attach Cost Can touch files
A Claude Code subscription Claude Code already paid for yes
A ChatGPT or Google plan Codex or Gemini CLI already paid for yes
An API key any OpenAI-compatible endpoint — Grok, Kimi, DeepSeek, Together, Groq per token, billed by them no
A GPU and no budget a local Ollama model free no
Something else entirely any CLI agent, with its own flags yours yes

Two people in one room need not have made the same choice, which is the point: a subscription and an API key sit side by side and neither knows about the other.

That last column is not a footnote. Capability differs and the room says so rather than implying otherwise: a Claude Code agent reads and writes files and runs commands; an API-key agent can only talk. Presenting both as "an agent" without distinction would let somebody ask Grok to fix a file and get a confident answer while nothing happened.

The API-key path is tested against a mock endpoint — request shape, auth, history, token accounting and each failure mode — but has not been run against a live provider, so treat vendor quirks as unproven.

Usage is per agent, and in BYO it is turns and tokens, never a price. Claude Code reports a dollar figure on a subscription too, where it is what those tokens would have cost on the API and nobody is billed a penny of it — and next to a colleague's agent on a different plan, the two figures do not mean the same thing.

An agent can also join over MCP instead. See the MCP setup guide before copying .mcp.json.example: remote relays need a room token and verified relays additionally need a GitHub token, and MCP client configuration commonly stores those values as plain text. That path gets ten tools (room_read, room_post, room_roster, room_actions and six workspace_*).

The shared workspace

An agent normally works in its owner's own directory, and two members' copies of a project may genuinely differ — which is useful, and is why an agent can read the same file from two machines and tell you what changed.

Point an agent at the room's workspace instead and something different happens: it acts on whichever machine is hosting, and so does everyone else's.

One filesystem, not many copies. There is nothing to replicate and nothing to merge, because there is only ever one of it. Mount it in your own explorer and you get a live read-only view of the host's actual disk, fetched on demand rather than copied to yours.

Invalidated after a short debounce. The host watches its workspace and publishes changes after a 400ms debounce, ignoring the churn nobody browses — node_modules, .git, build output. Network and editor scheduling mean this is not an end-to-end half-second guarantee. The relay checks the sender really is the host before rebroadcasting, so a member cannot invalidate everyone else's view of a workspace that is not theirs. Every member drops the stale paths and re-reads on next access.

Member-hosted writes stop at a person. An agent's write raises a diff on the member host's machine naming the agent that asked, and nothing lands until they accept. It then appears in the room's action log, so other agents can see what has been done instead of redoing it. The headless container has no person to prompt; it uses its explicit command/write policy instead.

The container (packages/workspace-host) is the version that outlives everyone: it clones a git repository, serves the same tool calls, and commits each write as the agent that made it, so provenance ends up in git log rather than only in the room. Built and tested; not deployed anywhere yet. Run it locally with npm run build:workspace && npm run start:workspace.

Layout

Package What it is
packages/protocol The WebSocket contract. Pure types and helpers, no dependencies
packages/relay The server: membership, transcript, provenance, tool routing, persistence, identity, roles
packages/relay-client The WebSocket client both the extension and the container use
packages/workspace-core The workspace tools and the path-confinement boundary, shared by the editor and the container
packages/workspace-host The container that can host a room's workspace
packages/extension VS Code: the room UI, agent hosting, approvals, the shared-workspace view
packages/mcp An MCP server, so an agent can join a room without the extension

The relay splits into a room core — membership, transcript, provenance, addressing — and a driver that turns room events into agent turns. The core never assumes it owns the agent loop. ByoDriver coordinates each member's own agent; a HostedDriver running one shared session was built against the same interface, which is the evidence the seam is real rather than hypothetical.

Security boundaries

The container's command allowlist is a trust decision, not a sandbox

RIPIENO_ALLOWED_COMMANDS is empty by default and a container with no allowlist runs nothing. If you set one, understand what you are choosing.

An agent can write project files. So allowlisting npm test means an agent can write a package.json whose test script is anything at all, and then legitimately ask for npm test. The escape is through the allowed program's own configuration, not through the command string, so no amount of blocking shell metacharacters changes it.

Allowlisting a build tool means trusting everyone in the room with code execution in that container. Often a reasonable choice; not one to make by accident. What the container does instead is make the prize small: RIPIENO_* variables are stripped from every command's environment in both hosts, and in the container provider keys and anything ending in TOKEN, SECRET or PASSWORD go too — nobody is watching there, and the room could simply ask an agent to run env. On a member's own machine they are kept: a human approved that command, and stripping their environment would break ordinary work to prevent something they were present for.

Tests

npm test          # builds first, then runs every package that has a test script
npm run typecheck

The rules that are expensive to get wrong are pure functions with no I/O — roomCore.ts (provenance envelope, addressing, event dedupe, the idle gate), protocol/describeMembers (what an agent is told about who is in the room) and workspace-core/paths.ts (the confinement boundary) — so they are tested without a socket or a credential.

What that misses is everything the pieces do together, which is where the bugs two real users hit actually lived. So extension/test/agentHost.test.js starts a relay, attaches real agent hosts to it, and puts a script in place of the model that writes down the prompt it was given. It is the only way to assert that an agent was told who is in the room — and it immediately found that selecting a CLI provider had been quietly running Claude Code instead.

Not done, on purpose


Built by Ivan Hart (@ijmh2). Source-available under the alpha terms — free for personal and non-commercial use, licensed for organisations. Not open source: the source is public so it can be checked, not so it can be resold.

Before using a shared room, read the security model and privacy and data-flow disclosure. Help and issue-reporting guidance is in SUPPORT.md; bundled dependency licenses are in packages/extension/THIRD_PARTY_NOTICES.md.

The names in the tests and screenshots — Mira Ellery, Sam Whitfield, Kate Nakamura, Alex — are fictional. Addressing is decided by matching a first name against a role word against an @handle, and those three have to stay distinct for the tests to mean anything, so the fixtures use people who do not exist.