Products
Experimental

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).

ReleaseWhat 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, changedNew 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

One daemon, many worktrees. The desktop reaches loams-agentd through Electron main, which adds the owner token; the browser reaches a remote daemon over TLS with an OIDC access token. A run group fans one prompt out to N variants and brings them back to one compare view.

Everything Studio does goes through one backend, loams-agentd (D906). The pieces:

PieceWhereWhat it does
The console host and the Studio pluginweb/packages/console-host, web/plugins/studioThe mode switcher, Studio's routes under /studio, its pages
@loams/studio-uiweb/packages/studio-uiComponents vendored from T3 Code and rebuilt on @loams/ui tokens, Base UI and connect-query
@loams/agentd-clientweb/packages/agentd-clientThe Connect transports (desktop and remote) and the transcript reducer that Studio and the docked agent panel share (D917)
loams-agentdcrates/loams-agentdThe per-user daemon: lock, discovery, token, roles, config, the run and serve commands, service install
loams-agentd-connectcrates/loams-agentd-connectThe loams.agentd.v1 Connect surface: a loopback listener, and the remote TLS listener with OIDC and CORS
loams-agentd-sessions, -store, -proto, -rpccrates/The sessions engine, its SQLite store, the wire types and the ndjson RPC
loams-agentd-harnesscrates/loams-agentd-harnessOne 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:

HarnessHow the daemon drives it
Claude CodeIts stream-json protocol
CodexThe Codex app-server JSON-RPC
opencodeIts own HTTP and SSE server protocol (opencode serve)
PiIts native JSONL RPC
Devin, Grok, Hermes, AntigravityACP (devin acp, grok agent stdio, hermes acp, Google's agy_acp_server)
Loams BotACP, 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.

HarnessIn a supervised thread
Claude CodeRuns with --permission-mode default, never bypass; each permission request waits
CodexApproval policy on-request with the thread's own sandbox; command and file-change approvals wait
opencodeEach permission.asked waits
ACP agentsEach tool permission waits; asks during session setup are rejected
PiCannot 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 = 8

max_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 --remote opens a separate TLS listener, off by default. It accepts only access tokens from the configured issuer with the audience loams-agentd and the one subject the daemon was enrolled for, sends CORS headers only to configured console origins, and serves only loams.agentd.v1 and 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.

DecisionWhatWhy, and what was rejected
D900, D901Studio is a mode of the one console, selected by a manifest modes fieldOne shell: moving between running your data and building with agents needs no second app or second sign-in. A separate app was rejected
D906loams-agentd is Studio's only backend for code workDesktop 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
D907A Connect surface, loams.agentd.v1, beside the ndjson RPCThe 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
D908Desktop calls go through Electron main, which adds the tokenThe renderer must never hold the daemon's token (D788, D789 hold)
D909, D921A separate remote TLS listener with OIDC, one subject per daemonThe loopback listener keeps refusing browsers (D788); a multi-user hosted daemon is out of scope
D904Parallel runs are run groups owned by the daemonA group survives the window closing; a person always picks the winner
D911Real approvals, with a supervised modeBefore Studio, every harness driver auto-approved tool calls; only genuine questions reached the person
D912, D913, D915Vendor T3 Code's UI into @loams/studio-ui with provenance and MIT headers; treat it as a forkOwn the code on our UI stack; do not embed T3 Code's app, server, Effect runtime or WebSocket protocol
D914An npm licence gateThere 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, D919First-party plugins only; untrusted content renders as textA third-party frame never reaches the daemon; model, tool and diff output never becomes raw HTML
D920Terminals on xterm.jsVendoring 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-agentd image.

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 StudioCursorOpenAI Codex (app, CLI)Claude CodeT3 Code
What it isAn ADE mode in the Loams console, on a local or remote daemonA full AI code editor, forked from VS CodeOpenAI's coding agent, as a CLI, IDE extension and cloud tasksAnthropic's coding agent, primarily a terminal CLIAn open-source GUI over coding agent CLIs
AgentsMany vendors' CLIs, side by sideCursor's own agent over several model providersOpenAI modelsAnthropic modelsSeveral agent CLIs, through its own server
Editing code yourselfNo editor; hands off to yoursA complete editorThrough your editor or the IDE extensionThrough your editorNo editor
IsolationA git worktree per threadWorktrees for parallel agents in recent versions; remote background agentsCloud tasks run in sandboxed containers; the CLI has sandbox modesRuns in your checkout; you manage worktreesA new worktree or the local checkout, per thread
Parallel attempts comparedRun groups: a file matrix, side-by-side diffs, checks, then merge or PRSeveral agents in parallelSeveral attempts per cloud taskNot built inNo compare view
LicenceApache-2.0 with MIT portionsProprietaryThe CLI is open source; the service is proprietaryProprietaryMIT

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 studio until 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.toml

Every 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 dev

Open the dev server's /ui/?desktop address for the desktop edition with a fake daemon.

Source

On this page