Skip to content

Mods: your own interface inside T3 Code, the same with every model - #1

Open
leorivastech wants to merge 1 commit into
mainfrom
feature/mods
Open

leorivastech wants to merge 1 commit into
mainfrom
feature/mods

Conversation

@leorivastech

Copy link
Copy Markdown
Owner

This pull request lives in my own fork. It is not a request to merge anything into T3 Code.
It is the public, dated record of work I designed and built myself, with the code in this branch. I first described it in the Ideas section of the T3 Code repository on October 6, 2026 (discussion 16720). If the T3 Code team ever wants it, it is theirs to take; until then it stays here.

A mod is a small folder of local code that adds interface to the app: a pane beside the chat, a bar above the composer, a status line, a slash command. It belongs to no provider, so the same mod works in a Claude thread, a GPT thread, or a thread where nothing has been sent yet, and running one costs no tokens.

This is built and working. It runs in the app installed on my machine, and the branch sits on upstream main. It is not a sketch looking for someone to write it.

Where a mod draws

Recording of the real UI, 1:39:

t3-mods-demo-v3.mp4

Why

I kept wanting small things next to the chat that are mine and not the model's: the state of my open pull requests, what is playing, something to do while an agent works. Today that means another window. A provider's own plugin system does not solve it either, because it stops existing the moment I switch models.

What I have working

Three mods I use, all in the recording:

  • /prs opens a pane with my open-source pull requests, read from a local file I already keep.

    PR board

  • /music puts a bar above the composer that shows and controls the player on the machine.

    Music bar

  • /fight is a one-key real-time game in a pane, against the CPU or against another person through a small server. Escape returns to the chat; the command returns to the game.

    Fight
    Two players

And the point of it:

  • Any model, or none. Mod commands appear in the slash menu with every provider and run locally. Nothing is sent to the model.

    Another model, same mod

  • Any model can write one. Agents get three MCP tools: mods_guide (how to write a mod), mods_list (what loaded, and the error if one did not) and mods_run. To check that the guide is enough, I asked three different models (Claude Haiku, Gemini and Grok) to write a mod with nothing but it. Each one produced a mod that loaded and worked.

How a mod is made

One folder under ~/.agents/mods/, two files:

~/.agents/mods/hello/
  mod.json    { "name": "hello", "description": "..." }
  mod.tsx     exports register(on)
export function register(on) {
  on("command.run", { command: "hello" }, async ($) => {
    await $.ui.open({ id: "hello", title: "Hello" })
  })
  on("ui.render", { component: "Pane", requestId: "hello" }, async ($, e) => {
    const { Box, Text } = $.ui.resolve(e)
    return <Box><Text bold>Hello from a mod</Text></Box>
  })
}

Saving a file reloads it. A mod reaches the outside only through $: files in the project, running a command, fetching a URL, a small store of its own. For what elements cannot draw, a pane can hold a page of the mod's own in a sandboxed iframe, with a canvas and the keyboard; that is how the game works.

How it fits the codebase

  • packages/mod-engine: the engine, with no dependency on Effect or on the rest of T3 Code. It runs mods in a process of its own, so a mod that loops forever is noticed, killed and restarted without freezing the app. A second freeze turns mods off with a notice.
  • One server service (apps/server/src/mods): one session per thread, shut down a few minutes after the last window leaves. It also tells mods what the agent does (a turn starting, a tool running, a turn ending), mapped from the orchestration events, so it is the same for every adapter.
  • Contracts and two WebSocket methods (mods.subscribe, mods.request), authorized like the rest.
  • One MCP toolkit, declared through McpToolAccess like the others.
  • Web: the pane tab in the right panel, the band above the composer, and the slash menu entries.

It is 61 files and about 9,950 added lines, tests included, on top of upstream main as of October 7, 2026.

What it does not do

  • Mobile. Mods are drawn on web and desktop only. The mobile app does not show them.
  • Remote and tunnel. Mods run on the environment's machine and reach clients over the existing WebSocket, so it should work, but I have only used it locally. Untested.
  • No marketplace, no install command. A mod is a folder you put there, or ask an agent to write.
  • A mod is not sandboxed from your machine. It runs with your permissions, like any script you run. Only the page inside a pane is sandboxed from the app. The guide says so.
  • A mod cannot redraw the chat, change the composer, or stop the agent. It only watches. I left those out to keep mods small and identical across providers.

What it costs

Measured against the same commit without mods, in the web app and in the engine process alone:

  • With no mod installed, nothing starts: no process, no work. The engine starts when the first mod appears.
  • With mods installed, the only cost is the engine process: about 92 MB, 98 MB with twenty threads open, and it stays under 160 MB after opening and closing three hundred threads. A new thread costs it 9 ms.
  • Opening a thread, the slash menu, typing in the composer, idle load and websocket traffic measure the same with and without mods.
  • A mod that changes its state a thousand times a second is redrawn at most 25 times a second and does not slow other threads. A mod that loops forever is killed and restarted.

Not measured: a real GPU (the measurements ran in a headless browser), remote and tunnel connections.

Where the code is

  • feature/mods: upstream main plus this change, one commit.
  • mods-telegram: the same plus my Telegram channel, which is what I run.

Engine tests 26, server 24, web 329, all passing on this branch.

A mod is a small folder of local code under ~/.agents/mods that adds
interface to T3 Code: a pane beside the chat, a band above the composer,
a status line, toasts and slash commands. It belongs to no model or
provider and costs no tokens to run.

Mods run in a process of their own, so one that loops forever is stopped
and restarted without freezing the app. They hear what the agent does
(turns and tool calls) the same way for every provider, and agents reach
them through three MCP tools: mods_guide, mods_list and mods_run.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Oct 7, 2026
@github-actions

Copy link
Copy Markdown

Thread transfer impact

⚠️ The latest CI run did not produce a thread transfer result for 98d234c.

This comment will update automatically after the next completed run.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant