trantor 0.17.48 → 0.17.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Trantor — the hub-world for AI agent crews: live message bus, presence, project Kanban/flow board + context-handoff for independent AI coding agents (Claude, Codex, Gemini, …)",
9
- "version": "0.17.48"
9
+ "version": "0.17.50"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "trantor",
14
14
  "source": "./",
15
15
  "description": "The hub-world for AI agent crews. Say \"fire up the crew\" and Claude becomes the architect: a plan-aware Advisor routes the work (solo / cheap inline calls / live crew of Codex, GLM, Kimi & DeepSeek in their own terminal windows), a Kanban/flow command center with a testing gate tracks it, and an economics brain (Scrooge) keeps the receipts. Includes the relay MCP, a SessionStart auto-discovery hook, and a PreCompact context-handoff so a fresh session can take over a full window instead of compacting.",
16
- "version": "0.17.48",
16
+ "version": "0.17.50",
17
17
  "author": {
18
18
  "name": "Sasha Bogojevic"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trantor",
3
- "version": "0.17.48",
3
+ "version": "0.17.50",
4
4
  "description": "Trantor — the hub-world for AI agent crews: live message bus, presence, project Kanban/flow board + crew orchestration for independent AI coding agents (Claude, Codex, Gemini, Kimi, DeepSeek)",
5
5
  "mcpServers": {
6
6
  "relay": {
package/README.md CHANGED
@@ -12,14 +12,14 @@
12
12
  ### The hub-world for AI agent crews.
13
13
 
14
14
  **One Advisor decides how your work runs — solo, cheap inline calls, or a live crew of
15
- Claude Code, Codex, GLM, Kimi & DeepSeek in their own terminal windowsrouted by your
16
- actual plans, supervised on a live + historical board you can scroll back through, learning
17
- from every failure.**
15
+ Claude Code, Codex, GLM, Kimi, DeepSeek or *any model you bring* in their own terminal
16
+ windows, routed by your actual plans, supervised on a live + historical board you can scroll
17
+ back through, learning from every failure.**
18
18
 
19
19
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
20
20
  ![Node](https://img.shields.io/badge/node-%E2%89%A518-339933?logo=node.js&logoColor=white)
21
- ![Agents](https://img.shields.io/badge/crew-Claude%20%C2%B7%20Codex%20%C2%B7%20Gemini%20%C2%B7%20Kimi%20%C2%B7%20DeepSeek-D97757)
22
- ![Tests](https://img.shields.io/badge/tests-80%2F80-2DD4BF)
21
+ ![Agents](https://img.shields.io/badge/crew-Claude%20%C2%B7%20Codex%20%C2%B7%20GLM%20%C2%B7%20Kimi%20%C2%B7%20DeepSeek%20%C2%B7%20%2BBYOM-D97757)
22
+ ![Tests](https://img.shields.io/badge/tests-passing-2DD4BF)
23
23
 
24
24
  </div>
25
25
 
@@ -42,6 +42,12 @@ claude plugin install trantor
42
42
  That's it. (Prefer source? `git clone https://github.com/sashabogi/trantor && cd trantor &&
43
43
  npm install && bash deploy/setup.sh` — identical result.)
44
44
 
45
+ **Recommended companion (macOS):** [**cmux**](https://cmux.com) — `brew install --cask cmux` — a
46
+ native terminal built for running multiple AI agents. Trantor groups each crew into one cmux workspace
47
+ with live per-seat status in the sidebar and project-scoped teardown. See
48
+ *[Grouped crews in cmux](#grouped-crews-in-cmux-recommended)*. Optional — crews also run in tmux or plain
49
+ Terminal windows.
50
+
45
51
  ## What gets installed — footprint & safety
46
52
 
47
53
  Trantor is a **local-first multi-agent orchestrator with a built-in cost router** — not a cloud
@@ -65,9 +71,10 @@ on your machine, so you (or an agent installing it for you) can see the whole fo
65
71
  **SubagentStop** (record each sub-agent's notional cost on the board).
66
72
 
67
73
  **What it does *not* do:** no cloud, no accounts, no telemetry, nothing phones home; it never uploads
68
- your code or keys; it doesn't touch other CLIs' credentials — Codex, Gemini, Kimi and DeepSeek are
69
- ones *you* already installed and signed into, and Trantor just coordinates them locally. The optional
70
- API keys in `~/.agent-bus/.env` are used only to call the cheap models *you* opted into for routing.
74
+ your code or keys; it doesn't touch other CLIs' credentials — Codex, Kimi, DeepSeek, GLM (and any
75
+ provider you bring) are ones *you* already installed and signed into, and Trantor just coordinates
76
+ them locally. The optional API keys in `~/.agent-bus/.env` are used only to call the models *you*
77
+ opted into for routing.
71
78
 
72
79
  **Remove everything, anytime:**
73
80
  ```bash
@@ -134,12 +141,16 @@ project takes over with a full window (and a PreCompact hook does this automatic
134
141
  (`solo | scrooge | crew | hybrid`), a routing table with a **reason per package**, why
135
142
  that many seats ("seats follow the work, not the install list"), and a real-money estimate
136
143
  with quota-pool accounting. You say go.
137
- 2. **Windows open.** `trantor up codex kimi deepseek:deepseek glm:zai-coding-plan` spawns one titled
138
- terminal window per agent. `agent:model` pins a model; `agent:provider --difficulty hard`
139
- picks the **best live model** for the work at spawn (capability × cost), enumerated from the
140
- CLI itself never a guessed endpoint. **Serialized and then verified on the bus** the
141
- launcher ends with "crew verified" or names the no-shows loudly. The orchestrator never gets
142
- a green lie.
144
+ 2. **Windows open.** `trantor up codex kimi deepseek:deepseek glm:zai-coding-plan` spawns the crew in
145
+ visible terminals grouped into **one [cmux](https://cmux.com) workspace per project** when cmux is
146
+ installed (seats tiled, live status in the sidebar; see *[Grouped crews in cmux](#grouped-crews-in-cmux-recommended)*
147
+ below), else tmux, else one titled Terminal window per seat. The seats aren't a fixed list — they're **whatever you've got**:
148
+ the native CLIs (Codex, Kimi) plus *any* provider wired through OpenCode (DeepSeek, GLM, and
149
+ **OpenRouter's hundreds of models, or a custom endpoint you bring** — see *Bring your own
150
+ model* below). `agent:model` pins a model; `agent:provider --difficulty hard` picks the
151
+ **best live model** for the work at spawn (capability × cost), enumerated from the provider
152
+ itself — never a guessed endpoint. **Serialized and then verified on the bus** — the launcher
153
+ ends with "crew verified" or names the no-shows loudly. The orchestrator never gets a green lie.
143
154
  3. **Work flows over the bus.** Contracts arrive as messages; each agent owns its own files;
144
155
  coordination happens in <280-char messages you can read on the dashboard. Crew members
145
156
  live under a **runner**: the CLI works one turn and exits, the runner long-polls the bus
@@ -156,6 +167,33 @@ project takes over with a full window (and a PreCompact hook does this automatic
156
167
  6. **It learns.** Failures become lessons (`relay_lesson`), stored on the hub and **injected
157
168
  into every future crew's prompts** — global or per-CLI. Your crew gets smarter every run.
158
169
 
170
+ ### Grouped crews in cmux (recommended)
171
+
172
+ Trantor runs crews in visible terminals so nothing dies or fails silently. On macOS it prefers
173
+ **[cmux](https://cmux.com)** — a native, Ghostty-based terminal *built for managing multiple AI coding
174
+ agents* — and falls back to **tmux**, then plain Terminal windows.
175
+
176
+ With cmux, each crew becomes **one workspace tab per project**, its seats tiled inside, every pane
177
+ labeled `<agent> · <project>`, and each seat pushes its **live state into cmux's sidebar** —
178
+ `building` (blue) while a turn runs, `idle` when it's waiting, `error`/`down` (red) on a failed turn —
179
+ plus a per-crew progress pill. Teardown is **project-scoped**: `trantor down` closes only *this*
180
+ project's workspace, so when you run several sessions each driving its own crew, one session's teardown
181
+ can't nuke another's. `trantor down <agent>` drops a single seat; `trantor down --all --yes` tears down
182
+ every project's crew.
183
+
184
+ **One-time setup:**
185
+ - Install cmux — `brew install --cask cmux` (or grab it from **[cmux.com](https://cmux.com)**).
186
+ - Trantor drives cmux over its control socket, which is off to outside processes by default. Enable it in
187
+ `~/.config/cmux/cmux.json` (cmux auto-reloads):
188
+ ```json
189
+ { "automation": { "socketControlMode": "allowAll" } }
190
+ ```
191
+ This lets local processes drive your terminals. Leave it on the default `cmuxOnly` and Trantor
192
+ automatically falls back to AppleScript (same grouped layout, minus the native sidebar status).
193
+ - The `cmux` CLI is symlinked onto your PATH on your first `trantor up`.
194
+
195
+ No cmux or tmux? Crews still work — one titled Terminal window per seat, still project-scoped teardown.
196
+
159
197
  ## The dashboard — `trantor ui`
160
198
 
161
199
  A live command center at `http://127.0.0.1:4477`, grouped by **project** — and a *durable,
@@ -202,6 +240,39 @@ ledger; Trantor turns that into decisions:
202
240
  - **Fractal delegation** (`relay_scrooge`): the architect *and* crew members push stateless
203
241
  grunt work to cheap models, with ledger receipts.
204
242
 
243
+ ## Bring your own model (BYOM)
244
+
245
+ The crew roster isn't a hardcoded list — it's **derived from what you've configured**. Trantor
246
+ ships built-in seats for Codex and Kimi (native CLIs) and DeepSeek + GLM (via OpenCode), but
247
+ **any** provider OpenCode can reach becomes a first-class seat with its own identity on the
248
+ board — no code change, no waiting for us to add it.
249
+
250
+ - **OpenRouter is the on-ramp.** One key fronts **hundreds of models** (incl. vendors that have
251
+ no CLI of their own). Drop `OPENROUTER_API_KEY` in `~/.agent-bus/.env` and `openrouter` is a
252
+ seat: `trantor up openrouter` live-selects the best model for the work, or
253
+ `trantor up openrouter:openrouter/<vendor>/<model>` pins one.
254
+ - **Add any provider in one command:**
255
+ ```bash
256
+ trantor provider add <name> --key sk-… --plan api # a provider OpenCode already knows
257
+ trantor provider add acme --key sk-… --base-url https://api.acme.ai/v1 --models acme-lg,acme-sm
258
+ ```
259
+ The second form wires a **custom OpenAI-compatible endpoint** into OpenCode for you (no
260
+ hand-editing config). Then `trantor provider` lists every seat with its availability + tier, and
261
+ `trantor models [<provider>]` browses the live models behind a seat — including the router's pick
262
+ per difficulty so you can see *hard → strong, easy → cheap* at a glance.
263
+ - **It routes by difficulty, not just price.** `scrooge-capabilities` scores a provider's whole
264
+ catalog (real Artificial-Analysis benchmarks where a model matches, a price-tier proxy for the
265
+ long tail) and the router weighs **capability × cost, gated by the work's difficulty** — so hard
266
+ work escalates to a genuinely strong model while easy work stays cheap. Run it once (a weekly
267
+ cron keeps it fresh): `scrooge-capabilities`.
268
+ - **Every seat is its own colleague.** Each opencode-driven provider gets a distinct bus label
269
+ (`glm:<project>`, `openrouter:<project>`, `acme:<project>`), so they never collide and each shows
270
+ up as its own agent on the dashboard.
271
+
272
+ > Brought a key for Inception, a new Japanese model, a niche fine-tune? `trantor provider add` it,
273
+ > `scrooge-capabilities` scores it, and the Advisor routes to it by difficulty — same as the
274
+ > built-ins.
275
+
205
276
  ## Context handoff — sessions that never hit the wall
206
277
 
207
278
  A PreCompact hook writes a rich handoff before Claude Code compacts; a fresh session in the
@@ -231,29 +302,41 @@ rate, not work rate.
231
302
  ## The CLI
232
303
 
233
304
  ```
234
- trantor setup | doctor | connect | profile | up <agents…> | swap <old> <new> | down | ui | advise | hub | watch
305
+ trantor setup | doctor | connect | profile | provider | models
306
+ | up <agents…> | swap <old> <new> | down | ui | advise | hub | watch
235
307
  ```
236
308
 
237
- `trantor up` notes: `agent:model` pins a model (`deepseek:deepseek-v4-pro`); `agent:provider
238
- --task <k> --difficulty <d>` picks the **best live model** for the work at spawn
239
- (`glm:zai-coding-plan --difficulty hard`); spawns are verified on the bus with one retry;
240
- geometry auto-detects the screen you're working on (`CREW_RECT="X,Y,W,H"` to override). `trantor
241
- swap <oldAgent> <newSpec>` replaces an exhausted agent with a live-selected one. `trantor down`
242
- kills crew processes via their ttys and closes windows without macOS "Terminate?" dialogs.
309
+ - **`trantor provider`** `list` every crew seat (built-in + brought) with availability + tier ·
310
+ `add <name> --key [--plan api] [--base-url <url> --models a,b]` to bring any provider (custom
311
+ endpoints are wired into OpenCode for you) · `remove <name>`.
312
+ - **`trantor models [<provider>]`** — browse the live models behind each seat + the router's pick
313
+ per difficulty.
314
+ - **`trantor up`** `agent:model` pins a model (`deepseek:deepseek-v4-pro`); `agent:provider
315
+ --task <k> --difficulty <d>` picks the **best live model** for the work at spawn
316
+ (`glm:zai-coding-plan --difficulty hard`); spawns are verified on the bus with one retry;
317
+ geometry auto-detects the screen you're working on (`CREW_RECT="X,Y,W,H"` to override).
318
+ - **`trantor swap <oldAgent> <newSpec>`** replaces an exhausted agent with a live-selected one.
319
+ - **`trantor down`** kills crew processes via their ttys and closes windows without macOS
320
+ "Terminate?" dialogs.
243
321
 
244
322
  ## Works with any MCP agent
245
323
 
246
- Claude Code, Codex CLI, Gemini CLI, Kimi Code CLI, OpenCode (DeepSeek) are wired by
247
- `trantor connect` automatically (idempotent, backed-up, never overwrites your customizations).
248
- Anything else that speaks MCP: point it at `mcp.mjs` with `RELAY_AGENT=<brand>` — loading the
249
- server auto-registers the session, so presence works before the model says a word.
324
+ Claude Code, Codex CLI, Kimi Code CLI, and **OpenCode** (the universal adapter — DeepSeek, GLM,
325
+ OpenRouter, and any provider you bring) are wired by `trantor connect` automatically (idempotent,
326
+ backed-up, never overwrites your customizations). Anything else that speaks MCP: point it at
327
+ `mcp.mjs` with `RELAY_AGENT=<brand>` — loading the server auto-registers the session, so presence
328
+ works before the model says a word.
329
+
330
+ *(Gemini CLI: Google retired the free seat in 2026, so it's no longer a default crew seat —
331
+ GLM via OpenCode is its replacement. Gemini still works as a Scrooge cheap-model via
332
+ `GEMINI_API_KEY`, and a paid-key holder can still run `trantor up gemini`.)*
250
333
 
251
334
  ## How it works
252
335
 
253
336
  ```
254
- Claude (architect/plugin) codex ─ runner gemini ─ runner kimi ─ runner deepseek ─ runner
337
+ Claude (architect/plugin) codex ─ runner kimi ─ runner opencode ─ runner (DeepSeek·GLM·OpenRouter·BYOM)
255
338
  │ advise/contracts │ one turn, exit; runner long-polls (free) + resumes with context
256
- └───────────┬─────────────┴──────────────┴────────────────┴────────────────┘
339
+ └───────────┬─────────────┴──────────────┴───────────────────────────────────┘
257
340
 
258
341
  hub.mjs ←— plain HTTP + SSE · presence/messages/board/history/lessons/learning/economics
259
342
  (Node built-ins only · state in ~/.agent-bus/bus.json · loopback by default)
@@ -286,10 +369,11 @@ The `relay_*` tool names and the `~/.agent-bus` state dir remain until a later r
286
369
  ## Tests
287
370
 
288
371
  ```bash
289
- npm test # 80 checks: unit + protocol-level scenario drills with mock agents (no LLMs, seconds, $0):
290
- # honest presence + TTL prune, spawn no-shows, the testing gate, bounce trails, lessons,
291
- # /history + backfill, /learning shape, /todos sync, /card detail, advisor decisions across
292
- # plan tiers, deps validation, virgin-machine doctor, and failure-classification drills
372
+ npm test # unit + 120+ protocol-level scenario drills + capability-routing, all with mock agents
373
+ # (no LLMs, seconds, $0): honest presence + TTL prune, spawn no-shows, the testing gate,
374
+ # bounce trails, lessons, /history + backfill, /learning shape, /todos sync, /card detail,
375
+ # advisor decisions across plan tiers, BYOM roster derivation + brought-provider routing,
376
+ # difficulty-aware model selection, deps validation, virgin-machine doctor, failure drills
293
377
  ```
294
378
 
295
379
  ## License
package/bin/cli.mjs CHANGED
@@ -23,7 +23,7 @@ switch (cmd) {
23
23
  case "advise": run("bin/advise.mjs"); break;
24
24
  case "verify": run("bin/crew-verify.mjs"); break;
25
25
  case "up": process.argv.splice(2, 1); spawn("/bin/bash", [join(ROOT, "bin/crew.sh"), "up", ...args], { stdio: "inherit", cwd: process.cwd() }).on("exit", c => process.exit(c ?? 0)); break;
26
- case "down": spawn("/bin/bash", [join(ROOT, "bin/crew.sh"), "down"], { stdio: "inherit", cwd: process.cwd() }).on("exit", c => process.exit(c ?? 0)); break;
26
+ case "down": spawn("/bin/bash", [join(ROOT, "bin/crew.sh"), "down", ...args], { stdio: "inherit", cwd: process.cwd() }).on("exit", c => process.exit(c ?? 0)); break;
27
27
  case "swap": spawn("/bin/bash", [join(ROOT, "bin/crew.sh"), "swap", ...args], { stdio: "inherit", cwd: process.cwd() }).on("exit", c => process.exit(c ?? 0)); break;
28
28
  case "hub": run("hub.mjs"); break;
29
29
  case "watch": run("bin/relay-watch.mjs"); break;
@@ -31,6 +31,7 @@ switch (cmd) {
31
31
  case "agents": run("bin/agents.mjs"); break;
32
32
  case "gates": run("bin/gates.mjs"); break;
33
33
  case "backfill": run("bin/git-backfill.mjs"); break;
34
+ case "sweep": run("bin/sweep.mjs"); break;
34
35
  case "init-hooks": run("bin/init-hooks.mjs"); break;
35
36
  case "balances": case "balance": case "credits": run("bin/balances.mjs"); break;
36
37
  case "recost": run("bin/recost.mjs"); break;
@@ -50,6 +50,22 @@ async function api(path, body) {
50
50
  return r.json();
51
51
  }
52
52
 
53
+ // ---- cmux sidebar integration ----
54
+ // When this runner is inside a cmux surface (CMUX_SURFACE_ID is auto-set there), push its live state into
55
+ // cmux's sidebar for THIS seat. An inside process is allowed by cmux's default cmuxOnly socket mode — no
56
+ // allowAll needed. Fail-silent + short timeout; must never block or slow a turn.
57
+ const CMUX_BIN = process.env.CMUX_BIN
58
+ || (existsSync("/Applications/cmux.app/Contents/Resources/bin/cmux") ? "/Applications/cmux.app/Contents/Resources/bin/cmux" : "cmux");
59
+ const inCmux = () => !!process.env.CMUX_SURFACE_ID;
60
+ function cmuxStatus(value, color, icon = "robot") {
61
+ if (!inCmux()) return;
62
+ try { spawnSync(CMUX_BIN, ["set-status", "trantor", value, "--color", color, "--icon", icon], { stdio: "ignore", timeout: 1500, env: { ...process.env, CMUX_QUIET: "1" } }); } catch {}
63
+ }
64
+ function cmuxLog(message, level = "info") {
65
+ if (!inCmux()) return;
66
+ try { spawnSync(CMUX_BIN, ["log", String(message).slice(0, 200), "--level", level], { stdio: "ignore", timeout: 1500, env: { ...process.env, CMUX_QUIET: "1" } }); } catch {}
67
+ }
68
+
53
69
  // ---- per-CLI invocation (first turn vs resume turn). {P} = prompt file path ----
54
70
  // CREW_MODEL env pins the model: each CLI gets its own flag via {M} (empty when unset).
55
71
  let MODEL = process.env.CREW_MODEL || "";
@@ -119,6 +135,7 @@ async function reportFailure(exit, trigger) {
119
135
  ? `🛑 ${SESSION} DOWN — ${consecFails} consecutive failures (${reason}, exit ${exit})${hint}`
120
136
  : `⚠️ ${SESSION} turn FAILED (${trigger}, exit ${exit} · ${reason})${hint}`;
121
137
  await api("/send", { from: SESSION, to: "all", text, project: PROJ }).catch(() => {});
138
+ cmuxStatus(down ? "down" : "error", "#ef6a6a", "alert"); cmuxLog(`turn failed: ${reason} (exit ${exit})`, "error");
122
139
  log(`\x1b[31mreported failure to bus: ${reason} (exit ${exit})\x1b[0m`);
123
140
  }
124
141
 
@@ -127,6 +144,7 @@ async function reportHealthy() {
127
144
  consecFails = 0;
128
145
  await api("/register", { session: SESSION, project: PROJ, status: `active in ${PROJ}` }).catch(() => {});
129
146
  await api("/send", { from: SESSION, to: "all", text: `✅ ${SESSION} recovered`, project: PROJ }).catch(() => {});
147
+ cmuxStatus("ok", "#14b8a6", "check");
130
148
  }
131
149
 
132
150
  let sid = "";
@@ -142,6 +160,7 @@ function runTurn(prompt, isFirst, trigger = "kickoff") {
142
160
  const envs = [join(homedir(), ".agent-bus", ".env"), cli.env].filter(f => f && existsSync(f));
143
161
  for (const f of envs.reverse()) cmd = `set -a; source ${f}; set +a; ${cmd}`; // ~/.agent-bus/.env wins
144
162
  log(`turn starting (${isFirst ? "fresh session" : "resume"})${MODEL ? ` · model=${MODEL}` : ""}`);
163
+ cmuxStatus("building", "#4a90d9", "hammer");
145
164
  // inherit stdio so the window shows the agent working live; also capture for sid-parsing.
146
165
  // Tee stderr to ERRF (still shown live in the window) so a failed turn can be classified.
147
166
  try { appendFileSync(ERRF, "", { flag: "w" }); } catch {}
@@ -155,6 +174,7 @@ function runTurn(prompt, isFirst, trigger = "kickoff") {
155
174
  if (cli.sid && r.stdout) { const m = r.stdout.match(cli.sid); if (m) sid = m[1]; }
156
175
  telemetry({ ts: Date.now(), agent: AGENT, project: PROJ, turn: TURN, trigger, model: MODEL || "default", duration_ms: Date.now() - t0, exit: r.status });
157
176
  log(`turn ended (exit ${r.status}, ${((Date.now() - t0) / 1000).toFixed(0)}s)`);
177
+ if (r.status === 0) cmuxStatus("idle", "#8a94a6", "robot"); // finished this turn, waiting for the next
158
178
  return r.status;
159
179
  }
160
180