trantor 0.17.43 → 0.17.45

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.43"
9
+ "version": "0.17.45"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "trantor",
14
14
  "source": "./",
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, Gemini, 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.43",
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.45",
17
17
  "author": {
18
18
  "name": "Sasha Bogojevic"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trantor",
3
- "version": "0.17.43",
3
+ "version": "0.17.45",
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,7 +12,7 @@
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, Gemini, Kimi & DeepSeek in their own terminal windows — routed by your
15
+ Claude Code, Codex, GLM, Kimi & DeepSeek in their own terminal windows — routed by your
16
16
  actual plans, supervised on a live + historical board you can scroll back through, learning
17
17
  from every failure.**
18
18
 
@@ -134,7 +134,7 @@ project takes over with a full window (and a PreCompact hook does this automatic
134
134
  (`solo | scrooge | crew | hybrid`), a routing table with a **reason per package**, why
135
135
  that many seats ("seats follow the work, not the install list"), and a real-money estimate
136
136
  with quota-pool accounting. You say go.
137
- 2. **Windows open.** `trantor up codex gemini kimi deepseek:deepseek-v4-pro` spawns one titled
137
+ 2. **Windows open.** `trantor up codex kimi deepseek:deepseek opencode:zai-coding-plan` spawns one titled
138
138
  terminal window per agent. `agent:model` pins a model; `agent:provider --difficulty hard`
139
139
  picks the **best live model** for the work at spawn (capability × cost), enumerated from the
140
140
  CLI itself — never a guessed endpoint. **Serialized and then verified on the bus** — the
@@ -194,7 +194,7 @@ Trantor's economics engine ([Scrooge](https://github.com/sashabogi/token-scrooge
194
194
  automatically by `trantor setup`) knows model capabilities, per-1M costs, and keeps the
195
195
  ledger; Trantor turns that into decisions:
196
196
 
197
- - **Declare your plans once:** `trantor profile set claude=max codex=plus gemini=tier kimi=coding-plan deepseek=api`
197
+ - **Declare your plans once:** `trantor profile set claude=max codex=plus zai=coding-plan kimi=coding-plan deepseek=api`
198
198
  - The Advisor routes by **your economics**: API-billed orchestrator → offload everything;
199
199
  $20-tier plan → the crew *is* the only way a real build fits; max-tier → context horizon
200
200
  decides. **Quota pooling**: one build spread across your separate subscription buckets —
package/bin/advise.mjs CHANGED
@@ -19,12 +19,51 @@ import { pathToFileURL } from "node:url";
19
19
  const H = homedir();
20
20
  const read = (p, fb) => { try { return JSON.parse(readFileSync(p, "utf8")); } catch { return fb; } };
21
21
 
22
+ // ---- canonical crew roster (single source of truth, mirrors bin/doctor.mjs) ----
23
+ // Each token maps to: the CLI binary that must exist · the `trantor up` LAUNCH spec the
24
+ // orchestrator runs · the bus SESSION name (= spec's agent part) · the profile PROVIDER
25
+ // key that gates the seat and supplies the tier.
26
+ //
27
+ // GEMINI is deliberately ABSENT: Google retired the free Gemini CLI seat (2026-06-18), so
28
+ // even though the `gemini` binary is usually still on PATH, `gemini --yolo` crashes (exit 1)
29
+ // for everyone without a paid enterprise key. Its replacement seat is GLM via opencode.
30
+ // (Gemini still works as a Scrooge cheap-model via GEMINI_API_KEY — a separate path.) A
31
+ // paid-key holder can still force it with an explicit `trantor up gemini`; we just never
32
+ // auto-recommend a dead seat.
33
+ export const ROSTER = {
34
+ codex: { cli: "codex", launch: "codex", session: "codex", provider: "codex" },
35
+ kimi: { cli: "kimi", launch: "kimi", session: "kimi", provider: "kimi" },
36
+ deepseek: { cli: "opencode", launch: "deepseek:deepseek", session: "deepseek", provider: "deepseek" },
37
+ glm: { cli: "opencode", launch: "opencode:zai-coding-plan", session: "opencode", provider: "zai" },
38
+ // OpenRouter = the BYOM on-ramp: one key fronts hundreds of models (incl. vendors with no
39
+ // CLI of their own). Rides the opencode runner like glm/deepseek, but under its OWN bus label
40
+ // `openrouter` (distinct session, never collides with the glm `opencode` seat). The launcher
41
+ // live-selects the best OpenRouter model for the work's difficulty (or pin one:
42
+ // `openrouter:openrouter/<vendor>/<model>`). The model picked is what determines its strength,
43
+ // so it sits LAST in every CREW_PREF tier — a wildcard that fills once the proven native seats
44
+ // are taken, and the ONLY seat for a user who brought nothing but an OpenRouter key.
45
+ openrouter: { cli: "opencode", launch: "openrouter:openrouter", session: "openrouter", provider: "openrouter" },
46
+ };
47
+
22
48
  export function loadWorld() {
23
49
  const profile = read(join(H, ".agent-bus", "profile.json"), { providers: {} });
24
50
  const registry = read(join(H, ".token-scrooge", "registry.json"), { models: {}, tasks: {} });
25
51
  const caps = read(join(H, ".token-scrooge", "capabilities.json"), {});
26
52
  const has = (c) => { try { execSync(`command -v ${c}`, { stdio: "ignore", shell: "/bin/sh" }); return true; } catch { return false; } };
27
- const agents = ["codex", "gemini", "kimi", "deepseek"].filter(a => has(a === "deepseek" ? "opencode" : a));
53
+ const opencodeKey = (prov) => !!read(join(H, ".config", "opencode", "opencode.json"), {})?.provider?.[prov]?.options?.apiKey;
54
+ // a key the user already has for Scrooge counts too — the opencode runner sources these .env
55
+ // files, so OPENROUTER_API_KEY in ~/.token-scrooge/.env lights up the crew seat with no extra setup.
56
+ const envHasKey = (k) => !!process.env[k] || [join(H, ".token-scrooge", ".env"), join(H, ".agent-bus", ".env")]
57
+ .some(f => { try { return readFileSync(f, "utf8").includes(k); } catch { return false; } });
58
+ // a seat is available only if its CLI exists AND its provider is actually set up — a present
59
+ // binary with a dead/missing seat (gemini, or opencode with no provider key) must NOT be recommended.
60
+ const hasSeat = (tok) => {
61
+ const r = ROSTER[tok]; if (!r || !has(r.cli)) return false;
62
+ if (tok === "glm") return !!profile?.providers?.zai || opencodeKey("zai-coding-plan");
63
+ if (tok === "openrouter") return !!profile?.providers?.openrouter || opencodeKey("openrouter") || envHasKey("OPENROUTER_API_KEY");
64
+ return true;
65
+ };
66
+ const agents = Object.keys(ROSTER).filter(hasSeat);
28
67
  return { profile, registry, caps, agents, scrooge: has("scrooge") };
29
68
  }
30
69
 
@@ -42,8 +81,9 @@ export function scroogeModelFor(registry, caps, kind = "code", difficulty = "eas
42
81
 
43
82
  // crude per-package token forecast (input+output through the executor)
44
83
  const FORECAST = { easy: 0.3e6, medium: 1.5e6, hard: 6e6 }; // tokens
45
- // crew agent preference per difficulty: frontier subs take hard, cheap takes easy
46
- const CREW_PREF = { hard: ["codex", "gemini", "kimi", "deepseek"], medium: ["kimi", "gemini", "codex", "deepseek"], easy: ["deepseek", "kimi", "gemini", "codex"] };
84
+ // crew agent preference per difficulty: frontier subs take hard, cheap takes easy.
85
+ // (gemini retired its slot goes to glm, a strong coding-plan seat on $0 marginal quota.)
86
+ const CREW_PREF = { hard: ["codex", "glm", "kimi", "deepseek", "openrouter"], medium: ["kimi", "glm", "codex", "deepseek", "openrouter"], easy: ["deepseek", "kimi", "glm", "codex", "openrouter"] };
47
87
 
48
88
  export function advise(input, world = loadWorld()) {
49
89
  const { profile, registry, caps, agents, scrooge } = world;
@@ -82,19 +122,23 @@ export function advise(input, world = loadWorld()) {
82
122
  if (p.owner === "self") return { ...p, executor: "orchestrator", pool: tierOf(profile, "claude"), reason: "architect-owned (foundation/integration doctrine) — the orchestrator keeps the shared contract in its own hands" };
83
123
  if (mode === "solo") return { ...p, executor: "orchestrator", pool: tierOf(profile, "claude"), reason: "small enough to do inline" };
84
124
  const pref = CREW_PREF[p.difficulty].filter(a => agents.includes(a));
85
- const agent = pref.sort((a, b) => (used[a] || 0) - (used[b] || 0))[0] || "deepseek";
125
+ const agent = pref.sort((a, b) => (used[a] || 0) - (used[b] || 0))[0] || agents[0] || "deepseek";
86
126
  used[agent] = (used[agent] || 0) + 1;
87
- const pool = tierOf(profile, agent === "deepseek" ? "deepseek" : agent);
127
+ const pool = tierOf(profile, ROSTER[agent]?.provider || agent);
88
128
  let est = null;
89
129
  if (pool === "api") { // deepseek API etc — estimate real $ via registry
90
130
  const m = registry.models?.["deepseek-v4-flash"] || { cost_in: 0.14, cost_out: 0.28 };
91
131
  est = +((FORECAST[p.difficulty] * 0.85 * m.cost_in + FORECAST[p.difficulty] * 0.15 * m.cost_out) / 1e6).toFixed(2);
92
132
  }
93
- const why_r = p.difficulty === "hard"
133
+ let why_r = p.difficulty === "hard"
94
134
  ? `hard → strongest available coder (${agent}); its ${pool} pool means ${pool === "api" ? "real $ but cheapest capable" : "$0 marginal on existing quota"}`
95
135
  : p.difficulty === "medium"
96
136
  ? `medium → solid mid-tier (${agent}) keeps frontier seats free for hard work; ${pool === "api" ? "metered" : "quota"} pool`
97
137
  : `easy → cheapest seat (${agent})`;
138
+ // OpenRouter live-select ranks by COST only (the 335-model catalog has no capability scores
139
+ // yet — that's the capability-ingestion follow-up), so for HARD work it can land a cheap model.
140
+ // Flag it: pin a strong model explicitly (openrouter:openrouter/<vendor>/<model>) for hard work.
141
+ if (agent === "openrouter" && p.difficulty === "hard") why_r += ` — ⚠️ live-select ranks by cost; PIN a strong model (e.g. openrouter:openrouter/anthropic/claude-opus-latest) for hard work until capability data lands`;
98
142
  return { ...p, executor: agent, pool, est_cost_usd: est, reason: why_r };
99
143
  });
100
144
  // crew-size rationale: seats are EMERGENT from the work, and we say so
@@ -120,17 +164,26 @@ export function advise(input, world = loadWorld()) {
120
164
  // creation order — the architect substitutes real ids as it creates them.)
121
165
  const selfPkgs = routing.filter(r => r.executor === "orchestrator");
122
166
  const foundationIdx = selfPkgs.length ? [1] : [];
123
- const cards = routing.map((r, i) => ({
167
+ const cards = routing.map((r, i) => {
168
+ const seat = ROSTER[r.executor];
169
+ return {
124
170
  order: i + 1, title: r.title, difficulty: r.difficulty,
125
- assignee: r.executor === "scrooge" || r.executor === "orchestrator" ? undefined : `${r.executor}:<project>`,
126
- // "auto" = resolve a LIVE model at spawn (the orchestrator runs `trantor up <agent>:<provider>
127
- // --task --difficulty`, which picks the best live model). Was `<cli>-default` a stale default.
171
+ // bus identity = the runner's session name (spec's agent part) — glm rides the `opencode`
172
+ // runner so its session/assignee is `opencode:<project>`, NOT `glm:<project>`.
173
+ assignee: r.executor === "scrooge" || r.executor === "orchestrator" ? undefined : `${seat?.session || r.executor}:<project>`,
174
+ // launch = the EXACT `trantor up` spec to spawn this seat; the orchestrator runs
175
+ // `trantor up <launch> --task <task> --difficulty <difficulty>`. Carrying it explicitly is
176
+ // what teaches the orchestrator the GLM path (`opencode:zai-coding-plan`) instead of guessing.
177
+ launch: ["scrooge", "orchestrator"].includes(r.executor) ? undefined : (seat?.launch || r.executor),
178
+ // "auto" = resolve a LIVE model at spawn (the launch spec already pins the provider; the
179
+ // runner picks the best live model for it). Was `<cli>-default` — a stale default.
128
180
  model: r.model || (["scrooge", "orchestrator"].includes(r.executor) ? undefined : "auto"),
129
181
  task: ["scrooge", "orchestrator"].includes(r.executor) ? undefined : r.kind,
130
182
  via: r.executor === "scrooge" ? "relay_scrooge" : "relay_task_add",
131
183
  deps_orders: r.executor === "orchestrator" && /integrat/i.test(r.title)
132
184
  ? routing.map((x, j) => j + 1).filter(j => j !== i + 1)
133
- : (r.executor !== "orchestrator" ? foundationIdx.filter(f => f !== i + 1) : []) }));
185
+ : (r.executor !== "orchestrator" ? foundationIdx.filter(f => f !== i + 1) : []) };
186
+ });
134
187
  return { mode, why, crew, routing, routing_table_md: table, card_args: cards, est_api_cost_usd: apiCost, quota_pools: pools, summary, orchestrator_tier: orchTier, agents_available: agents };
135
188
  }
136
189
 
package/bin/cli.mjs CHANGED
@@ -49,7 +49,7 @@ switch (cmd) {
49
49
  trantor doctor where do I stand? hub/plugin/CLIs/auth/keys/profile, with copy-paste fixes
50
50
  trantor connect (re)wire every installed AI CLI to the bus
51
51
  trantor profile declare your plans: trantor profile set claude=max codex=plus deepseek=api
52
- trantor up … spawn a crew here: trantor up codex gemini kimi deepseek:deepseek-v4-pro
52
+ trantor up … spawn a crew here: trantor up codex kimi deepseek:deepseek opencode:zai-coding-plan
53
53
  trantor down tear the crew down (kills processes, closes windows, no dialogs)
54
54
  trantor ui open the live dashboard (board + flow views)
55
55
  trantor catchup "where are we?" — the continuous board + git, with a synthesized brief
@@ -69,6 +69,12 @@ const CLI = {
69
69
  next: `opencode run -c{M} "$(cat {P})"`, mflag: " -m ", env: join(homedir(), ".token-scrooge", ".env") },
70
70
  opencode: { first: `opencode run{M} "$(cat {P})"`,
71
71
  next: `opencode run -c{M} "$(cat {P})"`, mflag: " -m ", env: join(homedir(), ".token-scrooge", ".env") },
72
+ // OpenRouter rides the opencode CLI exactly like deepseek/glm, but under its OWN agent label so
73
+ // its bus identity is `openrouter:<project>` (RELAY_AGENT is set per-spawn) — never colliding with
74
+ // the glm `opencode` seat. Model ids come pre-qualified (`openrouter/<vendor>/<model>`). Sources
75
+ // the token-scrooge .env so an existing OPENROUTER_API_KEY authenticates with no extra wiring.
76
+ openrouter: { first: `opencode run{M} "$(cat {P})"`,
77
+ next: `opencode run -c{M} "$(cat {P})"`, mflag: " -m ", env: join(homedir(), ".token-scrooge", ".env") },
72
78
  claude: { first: `claude{M} -p "$(cat {P})" --dangerously-skip-permissions`,
73
79
  next: `claude -c{M} -p "$(cat {P})" --dangerously-skip-permissions`, mflag: " --model " },
74
80
  };
package/bin/crew.sh CHANGED
@@ -75,7 +75,7 @@ SCROOGE="$BUS_DIR/engine/bin/scrooge"
75
75
  resolve_model() {
76
76
  local agent="$1" provider="$2" task="$3" diff="$4" cands="" out=""
77
77
  case "$agent" in
78
- opencode|deepseek)
78
+ opencode|deepseek|openrouter)
79
79
  cands="$(opencode models "$provider" 2>/dev/null | tr '\n' ' ')"
80
80
  [ -n "$cands" ] || { echo "[crew] no live models via 'opencode models $provider' — CLI default" >&2; return 0; }
81
81
  out="$(python3 "$SCROOGE" route --candidates "$cands" -t "$task" -d "$diff" --json 2>/dev/null)" ;;
@@ -133,6 +133,9 @@ spawn_grid() { # $@ = agents — (re)computes the grid for THIS batch and spawn
133
133
  for SPEC in "$@"; do
134
134
  AGENT="${SPEC%%:*}" # agent[:provider[/model]] — model rides in as CREW_MODEL
135
135
  FIELD=""; [ "$SPEC" != "$AGENT" ] && FIELD="${SPEC#*:}"
136
+ # `trantor up openrouter` (no provider) → default the opencode provider to openrouter so it
137
+ # live-selects from the OpenRouter catalog instead of falling back to opencode's default model.
138
+ [ "$AGENT" = "openrouter" ] && [ -z "$FIELD" ] && FIELD="openrouter"
136
139
  MODEL=""
137
140
  if [ -n "$FIELD" ]; then
138
141
  case "$FIELD" in
package/bin/doctor.mjs CHANGED
@@ -61,6 +61,10 @@ const CLIS = [
61
61
  { name: "kimi", bin: "kimi", wired: () => !!read(join(H, ".kimi", "mcp.json"))?.mcpServers?.relay, auth: () => existsSync(join(H, ".kimi", "credentials")), login: "kimi → /login (Kimi account or Moonshot API key)" },
62
62
  { name: "deepseek (via opencode)", bin: "opencode", wired: () => !!read(join(H, ".config", "opencode", "opencode.json"))?.mcp?.relay, auth: () => !!process.env.DEEPSEEK_API_KEY || (existsSync(join(H, ".agent-bus", ".env")) && readFileSync(join(H, ".agent-bus", ".env"), "utf8").includes("DEEPSEEK_API_KEY")) || !!read(join(H, ".local", "share", "opencode", "auth.json")), login: `get a key at platform.deepseek.com, then: echo 'DEEPSEEK_API_KEY=sk-…' >> ~/.agent-bus/.env` },
63
63
  { name: "glm (via opencode · coding plan)", bin: "opencode", wired: () => !!read(join(H, ".config", "opencode", "opencode.json"))?.mcp?.relay, auth: () => !!read(join(H, ".config", "opencode", "opencode.json"))?.provider?.["zai-coding-plan"]?.options?.apiKey, login: `put your Z.ai coding-plan key at ~/.config/opencode/opencode.json → provider["zai-coding-plan"].options.apiKey, then seat: trantor up opencode:zai-coding-plan/glm-5.1` },
64
+ // OpenRouter — the BYOM on-ramp: ONE key fronts hundreds of models. Rides opencode; the same
65
+ // OPENROUTER_API_KEY Scrooge already uses authenticates the crew seat (the runner sources the
66
+ // .env files). Available the moment the key exists in env/opencode + declared `openrouter=api`.
67
+ { name: "openrouter (via opencode · BYOM, hundreds of models)", bin: "opencode", wired: () => !!read(join(H, ".config", "opencode", "opencode.json"))?.mcp?.relay, auth: () => !!process.env.OPENROUTER_API_KEY || !!read(join(H, ".config", "opencode", "opencode.json"))?.provider?.openrouter?.options?.apiKey || [join(H, ".token-scrooge", ".env"), join(H, ".agent-bus", ".env")].some(f => { try { return readFileSync(f, "utf8").includes("OPENROUTER_API_KEY"); } catch { return false; } }), login: `get a key at openrouter.ai/keys, then: echo 'OPENROUTER_API_KEY=sk-or-…' >> ~/.agent-bus/.env && trantor profile set openrouter=api. Seat: trantor up openrouter (live-selects) or pin trantor up openrouter:openrouter/<vendor>/<model>` },
64
68
  ];
65
69
  let installed = 0;
66
70
  for (const c of CLIS) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trantor",
3
- "version": "0.17.43",
3
+ "version": "0.17.45",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "trantor": "bin/cli.mjs"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: crew
3
- description: Orchestrate a multi-agent build with Trantor — get an Advisor recommendation (solo/scrooge/crew/hybrid based on the user's plans and the work), fire up helper AI CLIs (Codex, Gemini, Kimi, DeepSeek) with pinned models in visible terminal windows, assign difficulty-tagged work over the bus, track it on the Kanban dashboard with a testing gate, delegate grunt to Scrooge, supervise actively, integrate, ship. Use when the user wants several AI agents building something together, says "fire up the crew/agents", "build it with trantor / with the crew", or asks to coordinate other coding CLIs on a task.
3
+ description: Orchestrate a multi-agent build with Trantor — get an Advisor recommendation (solo/scrooge/crew/hybrid based on the user's plans and the work), fire up helper AI CLIs (Codex, GLM, Kimi, DeepSeek) with pinned models in visible terminal windows, assign difficulty-tagged work over the bus, track it on the Kanban dashboard with a testing gate, delegate grunt to Scrooge, supervise actively, integrate, ship. Use when the user wants several AI agents building something together, says "fire up the crew/agents", "build it with trantor / with the crew", or asks to coordinate other coding CLIs on a task.
4
4
  ---
5
5
 
6
6
  # Trantor crew — the unified playbook (brain × body)
@@ -39,12 +39,38 @@ explicit EVENT/INTERFACE CONTRACT — cross-agent bugs come from contract drift.
39
39
  3. Open the dashboard: `trantor ui` (or `open -na "Google Chrome" --args --new-window <hub-url>`)
40
40
 
41
41
  ## Phase 2 — fire up the crew (with the Advisor's models)
42
- `trantor up codex:gpt-5.5 gemini kimi deepseek:deepseek-v4-pro`
43
- `agent:model` pins a model (omit to use that CLI's default; use what relay_advise routed).
42
+ **Each crew card from `relay_advise` carries a `launch` spec — run it VERBATIM; never invent a
43
+ CLI invocation or run an agent "in a terminal" yourself.** Spawn every seat in one call:
44
+ `trantor up <launch> <launch> … --task <kind> --difficulty <diff>`. The live roster + the
45
+ EXACT launch spec per provider (do not improvise these):
46
+
47
+ | seat | launch spec | notes |
48
+ |---|---|---|
49
+ | Codex | `codex` (or `codex:gpt-5.5` to pin) | OpenAI CLI |
50
+ | Kimi | `kimi` | Moonshot coding-plan |
51
+ | DeepSeek | `deepseek:deepseek` | runs via opencode; `deepseek` alone = CLI default |
52
+ | **GLM (Z.ai)** | **`opencode:zai-coding-plan`** | **runs via opencode, NOT a bare `glm`/`zai` terminal command.** `opencode:zai-coding-plan/glm-5.2` pins a model; `opencode:zai-coding-plan` live-selects. |
53
+ | **OpenRouter (BYOM)** | **`openrouter`** | **the bring-your-own-model on-ramp — one key fronts hundreds of vendors (incl. ones with no CLI).** Bare `openrouter` live-selects the best OpenRouter model for the difficulty; pin one with `openrouter:openrouter/<vendor>/<model>`. Its own bus identity `openrouter:<project>` (never collides with the GLM `opencode` seat). |
54
+
55
+ `agent:provider` live-selects the best model now; `agent:provider/model` pins one. Example:
56
+ `trantor up codex kimi deepseek:deepseek opencode:zai-coding-plan --task code --difficulty hard`.
57
+ **Whatever the advisor's `launch` field says, run that verbatim** — the roster above is the menu,
58
+ but the advisor already picked the right seats and specs for THIS work (a user who's only brought
59
+ an OpenRouter key, for instance, gets `openrouter` for everything; one who brought five providers
60
+ gets a load-balanced spread). New providers a user brings (OpenRouter today; any opencode-supported
61
+ vendor next) appear automatically once declared via `trantor profile set <name>=api` + a key.
62
+
63
+ ⚠️ **Gemini CLI is RETIRED (Google killed the free seat 2026-06-18).** The advisor no longer
64
+ offers it and you must NOT fire up `gemini` — `gemini --yolo` exits 1 and crash-loops on the
65
+ bus. Its replacement seat is **GLM via `opencode:zai-coding-plan`**. (Gemini still serves as a
66
+ Scrooge cheap-model via `GEMINI_API_KEY` — that's a separate, working path, not a crew seat.)
67
+ Only a holder of a paid Gemini enterprise key should ever `trantor up gemini`.
68
+
44
69
  (If `trantor` isn't on PATH, the same launcher is `bash <plugin-root>/bin/crew.sh up …`.)
45
70
  The launcher auto-wires configs, spawns serialized runner windows, then **VERIFIES each agent
46
71
  on the bus with one retry**. READ ITS OUTPUT: it ends "crew verified" or "✗✗ CREW INCOMPLETE"
47
- naming no-shows. **Never assign work to an unverified agent.** The bus is the truth.
72
+ naming no-shows. **Never assign work to an unverified agent.** The bus is the truth. If a seat
73
+ fails to verify, run `trantor doctor` — it shows each CLI's wired/auth state and the exact fix.
48
74
 
49
75
  ## Phase 3 — contracts over the bus
50
76
  Build the shared foundation yourself first, then `relay_send` each agent its contract