trantor 0.17.77 → 0.17.79
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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +36 -6
- package/bin/models.mjs +5 -2
- package/bin/slop-gate.mjs +11 -3
- package/engine/bin/__pycache__/scrooge-capabilitiescpython-314.pyc +0 -0
- package/engine/bin/scrooge-capabilities +15 -4
- package/package.json +2 -2
- package/skills/crew/SKILL.md +7 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trantor",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.79",
|
|
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
|
@@ -51,7 +51,7 @@ heartbeats, inbox delivery, handoff/baton pass, sub-agent cards):
|
|
|
51
51
|
"relay": {
|
|
52
52
|
"command": "node",
|
|
53
53
|
"args": ["<absolute-path-to-trantor>/mcp.mjs"],
|
|
54
|
-
"env": { "
|
|
54
|
+
"env": { "RELAY_AGENT": "kimi-orch" },
|
|
55
55
|
"startupTimeoutMs": 15000,
|
|
56
56
|
"toolTimeoutMs": 150000
|
|
57
57
|
}
|
|
@@ -62,6 +62,13 @@ heartbeats, inbox delivery, handoff/baton pass, sub-agent cards):
|
|
|
62
62
|
2. In the Kimi TUI: `/plugins install <absolute-path-to-trantor>` (or the GitHub URL), then
|
|
63
63
|
`/reload`, then start a new session.
|
|
64
64
|
|
|
65
|
+
Since 0.17.76 the Kimi hooks are a **dialect bridge**, not a fork: `kimi/bridge.mjs` runs the
|
|
66
|
+
canonical hooks as children and translates Kimi's input/output conventions at the edges
|
|
67
|
+
(SessionStart context is stashed and delivered on the first prompt). Kimi hooks therefore sign
|
|
68
|
+
their hub calls, follow per-project hub pins, and inherit every future hook fix automatically.
|
|
69
|
+
Do **not** set `RELAY_URL` in MCP configs — a hardcoded URL overrides the per-project pin and
|
|
70
|
+
splits the project across hubs; the pin decides the hub.
|
|
71
|
+
|
|
65
72
|
Notes: Kimi plugin installs are **snapshots** (`~/.kimi-code/plugins/managed/trantor/`) — after
|
|
66
73
|
updating trantor, re-run `/plugins install` to refresh the skills/hooks. The MCP entry above always
|
|
67
74
|
runs your live checkout, so the relay server itself never goes stale. Invoke the skills with
|
|
@@ -260,6 +267,25 @@ Every session registers automatically — **crew or not** — and a solo session
|
|
|
260
267
|
shows up on the board as cards, so the dashboard reflects *all* the work on a project, not just
|
|
261
268
|
crew runs.
|
|
262
269
|
|
|
270
|
+
## The board keeps itself honest
|
|
271
|
+
|
|
272
|
+
Cards carry their **story on the card**: every `note` lands in a permanent per-card log (last 40
|
|
273
|
+
entries) that survives restarts, retention, and id remaps — the drawer shows it as the card's
|
|
274
|
+
narrative, so clicking an old card tells you what actually happened, not a one-line status trail.
|
|
275
|
+
The `todo` lane has a lifecycle: cards untouched for 14 days (`RELAY_TODO_STALE_MS`) move to
|
|
276
|
+
STALE with an "aged out" note instead of rotting silently, and todo tiles wear an age badge from
|
|
277
|
+
day 7. A commit closes the session's focus card and the two link both ways. `trantor doctor`
|
|
278
|
+
cross-checks every hub you know about against the per-project pins and reports any **split-brain**
|
|
279
|
+
(a project live on two hubs) with the exact fix — and `trantor adopt <project>` migrates a project
|
|
280
|
+
between hubs in one verified step, telling stale sessions to restart.
|
|
281
|
+
|
|
282
|
+
Crew output is gated mechanically, too: `bin/slop-gate.mjs` runs the vendored
|
|
283
|
+
[anti-slop](https://github.com/dmmulroy/anti-slop) Oxlint rules over an agent's **changed files
|
|
284
|
+
only** — unexplained type assertions, `unknown` laundering, runtime `typeof`, Reflect tricks and
|
|
285
|
+
friends fail the card before it can reach done, while legacy debt burns down on its own card
|
|
286
|
+
instead of blocking everyone. A surface that has paid its debt off stays paid: `desktop/src` is at
|
|
287
|
+
zero and `npm test` now lints it in full (`node bin/slop-gate.mjs --surface desktop/src`).
|
|
288
|
+
|
|
263
289
|
## The brain — plan-aware economics
|
|
264
290
|
|
|
265
291
|
Trantor's economics engine ([Scrooge](https://github.com/sashabogi/token-scrooge) — installed
|
|
@@ -326,8 +352,8 @@ rate, not work rate.
|
|
|
326
352
|
| `relay_send(to, text)` / `relay_inbox` / `relay_wait(t)` | Live messaging: direct, read-new, long-poll wake |
|
|
327
353
|
| `relay_peers` / `relay_status(text)` / `relay_whoami` | Presence: who's alive (honest, heartbeat-backed), doing what |
|
|
328
354
|
| `relay_project_brief(text)` | The project's what/why on the dashboard |
|
|
329
|
-
| `relay_task_add(title, …, difficulty, model, deps, project?)` | Cards with difficulty/model badges + DAG edges; `project` targets another board when you orchestrate from elsewhere |
|
|
330
|
-
| `relay_task_move(id, status)` | `todo → doing → testing → done` (the gate), `failed`, `blocked` |
|
|
355
|
+
| `relay_task_add(title, …, difficulty, model, deps, note?, project?)` | Cards with difficulty/model badges + DAG edges; `note` seeds the card's **permanent log**; `project` targets another board when you orchestrate from elsewhere |
|
|
356
|
+
| `relay_task_move(id, status, note?)` | `todo → doing → testing → done` (the gate), `failed`, `blocked` — moves to testing/done should carry a `note`: what you did + the evidence, stored on the card forever |
|
|
331
357
|
| `relay_board` | The project's full board, as text |
|
|
332
358
|
| `relay_scrooge(prompt, task?, difficulty?)` | Fractal cheap-model delegation, with the ledger receipt |
|
|
333
359
|
| `relay_lesson(text, scope?)` | Record a failure lesson — auto-injected into all future crews |
|
|
@@ -338,6 +364,7 @@ rate, not work rate.
|
|
|
338
364
|
```
|
|
339
365
|
trantor setup | doctor | connect | profile | provider | models
|
|
340
366
|
| up <agents…> | swap <old> <new> | down | ui | advise | hub | watch
|
|
367
|
+
| adopt <project> | reconcile | duty | orchestrate | patrol | app | backfill | init-hooks
|
|
341
368
|
```
|
|
342
369
|
|
|
343
370
|
- **`trantor provider`** — `list` every crew seat (built-in + brought) with availability + tier ·
|
|
@@ -355,9 +382,12 @@ trantor setup | doctor | connect | profile | provider | models
|
|
|
355
382
|
|
|
356
383
|
## Works with any MCP agent
|
|
357
384
|
|
|
358
|
-
Claude Code, Codex CLI, Kimi Code CLI, and **OpenCode** (the
|
|
359
|
-
OpenRouter, and any provider you bring) are wired by
|
|
360
|
-
backed-up, never overwrites your customizations).
|
|
385
|
+
Claude Code, Codex CLI, Kimi Code CLI, **DeepSeek Harness (`dsh`)**, and **OpenCode** (the
|
|
386
|
+
universal adapter — DeepSeek, GLM, OpenRouter, and any provider you bring) are wired by
|
|
387
|
+
`trantor connect` automatically (idempotent, backed-up, never overwrites your customizations).
|
|
388
|
+
For `dsh`, connect builds a full profile (`~/.dsh/profiles/trantor`): DeepSeek's own Claude Code
|
|
389
|
+
hooks bridge running the trantor hooks, plus their MCP client running the relay server — then
|
|
390
|
+
`trantor up dsh` runs it as a crew seat (fresh session per turn; uses `DEEPSEEK_API_KEY`). Anything else that speaks MCP: point it at
|
|
361
391
|
`mcp.mjs` with `RELAY_AGENT=<brand>` — loading the server auto-registers the session, so presence
|
|
362
392
|
works before the model says a word.
|
|
363
393
|
|
package/bin/models.mjs
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { execSync } from "node:child_process";
|
|
8
8
|
import { dirname, join } from "node:path";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
|
-
import { existsSync } from "node:fs";
|
|
10
|
+
import { existsSync, statSync } from "node:fs";
|
|
11
11
|
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
12
12
|
import { buildRoster, loadWorld } from "./advise.mjs";
|
|
13
13
|
|
|
@@ -57,7 +57,10 @@ function detail(name) {
|
|
|
57
57
|
if (existsSync(SCROOGE) || has("scrooge")) {
|
|
58
58
|
console.log(`\n${C.gold}router picks (code task):${C.off}`);
|
|
59
59
|
for (const d of ["easy", "medium", "hard"]) console.log(` ${d.padEnd(7)} → ${routePick(models, d)}`);
|
|
60
|
-
|
|
60
|
+
const caps = join(H, ".token-scrooge", "capabilities.json");
|
|
61
|
+
// only nag when the scores are missing or older than the weekly refresh allows for
|
|
62
|
+
const ageDays = existsSync(caps) ? (Date.now() - statSync(caps).mtimeMs) / 86400e3 : Infinity;
|
|
63
|
+
if (ageDays > 9) console.log(`${C.dim}(scores ${ageDays === Infinity ? "missing" : `${Math.floor(ageDays)}d old`} — run scrooge-capabilities to (re)score the catalog for accurate difficulty routing)${C.off}`);
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
|
package/bin/slop-gate.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
// node bin/slop-gate.mjs # lint changed + untracked lintable files (the crew gate)
|
|
5
5
|
// node bin/slop-gate.mjs --all # lint the whole configured surface (advisory audit)
|
|
6
|
+
// node bin/slop-gate.mjs --surface desktop/src # lint ONE paid-off surface in full (hard gate in npm test)
|
|
6
7
|
//
|
|
7
8
|
// The rules are the vendored dmmulroy/anti-slop set (tools/oxlint/anti-slop — ours to tune):
|
|
8
9
|
// they mechanically reject low-evidence AI patterns — unexplained type assertions, unknown
|
|
@@ -13,6 +14,10 @@
|
|
|
13
14
|
import { execSync, spawnSync } from "node:child_process";
|
|
14
15
|
|
|
15
16
|
const ALL = process.argv.includes("--all");
|
|
17
|
+
// A surface that has burned its debt down to zero (desktop/src, #4798) is gated in FULL, not by
|
|
18
|
+
// diff: the whole point of paying it off is that it stays paid. npm test runs this.
|
|
19
|
+
const SURFACE_AT = process.argv.indexOf("--surface");
|
|
20
|
+
const SURFACE = SURFACE_AT > -1 ? process.argv[SURFACE_AT + 1] : null;
|
|
16
21
|
const LINTABLE = /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
|
|
17
22
|
|
|
18
23
|
function sh(cmd) {
|
|
@@ -21,7 +26,9 @@ function sh(cmd) {
|
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
let args;
|
|
24
|
-
if (
|
|
29
|
+
if (SURFACE) {
|
|
30
|
+
args = [SURFACE];
|
|
31
|
+
} else if (ALL) {
|
|
25
32
|
args = ["."];
|
|
26
33
|
} else {
|
|
27
34
|
const changed = sh("git diff --name-only HEAD");
|
|
@@ -40,9 +47,10 @@ const hits = out.split("\n").filter(l => /error\s+anti-slop\(/.test(l));
|
|
|
40
47
|
|
|
41
48
|
if (hits.length) {
|
|
42
49
|
console.log(hits.join("\n"));
|
|
43
|
-
|
|
50
|
+
const where = SURFACE ? `in ${SURFACE} (a zero-debt surface — keep it at zero)` : "in your changes";
|
|
51
|
+
console.log(`\nslop-gate: ${hits.length} anti-slop error(s) ${where} — fix them (or state the SAFETY invariant) before moving the card to testing.`);
|
|
44
52
|
process.exit(1);
|
|
45
53
|
}
|
|
46
54
|
const warnings = out.split("\n").filter(l => /\bwarning\b/.test(l)).length;
|
|
47
|
-
console.log(`slop-gate: clean${warnings ? ` (${warnings} advisory warning(s) — not gating)` : ""}.`);
|
|
55
|
+
console.log(`slop-gate${SURFACE ? ` (${SURFACE})` : ""}: clean${warnings ? ` (${warnings} advisory warning(s) — not gating)` : ""}.`);
|
|
48
56
|
process.exit(0);
|
|
Binary file
|
|
@@ -22,7 +22,7 @@ Usage: scrooge-capabilities # refresh from AA (+OpenRouter), show a
|
|
|
22
22
|
scrooge-capabilities --dry-run # fetch + match, print, but don't write
|
|
23
23
|
Exit: 0 updated · 1 nothing fetched (no key / network) · 2 error.
|
|
24
24
|
"""
|
|
25
|
-
import sys, os, json, argparse, math, urllib.request, urllib.error
|
|
25
|
+
import sys, os, json, argparse, math, time, urllib.request, urllib.error
|
|
26
26
|
|
|
27
27
|
HOME = os.path.expanduser("~")
|
|
28
28
|
SCROOGE_DIR = os.environ.get("SCROOGE_HOME", os.path.join(HOME, ".token-scrooge"))
|
|
@@ -56,10 +56,21 @@ def load_env_file(path):
|
|
|
56
56
|
except Exception:
|
|
57
57
|
pass
|
|
58
58
|
|
|
59
|
-
def http_get_json(url, headers, timeout=30):
|
|
59
|
+
def http_get_json(url, headers, timeout=30, retries=(5, 20, 60)):
|
|
60
|
+
# the weekly launchd run can land while the Mac has no network yet (2026-08-17: DNS error,
|
|
61
|
+
# scores went a week stale) -- back off and retry on connection failures, not on HTTP errors
|
|
60
62
|
req = urllib.request.Request(url, headers=headers, method="GET")
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
for i, wait in enumerate(list(retries) + [None]):
|
|
64
|
+
try:
|
|
65
|
+
with urllib.request.urlopen(req, timeout=timeout) as r:
|
|
66
|
+
return json.loads(r.read().decode())
|
|
67
|
+
except urllib.error.HTTPError:
|
|
68
|
+
raise
|
|
69
|
+
except (urllib.error.URLError, OSError) as e:
|
|
70
|
+
if wait is None:
|
|
71
|
+
raise
|
|
72
|
+
sys.stderr.write(WARN(" network: %s -- retry %d in %ds\n" % (str(e)[:60], i + 1, wait)))
|
|
73
|
+
time.sleep(wait)
|
|
63
74
|
|
|
64
75
|
def norm(s):
|
|
65
76
|
return s.lower().replace(".", "-").replace("_", "-")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trantor",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.79",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"trantor": "bin/cli.mjs"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"zod": "^4.4.3"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"test": "node test.mjs && node test-scenarios.mjs && node test-failure.mjs && node test-redelivery.mjs && node test-handoff.mjs && node test-agents.mjs && node test-update.mjs && node test-handoff-guard.mjs && node test-balances.mjs && node test-subagent-cost.mjs && node test-inbox.mjs && node test-inflight.mjs && node test-notify.mjs && node test-focus.mjs && node test-cardlog.mjs && node test-relay-note.mjs && node test-reaper.mjs && node test-events.mjs && node test-proposals.mjs && node test-scrub.mjs && node test-store-delta.mjs && node test-claims.mjs && node test-adopt.mjs && node test-summarize.mjs && node test-identity-core.mjs && node test-identity.mjs && node test-identity-instances.mjs && node test-duty.mjs && node test-discovery.mjs && node test-doctor.mjs && node test-splitbrain.mjs && node test-overseer.mjs && node test-overseer-lib.mjs && node test-overseer-warn.mjs && node test-provider-keys.mjs && node test-inbox-delivery.mjs && node test-hub-routing.mjs && node test-hook-routing.mjs && node test-relay-wait.mjs && node test-dsh-seat.mjs && node test-kimi-bridge.mjs && node test-kimi-events.mjs && python3 engine/test-routing.py && node test-reconcile.mjs && node test-resources.mjs && node test-patrol.mjs && node test-bridge.mjs && bash test-crew.sh"
|
|
14
|
+
"test": "node bin/slop-gate.mjs --surface desktop/src && node test.mjs && node test-scenarios.mjs && node test-failure.mjs && node test-redelivery.mjs && node test-handoff.mjs && node test-agents.mjs && node test-update.mjs && node test-handoff-guard.mjs && node test-balances.mjs && node test-subagent-cost.mjs && node test-inbox.mjs && node test-inflight.mjs && node test-notify.mjs && node test-focus.mjs && node test-cardlog.mjs && node test-relay-note.mjs && node test-reaper.mjs && node test-events.mjs && node test-proposals.mjs && node test-scrub.mjs && node test-store-delta.mjs && node test-claims.mjs && node test-adopt.mjs && node test-summarize.mjs && node test-identity-core.mjs && node test-identity.mjs && node test-identity-instances.mjs && node test-duty.mjs && node test-discovery.mjs && node test-doctor.mjs && node test-splitbrain.mjs && node test-overseer.mjs && node test-overseer-lib.mjs && node test-overseer-warn.mjs && node test-provider-keys.mjs && node test-inbox-delivery.mjs && node test-hub-routing.mjs && node test-hook-routing.mjs && node test-relay-wait.mjs && node test-dsh-seat.mjs && node test-kimi-bridge.mjs && node test-kimi-events.mjs && python3 engine/test-routing.py && node test-reconcile.mjs && node test-resources.mjs && node test-patrol.mjs && node test-bridge.mjs && bash test-crew.sh"
|
|
15
15
|
},
|
|
16
16
|
"description": "The hub-world for AI agent crews \u2014 orchestrate Claude Code, Codex, GLM, Kimi, DeepSeek & any OpenRouter model as live crews with a plan-aware Advisor, a Kanban/flow command center, a testing gate, and an economics brain (Scrooge).",
|
|
17
17
|
"files": [
|
package/skills/crew/SKILL.md
CHANGED
|
@@ -51,6 +51,7 @@ EXACT launch spec per provider (do not improvise these):
|
|
|
51
51
|
| DeepSeek | `deepseek:deepseek` | runs via opencode; `deepseek` alone = CLI default |
|
|
52
52
|
| **GLM (Z.ai)** | **`glm:zai-coding-plan`** | **runs via opencode, NOT a bare `glm`/`zai` terminal command.** `glm:zai-coding-plan/glm-5.2` pins a model; `glm:zai-coding-plan` live-selects. (Legacy `opencode:zai-coding-plan` still works.) |
|
|
53
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
|
+
| **DeepSeek Harness** | **`dsh`** | DeepSeek's own open-source harness as a seat (API-billed via `DEEPSEEK_API_KEY`). `trantor connect` builds its profile (their CC-hooks bridge running trantor's hooks + their MCP client running the relay). **Fresh session per turn — no resume** — so the seat relies on the wake prompt + the board, not conversation memory; give it self-contained contracts. |
|
|
54
55
|
|
|
55
56
|
`agent:provider` live-selects the best model now; `agent:provider/model` pins one. Example:
|
|
56
57
|
`trantor up codex kimi deepseek:deepseek glm:zai-coding-plan --task code --difficulty hard`.
|
|
@@ -97,8 +98,12 @@ Loop until the board is done — you are a foreman, not a mailbox:
|
|
|
97
98
|
they auto-inject into every future crew's prompts.
|
|
98
99
|
|
|
99
100
|
## Phase 5 — verification gate + integration
|
|
100
|
-
Card flow is `todo → doing → testing → done`; `testing` runs the
|
|
101
|
-
|
|
101
|
+
Card flow is `todo → doing → testing → done`; `testing` runs the seat's OWN test file (never the
|
|
102
|
+
full suite from a seat — suites spawn hubs on fixed ports and collide across seats; the
|
|
103
|
+
orchestrator runs the full suite at integration) plus `node bin/slop-gate.mjs` where the repo has
|
|
104
|
+
one (the anti-slop lint over the seat's changed files — a card must not reach done failing it);
|
|
105
|
+
`done` only green, and moves to testing/done carry a `note` with the evidence — the note is the
|
|
106
|
+
card's permanent story; `failed` (+ bus report) pulses red on the board until you bounce it.
|
|
102
107
|
Enforce the gate — bounce anything that skipped it (bounces are visible: "↩ bounced" on the
|
|
103
108
|
card, history in its tooltip). When all report done: integrate, fix contract mismatches
|
|
104
109
|
YOURSELF, move your card through testing → done, broadcast "🚀 <thing> is live", and when the
|