Loams Studio
An agentic development environment in the Loams console: coding agents in their own git worktrees, with review, checkpoints, approvals and parallel runs, all through loams-agentd.
What it is
Loams Studio is the second mode of the Loams console. The first mode, Console, runs your data platform: orgs, projects, data, compute, access. Studio is where you build software with coding agents. Each agent works in a thread, each thread gets its own git worktree on its own branch, and around the threads Studio gives you a chat timeline, a composer, a diff panel with review comments, git actions, per-turn checkpoints, terminals, a file viewer, previews, supervised approvals and parallel run groups that you compare side by side before you merge one.
Studio does not bring its own agent. It drives the agent CLIs you already have (Claude Code, Codex, opencode, Pi and several ACP agents) through loams-agentd, the per-user agent daemon, on your own subscriptions. Use it if you already work with more than one coding agent, want each one isolated in a worktree, and want to review and pick between their results in one place next to your Loams data. Do not use it as an IDE: it has a file viewer, diffs, terminals and a preview, but no general editor, language server or debugger, and "Open in editor" hands off to yours. And treat it as experimental: the browser path still needs a pasted token, and the end-to-end suite is not finished.
Status
Studio is experimental in Loams 0.1.0: it ships and is tested in CI, but it may change more, or be reworked, before it settles. It is on by default in Loams Desktop. In the browser it sits behind the instance feature studio, which no Loams server sets yet (the feature status table lists "Studio mode is experimental" under the web console).
| Release | What shipped |
|---|---|
| 0.1.0-preview.2 (2026-10-15) | Studio as a second mode of the console and the desktop's default: projects, threads in their own git worktrees, run groups and compare, and the Data Studio, on the desktop daemon loams-agentd |
| 0.1.0 (2026-10-18, current) | The full SD1 surface: loams.agentd.v1 as a Connect API, diff review with comments, git writes (commit, push, pull requests, merge), per-turn checkpoints, terminals, files, previews, supervised approvals, run groups with compare and resolve, notifications, the remote daemon (loams-agentd serve --remote) for the browser, vendored T3 Code components with provenance, and an npm licence gate in CI |
| 0.1.0, changed | New threads are supervised by default, on your own machine as well as on the remote listener (owner decision of 2026-10-15). Existing local users see approval prompts for threads that used to run unattended |
The SD1 plan has Tasks 0 to 28, 30 and 31 merged. Task 29 (Playwright end to end on both editions) and Task 32 (the exit report, which also turns Studio on in the browser) remain.
Architecture
Everything Studio does goes through one backend, loams-agentd (D906). The pieces:
| Piece | Where | What it does |
|---|---|---|
| The console host and the Studio plugin | web/packages/console-host, web/plugins/studio | The mode switcher, Studio's routes under /studio, its pages |
@loams/studio-ui | web/packages/studio-ui | Components vendored from T3 Code and rebuilt on @loams/ui tokens, Base UI and connect-query |
@loams/agentd-client | web/packages/agentd-client | The Connect transports (desktop and remote) and the transcript reducer that Studio and the docked agent panel share (D917) |
loams-agentd | crates/loams-agentd | The per-user daemon: lock, discovery, token, roles, config, the run and serve commands, service install |
loams-agentd-connect | crates/loams-agentd-connect | The loams.agentd.v1 Connect surface: a loopback listener, and the remote TLS listener with OIDC and CORS |
loams-agentd-sessions, -store, -proto, -rpc | crates/ | The sessions engine, its SQLite store, the wire types and the ndjson RPC |
loams-agentd-harness | crates/loams-agentd-harness | One interface over the coding agents, with a native driver per agent |
How it works
One console, two modes
Studio is a mode of the one cordis console, not a second app (D900). A plugin's manifest gains a modes field (["console"], ["studio"] or both; D901), and switching modes filters the navigation, pages and palette without reloading the window. The transport, the sign-in, the query cache, the dock, approvals and notifications are shared, so an agent that is waiting for approval still shows in the badge while you are in Console. The desktop opens in Studio and the browser in Console; after that, the last mode you used wins (D902).
Projects, threads and worktrees
Studio's model is project, thread, worktree (D903). A project is an agentd space plus its git repository: a folder you pick on the desktop, or a URL the daemon clones on a remote host. A thread is exactly one agentd chat, with a harness, a model, a reasoning level, a sandbox level, an approval mode and a working directory.
By default the first run of a thread creates a git worktree under ~/.loams-desktop/worktrees/<repo>/<name> on the branch loams-desktop/<name>. Threads therefore never edit each other's files or your main checkout. "Local" mode, which runs in the main checkout, is a per-thread choice, and Studio warns when another thread is already working there. Worktrees separate edits, not processes: an agent with a shell can still read anything your user can, and the guides say so.
The timeline is the daemon's transcript stream (WatchDocMessages, folded by the shared reducer). Sending, steering, queueing while the agent is busy, interrupting and answering an agent's question are all daemon commands, so a turn keeps running when you close the window.
The harnesses
loams-agentd-harness speaks each agent's own wire instead of a common adapter:
| Harness | How the daemon drives it |
|---|---|
| Claude Code | Its stream-json protocol |
| Codex | The Codex app-server JSON-RPC |
| opencode | Its own HTTP and SSE server protocol (opencode serve) |
| Pi | Its native JSONL RPC |
| Devin, Grok, Hermes, Antigravity | ACP (devin acp, grok agent stdio, hermes acp, Google's agy_acp_server) |
| Loams Bot | ACP, through loams-agentd loams bot-acp |
The crate's own notes record why the ACP adapters for Claude Code, Codex and opencode were retired: they held prompt turns open for background work that the CLIs settle eagerly, which produced false "done" states the native wires do not have. A Mock harness exists for tests and never shows in a picker.
Diff review, git and checkpoints
The diff panel reads the working tree against its base, a commit, or one turn, live as files change. Review comments are anchored to the checkout, path, side, line and the diff's checksum, and are stored by the daemon (D905). A comment whose anchor no longer matches shows as outdated, as on a code host. "Ask the agent to address N comments" queues one turn built from a fixed template that quotes each comment as data; you can also post the comments as a review on the Forgejo or GitHub pull request.
The git toolbar commits, pushes, opens pull requests and merges (D910). Each write is guarded: a commit or discard carries the checksum the person saw, and the daemon refuses with stale_diff if the tree changed since. Pushes use force-with-lease only, never a plain force. A merge with conflicts stops and leaves the tree clean. Forgejo and GitHub credentials stay in the daemon's OS keyring and never reach the page.
Checkpoints are hidden git commits the daemon writes at the start and end of every turn, under refs/loams/checkpoints/<threadId>/turn/<n>/pre and .../post. They are written through a temporary index, so your index, HEAD and branches are never touched, and they do not show in git log or git branch. "Restore to before this turn" writes the pre commit back into the working tree after checking the checksum. It restores files, not history: a commit made since stays in the branch. The scheme is T3 Code's, reimplemented in Rust.
Approvals
Each thread has an approval mode, supervised or full-access (D911). In a supervised thread the harness driver stops auto-approving and turns each permission request into an ApprovalRequested event that only the owner, or the remote studio role, can answer with ResolveApproval. An agent's own MCP token can never watch, answer, or switch a thread to full access.
| Harness | In a supervised thread |
|---|---|
| Claude Code | Runs with --permission-mode default, never bypass; each permission request waits |
| Codex | Approval policy on-request with the thread's own sandbox; command and file-change approvals wait |
| opencode | Each permission.asked waits |
| ACP agents | Each tool permission waits; asks during session setup are rejected |
| Pi | Cannot ask, so a supervised Pi thread is refused until you set it to full access |
Since 2026-10-15 the daemon's default for a thread nobody chose a mode for is supervised, locally as well as remotely; full access is only ever asked for. "Always for this thread" is remembered by the daemon, per thread and tool, never by the agent, so an agent's own "always" cannot reach further than the thread. An approval nobody answers is denied after [approvals] timeout_secs (1800 seconds), pending approvals survive a daemon restart, and a side chat inherits its parent's mode so an agent cannot leave supervision by spawning one. Destructive git actions ask for confirmation in every mode.
Run groups
A run group is one prompt run N times in parallel, each variant with its own harness or model and its own worktree (D904). The daemon resolves the base branch to one commit when the group is created, so every variant and the comparison start from the same commit even if the branch moves. Each variant is an ordinary top-level thread, titled <prompt start> · <harness> <model>.
The group page shows each variant's status, elapsed time, files changed, lines added and removed and tokens; a matrix of which variant touched which file; any two variants' diffs for one file side by side; and, if you chose one, a check (a project action such as your tests) run in each finished worktree, with its exit status and the last 200 lines of output. The comparison snapshots each worktree through a throwaway index, so nothing is committed or staged to compare. An optional judge is an ordinary read-only thread with the comparison in its prompt; it is never a member and cannot resolve the group.
Resolving picks a winner and an action: merge into the base (merge commit, squash, rebase or fast-forward only), open a pull request, or keep it as a thread. The winner's uncommitted work is committed first with your identity. The others are archived, and their worktrees are removed only if you ask. Nothing merges on its own.
The daemon owns the group record, so a group keeps running and stays resolvable after the window closes. The limits come from the daemon's config.toml:
[groups]
max_running_per_group = 4
max_running = 8max_running counts group members only; your other threads are never held back.
Transport: desktop and browser
Studio reads the daemon through loams.agentd.v1, a Connect API served by loams-agentd-connect beside the older ndjson RPC (D907). Its handlers call the same engine dispatch as the ndjson methods, and a parity test runs each scenario through both wires.
- Desktop (D908). The page calls
loams-app://console/loams.agentd.v1.*. Electron main's protocol proxy checks the method against a generated allowlist, adds the owner token and forwards it to the daemon's loopback Connect listener. The page never sees the token. - Browser (D909, D921).
loams-agentd serve --remoteopens a separate TLS listener, off by default. It accepts only access tokens from the configured issuer with the audienceloams-agentdand the one subject the daemon was enrolled for, sends CORS headers only to configured console origins, and serves onlyloams.agentd.v1and preview paths. One remote daemon serves one person.
Key decisions and why
The Studio decisions D900 to D922 come from the owner's direction of 2026-10-13 and are recorded as proposed, with their as-built status, in the decision log. The design is §53, Loams Studio; the daemon is §50.
| Decision | What | Why, and what was rejected |
|---|---|---|
| D900, D901 | Studio is a mode of the one console, selected by a manifest modes field | One shell: moving between running your data and building with agents needs no second app or second sign-in. A separate app was rejected |
| D906 | loams-agentd is Studio's only backend for code work | Desktop and web behave the same way, and the daemon's durability, secrets handling and security model cover Studio. Embedding T3 Code's server was rejected |
| D907 | A Connect surface, loams.agentd.v1, beside the ndjson RPC | The console's UI stack reads server state with connect-query, which needs protobuf descriptors. A typed ndjson client was rejected because a browser WebSocket cannot send Authorization; generating protos from the ts-rs types was rejected as fragile |
| D908 | Desktop calls go through Electron main, which adds the token | The renderer must never hold the daemon's token (D788, D789 hold) |
| D909, D921 | A separate remote TLS listener with OIDC, one subject per daemon | The loopback listener keeps refusing browsers (D788); a multi-user hosted daemon is out of scope |
| D904 | Parallel runs are run groups owned by the daemon | A group survives the window closing; a person always picks the winner |
| D911 | Real approvals, with a supervised mode | Before Studio, every harness driver auto-approved tool calls; only genuine questions reached the person |
| D912, D913, D915 | Vendor T3 Code's UI into @loams/studio-ui with provenance and MIT headers; treat it as a fork | Own the code on our UI stack; do not embed T3 Code's app, server, Effect runtime or WebSocket protocol |
| D914 | An npm licence gate | There was no npm licence check; one dependency of the upstream (heic-to, LGPL-3.0) is outside the allow list, so HEIC attachments were dropped |
| D918, D919 | First-party plugins only; untrusted content renders as text | A third-party frame never reaches the daemon; model, tool and diff output never becomes raw HTML |
| D920 | Terminals on xterm.js | Vendoring T3 Code's prebuilt libghostty-vt wasm terminal would need a reproducible build of it first |
Credit: T3 Code
Studio's interface comes from T3 Code, an MIT-licensed GUI for coding agents by T3 Tools Inc. It is vendored from the upstream commit 6524a8d4 (version 0.0.45), and every vendored or adapted file keeps an MIT header naming T3 Code, its upstream path and the commit. T3 Code's licence is kept verbatim as web/packages/studio-ui/LICENSE.t3code, provenance.json records the upstream and local SHA-256 of every file, and the root NOTICE carries the entry. The import tool copies files from a verified archive without executing anything in it. What was taken, adapted, rewritten or skipped is listed in the vendoring inventory. The checkpoint scheme is T3 Code's too. The compare view is Loams' own: T3 Code has no parallel-run compare UI.
What is compatible
- Harnesses: Claude Code, Codex, opencode, Pi, Devin, Grok, Hermes, Antigravity and Loams Bot, each driven over its own protocol (above). Studio uses whichever of them are installed on the daemon's machine; the daemon can install some from Settings on the desktop.
- Code hosts: pull requests and reviews on Forgejo (with the factory credential in the daemon's keyring) and GitHub (through
gh). - Identity for the remote daemon: an OIDC issuer with RS256 or EdDSA keys; the guides describe an Authentik application. The guides mark those Authentik steps as not yet tried against a live Authentik.
- Platforms: wherever Loams Desktop runs (Linux, macOS, Windows), and, for the remote daemon, the
ghcr.io/ostrium-labs/loams-agentdimage.
No harness CLI version matrix is published in the repository, so this page does not list one.
How it compares
These tools change quickly. This compares what each is known for, not a feature census.
| Loams Studio | Cursor | OpenAI Codex (app, CLI) | Claude Code | T3 Code | |
|---|---|---|---|---|---|
| What it is | An ADE mode in the Loams console, on a local or remote daemon | A full AI code editor, forked from VS Code | OpenAI's coding agent, as a CLI, IDE extension and cloud tasks | Anthropic's coding agent, primarily a terminal CLI | An open-source GUI over coding agent CLIs |
| Agents | Many vendors' CLIs, side by side | Cursor's own agent over several model providers | OpenAI models | Anthropic models | Several agent CLIs, through its own server |
| Editing code yourself | No editor; hands off to yours | A complete editor | Through your editor or the IDE extension | Through your editor | No editor |
| Isolation | A git worktree per thread | Worktrees for parallel agents in recent versions; remote background agents | Cloud tasks run in sandboxed containers; the CLI has sandbox modes | Runs in your checkout; you manage worktrees | A new worktree or the local checkout, per thread |
| Parallel attempts compared | Run groups: a file matrix, side-by-side diffs, checks, then merge or PR | Several agents in parallel | Several attempts per cloud task | Not built in | No compare view |
| Licence | Apache-2.0 with MIT portions | Proprietary | The CLI is open source; the service is proprietary | Proprietary | MIT |
Where the others are better today: Cursor is a real editor with a mature extension ecosystem, which Studio deliberately is not. Codex's cloud tasks run without any machine of yours, while Studio needs a daemon you run. Claude Code and Codex are each the first-party home of their own agent and get its features first; Studio drives them through their wires and follows. T3 Code is the more polished single-purpose GUI it was taken from. Where Studio is different: several vendors' agents in one place, compared on the same base commit; the daemon keeps groups, approvals and checkpoints across window restarts; supervision is enforced by the daemon rather than each CLI's settings; and it sits next to your Loams data in the same console.
What is missing and what is planned
- Studio in the browser by default. Behind the instance feature
studiountil SD1 Task 32; no Loams server sets it yet. - A sign-in that issues the agentd token (MT1). Until then the browser's Settings › Agentd hosts asks you to paste an access token per host, held only in that tab's memory.
- End-to-end tests on both editions (SD1 Task 29).
- A per-thread approval switch in the thread header. The mode is set when a thread is created and shown in the header.
- OS-level confinement inside the daemon (landlock, seatbelt, bubblewrap) is an open question (Q815). Supervision is the guard; run a remote daemon in a VM or a container with only your project directories mounted.
- The factory apps in the browser wait for SF1 (D922); on the desktop they live under Studio.
- Settings › Parallel runs is stored but not read yet: the Run in parallel dialog starts at 2 variants and caps at 4.
- Remote harness installs and account logins are not offered to the browser (Q808); do them on the machine.
- Next: §60, Places and Harness Composition (D1087 to D1093, proposed) studies composition rules, what an agent left behind, and durable agent workflows; the console redesign (§59, plan CF1) reuses Studio's T3 Code vendoring for an agent-first home.
How to try it
On the desktop. Install Loams Desktop from the GitHub release (see Loams Desktop and the loams CLI). On the first run it opens in Studio. If the daemon is not running, Studio's home offers Start. Add a project with Add beside Projects (it opens your folder picker; pick a git repository), click the project and type a prompt in New thread. The first run gets its own worktree. Switch modes with Mod+Shift+M.
To keep the old unattended behaviour for new threads, set it explicitly in the daemon's config.toml:
[approvals]
default_mode = "full-access"A remote daemon for the browser. On a machine you control (a dev box, a VM or a container), with your harness CLIs installed:
loams-agentd serve --remote \
--listen 0.0.0.0:8443 \
--tls-cert /etc/loams-agentd-tls/cert.pem --tls-key /etc/loams-agentd-tls/key.pem \
--issuer https://auth.example.com/application/o/loams-agentd/ \
--audience loams-agentd \
--subject <your sub> \
--allow-origin https://console.example.com \
--config /var/lib/loams-agentd/config.tomlEvery flag is required except --allow-origin, which you repeat per console origin; a missing one exits 4 and names it. Then, in Studio's Settings › Agentd hosts, add the host's https:// URL and paste an access token. Reach the machine over your tailnet or a single TLS port. Remember that browser Studio needs the studio instance feature.
Without a daemon. The console's dev server shows Studio with sample data and runs no agents:
pnpm install --frozen-lockfile # from the repository root
cd web && pnpm devOpen the dev server's /ui/?desktop address for the desktop edition with a fake daemon.
Source
- Design: §53 Loams Studio, §50 the desktop daemon, §39 the software factory, the vendoring inventory, §59, §60
- Plans: SD1 Loams Studio, DD1 the desktop daemon, CF1 the console foundation
- Guides:
docs/guides/studio - Code:
crates/loams-agentd,crates/loams-agentd-connect,crates/loams-agentd-harness,web/packages/studio-ui,web/plugins/studio