For developers · beta

Two apps. One local daemon. No magic.

A dependency-free TypeScript daemon on your machine, driven over stdio by either a VS Code fork or a plain chat app. Per-agent model binding, five permission modes with three rails you cannot unlock, and a copy-on-write overlay so a diff exists before your files move.

Ask for the beta Read the architecture

Windows builds today · bring your own key or your existing CLI subscription

⬡  STDIO
// host → daemon, one JSON object per line {"type":"hello","workspace":"C:/work/app","cliBackends":{"coder":{…}}} // daemon → host: the daemon never touches your disk itself {"type":"execute_tool","name":"fs_write","path":"src/api.ts"} {"type":"tool_result","ok":true,"wrote":".monoprise/ghost/coder/src/api.ts"} {"type":"diff_ghost"} → {"type":"ghost_diff","files":[…]} // nothing has touched src/api.ts yet. apply_ghost does that.

At a glance

The short version.

Daemon TypeScript, zero runtime dependencies, spawned on the runtime the app already ships. Nothing to install — no Python, no separate Node.
Transport Newline-delimited JSON over stdio. stdout is protocol-only, logs go to stderr. No port is bound, so there is no token and no token-missing failure path.
Hosts A VS Code 1.127.0 fork with a panel extension, and a chat app that is plain Node with zero dependencies. Same protocol, same daemon.
Binding Model, key and subscription attach to an agent, not to a role. Two agents holding the same role do not share a wallet.
Writes Copy-on-write overlay under .monoprise/ghost/, only changed files stored. Diff, then apply, then a checkpoint you can rewind to.
Permissions Five modes, plain-text rules in .monoprise/permissions.md, re-read on every call. An unknown mode falls back to asking, never to allowing.
Secrets Chat keeps keys in process memory only. The IDE keeps them in VS Code SecretStorage only. Neither writes a key to disk, a log or a record.
Extending MCP servers via .monoprise/mcp.json, plus per-role rule files and on-demand skill files under ~/.monoprise/.

Architecture

Two hosts, one daemon.

Everything that decides anything lives in the daemon. The hosts own their UI and their filesystem, and nothing else. The daemon cannot tell which of the two launched it — that was the design goal, and it is what keeps the two apps from drifting.

HOST · EDITOR

Monoprise IDE

A VS Code 1.127.0 fork. The team lives in a panel extension; the extension owns the webview, the workspace-trust gate, key storage and the daemon's lifetime.

HOST · CHAT

Monoprise Chat

Plain Node with zero dependencies — a local HTTP and SSE surface plus a browser UI. It honestly refuses the two editor-only tools rather than faking them.

SHARED DAEMON

TypeScript · zero dependencies · child process

Routing, orchestration, the permission gate, the overlay and checkpoints, the vendor-CLI contract and the audit log. It holds no keys and never touches your filesystem: it emits execute_tool and waits for tool_result. MCP is the single exception, because the daemon spawns those servers itself.

Why there is nothing to install. The daemon is spawned on process.execPath — the runtime the desktop app is already running on. That one decision is the entire story of the zero-prerequisite install. An earlier version of this daemon was Python over a WebSocket port, and the very first outside tester got stuck on installing Python 3.10.

Sessions are folder-scoped. An agent is pinned to the first folder it is handed, and changing folder restarts it. The chat app keeps per-folder transcripts keyed by a hash of the lowercased path, so reopening a folder reopens that folder's last conversation.

Connections

The connection belongs to the agent, not the role.

A subscription is bought by a person and follows that person. Bind it to a role instead and the moment two agents share a role, one of them is spending out of the other's wallet.

Agent Role in the run Connection Model
Rosa Planner Claude Code subscription opus
Dane Builder Anthropic API key sonnet
Iris Builder — second bid Codex subscription gpt-5.6-terra
Otto Reviewer Groq API key your pick

Dane and Iris hold the same role and bid against each other on the same task — on two different accounts, because the binding is per agent. Three slots sit outside this map on purpose: Judge, Chat and direct run, since none of them is a pipeline stage and an agent-name map would collide the day someone names an agent “Judge”.

What you can plug in

Vendor CLI subscription API key Notes
Claude Code ✓ ✓ The only one of the four that accepts a spend cap on a turn.
OpenAI Codex ✓ ✓ Detection stops at “installed” — its CLI exposes no login probe.
Google Antigravity ✓ ✕ Refuses key mode outright. The binary would ignore the key and bill a different account, and silently billing the wrong account is worse than refusing.
Gemini ✕ ✓ Key only, permanently. The interactive Google login in the old Gemini CLI was retired in June 2026, and reviving it from a third-party app gets accounts banned.
Anthropic · OpenAI · Groq — ✓ Direct API keys, billed by the provider to you.
Custom endpoint — ✓ Any OpenAI-shaped endpoint: set a base URL and it goes there instead. Presence of a base URL is what makes a key “custom”, checked before every other rule.

HOW A PASTED KEY IS ROUTED

// checked in this order — base URL wins over everything baseUrl present → Custom "sk-ant-…" → Anthropic "gsk_…" → Groq "sk-…" → OpenAI "AIza…" / "AQ.…" → Gemini otherwise → Unknown (it says so instead of guessing)

Detection, connection and the actual gate are three different things. Detection is a harmless --version probe, so it only knows a CLI is installed — never whether you are logged in. The “connected” list that populates the dropdowns is client-side state, an affordance and not a wall: if you connected a subscription and it is not in the list, run the connection test once and it appears.

The wall is in the daemon. Before every run it takes a no-charge probe of the backend an agent is bound to and refuses the turn if that backend is not runnable — memoised for 45 seconds so it costs you nothing. Login state is answered as true, false or null, and null means nothing answered the question. It does not guess.

The paid half of the connection test — one real round trip that answers with OK — never runs unless you explicitly consent, and when it does it runs read-only. A button that verifies a connection must not be able to change a file.

Permissions

Five modes. Three rails that outrank all five.

The modes decide how much you are asked. They never decide what is checked. When you say “unattended” you are saying don't ask me — you are not saying don't check.

Mode Ordinary work Risky operations On the CLI path
manual Asks about nearly everything Asks Demoted to read-only
ask Reads pass; anything that writes or runs asks Asks Edit
auto · default Passes Asks Edit
fullauto Passes Destructive ones are refused, not asked Edit
unattended Passes Passes every soft gate — the three rails still hold Edit

Two details worth knowing. An unrecognised mode string resolves to ask, never to allow. And our unattended is deliberately not wired to a vendor CLI's full bypass flag — “don't ask me” is not “unlock the vendor's own locks”.

The three rails

All three are evaluated before the mode gate is even consulted. There is no mode, rule or flag that turns them off.

OUTSIDE THE WORKSPACE

Paths are canonicalised to a single spelling first — NTFS alternate data streams, drive-relative C:..\, trailing dots, \\?\, junctions — and then anything landing outside the open folder is denied.

ITS OWN RULE FILES

It may read .monoprise/ but never write or delete inside it. A gate that can edit its own rule file is a lock that has handed over its key. Reading stays allowed so it can explain a refusal instead of retrying it.

YOUR DENY RULES

A matched deny returns before the mode is read. In unattended, where there is no “ask” to fall back to, a command naming a denied path is refused outright. Deny wins even in unattended.

.monoprise/permissions.md — RE-READ ON EVERY CALL

# workspace rules beat global rules beat the mode beat the defaults. # within one file the longest matching pattern wins. src/** = allow .env = deny # blocks reads too, not just writes node_modules/** = deny $ git push --force = deny # $ prefix = a shell command rule $ npm test = allow $ mcp__shell__run = deny # MCP tool names use the same prefix

Two files layer: ~/.monoprise/permissions.md globally and <workspace>/.monoprise/permissions.md per project. Both are re-read on every call, so an edit takes effect on the next action with no restart. A path deny blocks reads as well as writes — a rule that stops a write but lets the file be read to a model has blocked the cheap half and allowed the expensive one.

What counts as risky

Three things are classified risky, and in auto they stop and ask you:

1

Every delete

No exceptions and no size threshold.

2

Every MCP tool call

We have no idea what a third-party server does, so all of them are risky by default — including in fullauto, where they are asked rather than refused so a working server isn't bricked.

3

Shell commands that look like harm

Recursive removes on every shell dialect, disk-level tools, dd, mkfs, sudo, chmod 777, git push --force, fork bombs, and anything piping a download into an interpreter. Shell metacharacters count too, and so does any command that names a path you denied.

This list leaks, and we say so. A blocklist of dangerous command shapes is a memory of past accidents, not a boundary. It is deliberately narrow — rm -f one-file.txt is not treated as risky — and it never substitutes for the three rails above. Anything that depends on this list catching everything is depending on the wrong thing.

Change flow

A diff exists before your files move.

This is the part that makes leaving it running defensible. Writes land in an overlay, you get a real diff against your working tree, and only an explicit apply moves anything — with a checkpoint recorded first.

1

Writes are rerouted

Every write from a risky tool is redirected into .monoprise/ghost/<agent>/. Only changed files are stored — there is no copy of your repository, and read paths fall through to the real file so the agent sees a coherent tree.

2

You get a diff, not a summary

Per file: status, additions, deletions, binary flag and the patch itself. Long diffs are truncated for display, but the counts are always real — only the text is cut. The preview is exempt from the execution lock on purpose, because it is read-only and must answer while other work is still running.

3

Apply, or discard

Discard throws the overlay away and your tree was never touched. Apply writes through — and unlike the preview, both apply and rewind take the execution lock.

4

Rewind restores and un-creates

A checkpoint's manifest is written before the first overwrite, and it records created files as well as modified ones — because restoring a backup cannot express “this file did not exist before”. Rewinding puts back what changed and removes what appeared.

5

A stale restore is refused

If a file changed between you reading the diff and pressing the button, that file is refused by name rather than overwritten. Restores report what they refused, not just a count. A silent overwrite here is data loss wearing the mask of a successful restore.

The overlay does not exist on the subscription path — and the app says so before every turn. When a vendor CLI runs your turn, it edits real files with its own tools in its own process. There is no per-tool permission decision, no confirmation card and no overlay on that route. What remains is rollback after the fact, from a byte-level snapshot taken before the turn. If that snapshot cannot be taken, the turn is refused rather than run unprotected.

The snapshot uses a temporary git index, so it touches neither your real index, nor HEAD, nor your worktree, and it captures untracked files — which is exactly why git stash create was rejected for the job. Byte-identical restore needs git 2.40 or newer when a .gitattributes is present; on older git it returns a warning instead of claiming an identity it cannot guarantee.

What each stage is allowed to do

Stage Grade Why
Builder edit The only stage that needs to write.
Planner · Reviewer · Judge readOnly A reviewer that can edit is not a reviewer. On the strictest vendor this comes up triple-locked.
Direct run edit You addressed a CLI yourself, so it keeps its full toolset — which is why it is a separate slot rather than reusing the pipeline's.

Where a vendor's read-only mode rests on a single lock rather than several, the app announces that before it spawns the process. You are told how thin the guarantee is, in the moment it matters.

Local and secrets

No server of ours is in the path.

There is nothing to opt out of, because there is nothing to opt into. Your code is read and written locally, and requests go from your machine straight to the provider you configured.

Thing Chat app IDE
API keys Process memory only. Gone when the app closes. VS Code SecretStorage only. Never in settings state.
Saved on disk Which backend you chose — never the key itself. Same: the record holds vendor, mode and model, and no credential.
Returned to the UI The shape, never the secret. Usage figures and labels only.
Subscription credentials Never touched. In subscription mode nothing is passed and the vendor CLI finds its own login — and billing-related environment variables are stripped from the child process so an ambient one cannot redirect the charge.
Audit log Written to be shown to someone else: secrets are scrubbed from it, and the scrubber is tested against a corpus of fake credentials — including over-redaction, so it doesn't destroy the record it exists to keep.

~/.monoprise/ — ALL PLAIN TEXT, ALL RE-READ EVERY RUN

rules.md # always in the prompt rules/coder.md # layered in per role or per agent skills/<name>.md # one line resident, body loaded on demand judge.md # your acceptance bar permissions.md # the rule file above trusted_paths.md # deletes here need no confirmation trusted_commands.md # a program AND the mode it may run in <workspace>/.monoprise/ # same shapes, layered on top. commit to share.

Skills are why this does not turn into prompt bloat: only a one-line description of each stays resident, and the body is fetched when the model asks for it — so adding more skills does not cost more tokens per turn. Trusted commands name a program and the mode it may run in, because node --check -r ./payload.js app.js ran arbitrary code once, and once was enough.

What we will not claim. Two masking gaps in the audit scrubber are open and tracked: passwords embedded in URL userinfo that are short and unremarkable, and unnamed AWS secret access key values. Webhook forwarding uses the same scrubber, so it leaks the same way. We count this as “this much is open”, not as “closed”.

Model choice and extension

Sensible defaults you can overrule.

PER-ROLE DEFAULTS

Recommended, then editable

Each role gets a recommended model from a tier table, and you can change it. Quality roles have their own defaults, because a lightweight chat default leaking into planning and review is a silent downgrade nobody notices — the UI warns you if you assign a lite or mini family model to anything but chat.

WHEN A MODEL FAILS

One fallback, then it tells you

Models that already died this session are skipped before they are tried again, and a 404 or an exhausted quota gets exactly one automatic fallback. Rate limits are read rather than blanket retried: a short retry-after means wait, a long one means switch.

SUBSCRIPTION MODELS

A locked list, not a text box

Model names on a subscription come from a fixed dropdown, and an empty value means the account's own default. A name starting with a dash is rejected outright, so a client-supplied string can never become a command-line flag.

SPEND VISIBILITY

Real token counts, locally kept

Usage is taken from what each call actually reports, accumulated per key, and a key over its limit is dropped from requests rather than left to fail. Providers expose no account-balance API, so local accounting is the only honest option.

MCP servers

Declare servers in <workspace>/.monoprise/mcp.json and the daemon spawns them itself — the one place it does anything without going through the host. Their tools appear alongside the built-in ones, they can be denied by name with the same $ prefix as a shell rule, and they are treated as risky in every mode. Note one current limit before you wire up a chatty server: MCP results are cut at 40,000 characters from the front, so a longer result arrives with its tail already gone.

How it's kept honest

The contracts are locked by tests.

Two apps implementing the same logic twice, with no shared code, drift on every pass. So the drift was made loud: shared tables are named, and a suite fails by table name when the two sides disagree.

PARITY

Change one app, break the suite

29 shared tables are marked with a named anchor — key-shape detection, permission modes, secret patterns, model tiers, the connection gate and the rest. The parity suite does not compare strings: it extracts the declarations from both apps, compiles them, and runs them against a common corpus, so it compares behaviour. It carries 353 assertions.

Honest footnote: 22 of the 29 anchors appear by name in that suite. Four have no section at all. An anchor marks that a copy exists — it is not itself a lock.

VENDOR CLIS

Tested without ever calling them

Calling a real vendor CLI in a test burns the developer's own subscription, so the suite never does. Four fake vendor executables — shell and .cmd pairs — are put on PATH, and the product picks them up by name with no knowledge that anything is staged.

Their behaviour is switched by environment variable only, because the argument vector is the thing under test. And they never report their environment back, so a developer's real key cannot land in a temp file.

ESCAPES

The escape is actually attempted

The sandbox suite tries the escape for real and then checks the filesystem, rather than asserting on an error message — because a refusal string can be returned by a call that also succeeded in writing.

PACKAGING

The staged build is launched

The packaging test starts the packaged artifact and waits for a handshake, so “it builds” means the thing that ships actually speaks. It also asserts the process spawned is the application itself, not some interpreter found on the machine.

Vendor CLI versions the integration was measured against: Claude Code 2.1.143, Gemini 0.51.0, Codex 0.147.0, Antigravity 1.1.12. Prompts are always handed to a vendor CLI on stdin, never on the command line — Windows caps a command line at 32,767 characters, and going through the shell hands your prompt to the shell's own parser.

Where we are

Beta, and here's the list.

Written the way we'd want to read it if we were evaluating somebody else's tool.

WORKING
Both hosts on one daemon, same protocol both sides
Per-agent binding across 6 key providers and 3 CLI subscriptions
Five permission modes, three rails, plain-text rule files
Overlay, diff preview, apply, checkpoint and rewind
MCP servers, per-role rules and on-demand skills
Windows x64 portables and installers
NOT DONE
Windows only. No macOS or Linux artifact exists yet
No download page — the build is sent by email
44 known test failures, from suites that lag a refactor rather than from broken behaviour. Tracked as a number that must not grow
Two audit masking gaps, listed above, still open
MCP results truncated at 40,000 characters from the front
Workspace-trust gate is wired, but the UX around turning it on is unfinished
No paid plans, no accounts, no shared team state

And the thing that will actually bite you first: editing the source does not change a portable build. The daemon only loads new code when the app restarts, and a freshness check refuses to assemble a build whose sources are newer than its compiled output — except on one installer path, which is a known gap we are closing.

Want to try it?

Leave your email and we'll send you the build within a day. Tell us what you'd point it at — the sharper the setup, the more useful your report back.

Which one should we send?

Not sure? Take Chat — you can switch later and lose nothing.

Beta · free · no card

Prefer to just email us? monoprise@monoprise.dev

Used only to send you the beta build. No newsletter, nobody else sees it.