# Thomas — project context

> Get Thomas on the wire.

Thomas is the **dynamic harness for your agent fleet**. An agent
harness is everything around the model that makes it reliable — tools,
context, guardrails, the loop, the verify step. Codex is a harness
for one model; Thomas is the harness for your whole fleet of
(already-harnessed) agents — Codex, OpenClaw, Cursor, Codex,
Hermes, anything that calls an LLM or speaks MCP.

Thomas wraps the wire, not the agent: `thomas wire` detects what you run
and generates the wrapping on the fly — no rewrites. Free, it makes the
fleet **legible** — capture, decode, replay every action, score outcomes
with the T metric. Paid, it makes the fleet **enforceable** — guardrails
and policy you Control on your own agents, and Govern across a team's
agents (including ones employees bring themselves).

This file orients Codex (and other AI assistants) to the
project. For the full design, read `.strategy/` (private, gitignored)
— start with `.strategy/architecture.md`. The public `docs/` directory
only contains `cli.md` (the user-facing CLI reference).

## Status

v0.4 — first public release (`npm publish` debut is v0.4.0).
Solo-built and used daily by the author. Tested against real
Codex / OpenClaw / Codex / Hermes traffic.

## Naming

| Thing | Name |
|---|---|
| Project / brand | Thomas |
| Domain | `openthomas.com` |
| GitHub repo (planned) | `openthomas-com/thomas` |
| npm scope | `@openthomas` |
| npm package | `@openthomas/thomas` |
| Main binary | `thomas` |
| MCP stdio wrapper bin | `thomas-tap` |
| Daemon subcommand | `thomas daemon` |
| Config dir | `~/.thomas/` |
| Trace dir | `~/.thomas/wire/traces/` |
| Backups dir | `~/.thomas/backups/` |
| Logs dir | `~/.thomas/logs/` |
| Default UI URL | `http://localhost:9877` |
| macOS launchd label | `com.openthomas.daemon` |
| Free edition | Thomas (the legible harness) |
| Paid editions | Thomas Personal · Solo · Team · Enterprise |
| Deferred edition | Thomas Family (separately sequenced) |

The user-facing brand is **Thomas** (singular). "Get Thomas on the
wire" is the slogan — Thomas is the harness your agent fleet wears. The
sub-headline positions us: *the dynamic harness for your agent fleet*.
Marketing leads with the benefit — **reliability** — and explains it
with the mechanism — the harness.

`openthomas.com` is the canonical URL; the `Open` prefix communicates
the OSS distribution, but the product name is just Thomas. `wire` is
preserved as **architectural terminology** (URL paths under `/wire/`,
storage under `~/.thomas/wire/`, slogan) — it's load-bearing in the
code, not a brand asset.

## Repo layout

```
thomas/                       (repo: openthomas-com/thomas, planned)
├── AGENTS.md                 (this file)
├── README.md                 (public face — see story + journey table)
├── PRIVACY.md                (data handling contract)
├── LICENSE                   (MIT)
├── docs/
│   ├── cli.md                (every CLI command — user-facing reference)
│   └── thomas.md             (canonical Thomas metric spec — citation target)
├── .strategy/                (PRIVATE, gitignored — design + commercial)
│   ├── architecture.md       (system overview, four-layer model)
│   ├── data-model.md         (AgentPacket schema, storage)
│   ├── decisions.md          (technical decisions)
│   ├── decisions-paid.md     (commercial-tier decisions)
│   ├── distribution.md       (form factor, npm publish)
│   ├── protocols.md          (per-protocol decoder specs)
│   ├── runtime-modes.md      (per-agent apply-step semantics)
│   ├── verification.md       (open items needing ground-truth)
│   ├── roadmap.md            (versioned shipping plan)
│   ├── roadmap-full.md       (extended roadmap with paid timeline)
│   ├── positioning.md        (free vs paid, comparison, target user)
│   ├── founder-journey.md    (Idea→MVP→Launch→Scale × free/paid)
│   ├── agents/               (per-agent detector specs)
│   └── The-Founders-Playbook-*.pdf  (Anthropic reference)
├── references/               (third-party projects studied — gitignored)
├── internal/ui/              (Vite + React, builds to packages/thomas/ui-dist)
└── packages/thomas/          (the npm package: @openthomas/thomas)
```

The **openthomas.com marketing site** and the (planned) **paid cloud
backend** live in a separate closed-source repo:
[`openthomas-com/thomas-cloud`](https://github.com/openthomas-com/thomas-cloud).
This split is intentional — code that runs on a user's machine stays
here under MIT, no telemetry; everything that runs on our infrastructure
or is part of the paid product line lives there.

## Tech stack (locked)

| Layer | Choice | See decision |
|---|---|---|
| Runtime | Node.js 22+ LTS | .strategy/decisions.md / 0002 |
| Language | TypeScript 5+ | 0005 |
| HTTP | Hono + `@hono/node-server` | 0005 |
| CLI parsing | commander | 0005 |
| Error handling | `@praha/byethrow` (Result type) | 0005 |
| Schemas | Valibot | 0005 |
| Storage | better-sqlite3 + Drizzle ORM | 0006 |
| Build | tsdown | 0005 |
| UI | React + Vite (bundled into daemon as static assets) | 0001 |
| JSON / JSON5 edit | `jsonc-parser` (Microsoft) | 0007 |
| YAML edit | `yaml` (eemeli) — Document API to preserve comments | 0007 |
| Test | TBD (vitest or `bun:test`) | — |

## Conventions

- **Language**: All code, comments, commits, docs, UI strings → **English**.
  Non-English contributors must be able to read everything.
- **Style**: Default to no comments. Add `// why:` comments only when the
  intent is non-obvious. Never narrate WHAT code does.
- **Files**: kebab-case filenames; camelCase TS identifiers; PascalCase types.
- **Commits**: Conventional Commits.

## Mental model

Thomas is the **dynamic harness for your agent fleet**. Lead with the
benefit — **reliability** — and explain it with the mechanism — the
harness.

An agent harness (Tejas Kumar's framing; Mitchell Hashimoto's "harness
engineering") is everything around the model that grounds it in a stable
environment: tool registry, model, context management, guardrails, the
agent loop, the verify step. The harness — not the rented black-box
model — is what determines reliability.

Codex is a harness for **one model**. Thomas is the harness for a
**fleet of (already-harnessed) agents** — a loop around their loops.
Different unit, different layer; one nests in the other. This precise
claim must lead, or Thomas reads as "just another Codex."

Three verbs, in order of how much harness you have turned on:

- **See** (free) — the legible harness. Capture, decode, replay every
  action; score outcomes with the T metric (T *is* the harness's verify
  step, as a fleet-wide number). Local-first, zero accounts.
- **Control** (paid · self-harness) — you enforce on your *own* agents:
  guardrails, policy, budget caps, model routing. Editions: Personal,
  Solo.
- **Govern** (paid · governed-harness) — an authority enforces across a
  *group's* agents, including ones members bring themselves (BYOA —
  bring your own agent). Editions: Team, Enterprise; Family is the
  household instance, separately sequenced.

The harness is generated on the fly: `thomas wire` detects what you run
and wraps it — no rewrites, no framework. Thomas wraps the **wire**, not
the agent, so it can govern agents it does not own.

Capture → decode → store → present is the free pipeline. Local-first,
zero accounts. The primary paying user is the **non-coding solo founder**
orchestrating a fleet to ship product. Engineers will DIY this; they are
not the paying customer. See `.strategy/positioning.md`.

Avoid drifting into "another LLM observability dashboard" (Langfuse /
Helicone / LangSmith). The differentiator is **agent-runtime harnessing**
— tool calls, MCP, file ops, guardrails, the full action graph — not
prompt/response logs. `wire`, `flight recorder`, and `instrument panel`
survive as component-level vocabulary (the wire is what Thomas taps; the
recorder is the loop's trace; the instrument panel is the UI) — but the
top-level category is **harness**. `proxy` is an architecture word only,
never positioning.

## Working with this codebase

### Read first

1. `.strategy/architecture.md` — system overview, four-layer model
2. `.strategy/runtime-modes.md` — why each agent is wired differently
3. `.strategy/agents/<name>.md` — exact config paths and rewrite logic
4. `.strategy/data-model.md` — `AgentPacket` schema, storage, routing
5. `.strategy/positioning.md` — free vs paid line, target user

### Gotchas

- **Per-agent runtime modes.** Don't assume "rewrite config = done." For
  long-running agents (OpenClaw, Hermes), wiring also needs an **apply**
  step (restart or paste). See `.strategy/runtime-modes.md`.
- **Backup contract.** `thomas unwire` reverses exactly the edits Thomas
  recorded in `~/.thomas/backups/manifest.json` — a surgical
  reverse-replay (`cli/backup/restore.ts`), not a byte-exact whole-file
  restore. The manifest is the source of truth; edits the agent made to
  its own config after wiring are preserved, and a pointer whose value
  changed since wiring is left as-is and reported. `--force` (and legacy
  v1 manifests) fall back to the whole-file snapshot restore. Never edit
  in-place. Always: write to `.tmp`, fsync, rename.
- **Path-based routing.** The proxy receives requests at
  `/wire/<agent>/<provider>/<rest>`. Strip the prefix, look up the
  upstream from `~/.thomas/wire/routes.json`. Concatenate as strings —
  do NOT use `new URL(restPath, base)` because absolute `restPath`
  would clobber the base's path component.
- **Format-preserving edits.** Users' config files have comments and
  idiosyncratic formatting. Use AST-level editors (`jsonc-parser`,
  `yaml` Document API). Never parse-and-restringify.
- **`thomas-tap` cold start.** It's spawned per MCP server. Keep its
  bundle small — no heavy daemon code loaded.
- **Verification items**: see `.strategy/verification.md` for things
  believed but not physically tested. Don't rely on them in code until
  verified.
- **`wire` is architecture, not branding.** URL paths (`/wire/...`),
  storage paths (`~/.thomas/wire/...`), and the slogan all use `wire`
  — don't refactor it away when touching code.

### `references/`

Contains third-party projects studied (Portkey gateway, LiteLLM,
cc-switch, hermes-agent, openclaw, Codex-router, ccusage, …).
**Not dependencies** — never import from them, never add to package.json.
They are frozen learning material. Don't refactor them.

The full inventory of what each reference taught lives in
`.strategy/` (private); the agent specs in `.strategy/agents/` cite
the relevant findings where they're directly used.

## Do not

- Add a Tauri / Electron / native GUI app. Thomas is CLI + daemon +
  browser UI. Decision 0001 is permanent.
- Switch the published artifact's runtime to Bun. Bun is fine for dev
  tooling; the npm package must run on stock Node. Decision 0002.
- Rename the npm package away from `@openthomas/thomas` or the
  binaries away from `thomas` / `thomas-tap` once published — these
  are the user-facing contract.
- Add features beyond what the current commit needs.
- Write README files or docstrings unless explicitly asked.
- Refactor `references/`.
- Move design docs back into the public `docs/` directory. Public
  `docs/` is for "how to use the code" only. Everything else lives in
  `.strategy/`.
- Use `git push --force`, amend already-pushed commits, or skip
  pre-commit hooks without explicit user approval.
- **Send any data about the user or their usage anywhere besides the
  upstream the user's agent already calls.** No telemetry, no crash
  reports, no license-check calls, nothing that contacts an
  `openthomas.com` / Thomas / Anthropic server. The privacy contract in
  `PRIVACY.md` is load-bearing for trust. If a feature legitimately
  needs to send data about the user, it must be off by default,
  documented under the next version's section of `PRIVACY.md` with
  exactly what is sent and where, and called out in the release notes.
  - **One carve-out (v0.4+): the update version check.** A daily
    plain GET to the public npm registry (`registry.npmjs.org`) to see
    if a newer Thomas exists is permitted. It carries no user data, no
    identifiers, never contacts our servers, and is fully disableable
    (`updateCheck: false`). It is documented in the v0.4 section of
    `PRIVACY.md`. This is the *only* sanctioned unsolicited outbound
    call — the bar for anything else is unchanged.
