trantor 0.18.68 → 0.18.69
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/bin/write-handoff.mjs +20 -26
- package/hooks/lib/handoff.mjs +5 -4
- package/hooks/pricing.mjs +14 -6
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trantor",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.69",
|
|
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/bin/write-handoff.mjs
CHANGED
|
@@ -2,23 +2,18 @@
|
|
|
2
2
|
// Save a model-authored handoff (piped on stdin) for this project; the next session auto-loads it.
|
|
3
3
|
// With --baton: ALSO open a fresh self-announcing session and close THIS window once it takes over
|
|
4
4
|
// (the one-command manual baton behind /trantor:handoff). Without it: just write the file (legacy).
|
|
5
|
-
import {
|
|
6
|
-
import { join
|
|
7
|
-
import {
|
|
8
|
-
import { execSync } from "node:child_process";
|
|
9
|
-
import { spawnBaton, handoffMode, resolveHandoffSurface, attachState, resolveSeat, resolveHandoffCard } from "../hooks/lib/handoff.mjs";
|
|
5
|
+
import { readFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { writeHandoff, spawnBaton, resolveHandoffSurface } from "../hooks/lib/handoff.mjs";
|
|
10
8
|
import { handoffDir } from "../lib/project.mjs";
|
|
11
9
|
|
|
12
10
|
const baton = process.argv.includes("--baton");
|
|
13
|
-
// --latest: pass the baton on a handoff ALREADY on disk
|
|
14
|
-
//
|
|
15
|
-
// hand it over — 2m28s of regenerated prose on a live scribe session, 2026-08-24.
|
|
11
|
+
// --latest: pass the baton on a handoff ALREADY on disk — composing it again cost 2m28s of
|
|
12
|
+
// regenerated prose on a live scribe session (drill: test/handoff/test-baton-latest.mjs).
|
|
16
13
|
const latest = process.argv.includes("--latest");
|
|
17
14
|
// #6074: ONE resolver for which project this is and where the session lives — shared with
|
|
18
|
-
// bin/baton.mjs
|
|
19
|
-
//
|
|
20
|
-
// the witnessed crebral-scribe/ios handoff recorded project "ios" from a subfolder cwd and never
|
|
21
|
-
// found its pane.
|
|
15
|
+
// bin/baton.mjs so the two cannot diverge. The name comes from the registration
|
|
16
|
+
// (TRANTOR_ORCH / RELAY_PROJECT / orch-sessions.txt) before the cwd; a subfolder cwd never renames the project.
|
|
22
17
|
const resolved = resolveHandoffSurface({ sessionId: process.env.CLAUDE_SESSION_ID || "" });
|
|
23
18
|
const project = resolved.projectDir;
|
|
24
19
|
const name = resolved.project;
|
|
@@ -37,8 +32,6 @@ if (!latest) {
|
|
|
37
32
|
// directly, so an AGENT_BUS_DIR install wrote where nothing would look.
|
|
38
33
|
const dir = handoffDir();
|
|
39
34
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
40
|
-
const stamp = (() => { try { return execSync("date +%s", { encoding: "utf8" }).trim(); } catch { return String(process.pid); } })();
|
|
41
|
-
let git = ""; try { git = execSync("git -C " + JSON.stringify(project) + " status --short 2>/dev/null | head -30", { encoding: "utf8" }).trim(); } catch {}
|
|
42
35
|
// --latest short-circuits everything below: find this project's newest UNCONSUMED handoff and hand
|
|
43
36
|
// that over, untouched.
|
|
44
37
|
if (latest) {
|
|
@@ -59,18 +52,19 @@ if (latest) {
|
|
|
59
52
|
process.exit(0);
|
|
60
53
|
}
|
|
61
54
|
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
// the
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
55
|
+
// ONE WRITER (#8263): every guarantee CONTRACT-hooks states for "a handoff record" — the read-first
|
|
56
|
+
// floor, verifyGates, the sub-agent manifest, supersedeOlderHandoffs, uncapped persistence — lives
|
|
57
|
+
// in writeHandoff; the literal record assembled here reached none of them. Only trigger,
|
|
58
|
+
// transcript_path "" and force (the command IS the intent) differ.
|
|
59
|
+
const { file } = writeHandoff({
|
|
60
|
+
projectDir: project,
|
|
61
|
+
projectName: name,
|
|
62
|
+
sessionId: "",
|
|
63
|
+
transcript: "",
|
|
64
|
+
trigger: baton ? "manual-baton" : "manual-skill",
|
|
65
|
+
summary: summary.trim(),
|
|
66
|
+
force: true,
|
|
67
|
+
});
|
|
74
68
|
console.log(`handoff saved: ${file}`);
|
|
75
69
|
|
|
76
70
|
if (baton) {
|
package/hooks/lib/handoff.mjs
CHANGED
|
@@ -649,11 +649,12 @@ export function writeHandoff({ projectDir, sessionId, transcript, trigger, summa
|
|
|
649
649
|
else if (r.json && r.json.allow === false) return { skipped: true, reason: r.json.reason || "storm-guard", sinceSec: r.json.sinceSec };
|
|
650
650
|
}
|
|
651
651
|
if (!existsSync(HANDOFF_DIR)) mkdirSync(HANDOFF_DIR, { recursive: true });
|
|
652
|
-
// #5648: an automatic digest must never recompose+supersede a FRESH model-authored handoff
|
|
653
|
-
//
|
|
654
|
-
// spawn path proceeds on the authored handoff exactly as if it had just written it.
|
|
652
|
+
// #5648: an automatic digest must never recompose+supersede a FRESH model-authored handoff —
|
|
653
|
+
// if one exists (<15min, unconsumed), point the baton at THAT and write nothing, so the caller's
|
|
654
|
+
// spawn path proceeds on the authored handoff exactly as if it had just written it. #8263: the
|
|
655
|
+
// defer keys on the TRIGGER — manual-skill/manual-baton IS the model's newer words and must land.
|
|
655
656
|
const fresh = freshAuthoredHandoff(projectName);
|
|
656
|
-
if (fresh) return { deferred: true, file: join(HANDOFF_DIR, `${fresh.id}.json`), record: fresh };
|
|
657
|
+
if (fresh && !MANUAL_TRIGGERS.includes(trigger)) return { deferred: true, file: join(HANDOFF_DIR, `${fresh.id}.json`), record: fresh };
|
|
657
658
|
const stamp = nowSec() || Date.now();
|
|
658
659
|
let gitStatus = "";
|
|
659
660
|
try { gitStatus = execSync("git -C " + JSON.stringify(projectDir) + " status --short 2>/dev/null | head -30", { encoding: "utf8" }).trim(); } catch {}
|
package/hooks/pricing.mjs
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
// trantor — Anthropic API pricing, the single source of truth for the NOTIONAL dollar cost of a
|
|
2
2
|
// Claude Code sub-agent's token usage (it's "notional" because on a Pro/Max plan those tokens are
|
|
3
3
|
// plan-covered, not billed — we still want the number). $/MTok. Re-confirm before trusting old reports.
|
|
4
|
-
export const PRICING_AS_OF = "2026-
|
|
4
|
+
export const PRICING_AS_OF = "2026-09";
|
|
5
5
|
|
|
6
6
|
// in/out = input/output; cw5m/cw1h = cache-WRITE at the 5-minute / 1-hour TTL; cr = cache-READ.
|
|
7
7
|
// (cache write 5m = 1.25× input, 1h = 2× input, read = 0.1× input — encoded directly here.)
|
|
8
8
|
export const ANTHROPIC_PRICING = {
|
|
9
|
-
opus: { in: 5, out: 25, cw5m: 6.25, cw1h: 10, cr: 0.5 },
|
|
10
|
-
|
|
9
|
+
opus: { in: 5, out: 25, cw5m: 6.25, cw1h: 10, cr: 0.5 }, // Opus 5 / 4.8 / 4.7 / 4.6
|
|
10
|
+
"opus-5-5": { in: 4, out: 20, cw5m: 5, cw1h: 8, cr: 0.2 }, // cheaper than Opus 5, and its cache read is 0.05× input, not 0.1×
|
|
11
|
+
sonnet: { in: 2, out: 10, cw5m: 2.5, cw1h: 4, cr: 0.2 }, // Sonnet 5
|
|
12
|
+
"sonnet-4": { in: 3, out: 15, cw5m: 3.75, cw1h: 6, cr: 0.3 }, // Sonnet 4 / 4.5 / 4.6
|
|
11
13
|
haiku: { in: 1, out: 5, cw5m: 1.25, cw1h: 2, cr: 0.1 },
|
|
12
|
-
|
|
14
|
+
fable: { in: 10, out: 50, cw5m: 12.5, cw1h: 20, cr: 0.25 }, // Fable 5 / 5.1, cache read 0.025× input
|
|
15
|
+
"opus-4-1": { in: 15, out: 75, cw5m: 18.75, cw1h: 30, cr: 1.5 }, // legacy numbering — far pricier
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
// Map a transcript model id ("claude-opus-4-8", "claude-sonnet-4-6", …) to a price tier, or null.
|
|
19
|
+
// MOST SPECIFIC FIRST: a generic includes("opus") priced the default Opus 5.5 off the Opus 5 tier (#8443).
|
|
20
|
+
// An unrecognised version stays null — costUsd:null has a reporting path, a wrong dollar figure does not.
|
|
16
21
|
export function tierFor(model) {
|
|
17
22
|
const m = String(model || "").toLowerCase();
|
|
18
23
|
if (/opus-4-1\b|opus-4\.1/.test(m)) return "opus-4-1";
|
|
19
|
-
if (m
|
|
20
|
-
if (
|
|
24
|
+
if (/opus-5-5\b|opus-5\.5/.test(m)) return "opus-5-5";
|
|
25
|
+
if (/opus-(5|4-8|4-7|4-6|4-5)\b/.test(m)) return "opus";
|
|
26
|
+
if (/sonnet-4(-[0-9]+)?\b/.test(m)) return "sonnet-4";
|
|
27
|
+
if (/sonnet-5\b/.test(m)) return "sonnet";
|
|
28
|
+
if (m.includes("fable") || m.includes("mythos")) return "fable";
|
|
21
29
|
if (m.includes("haiku")) return "haiku";
|
|
22
30
|
return null; // unknown → caller emits costUsd:null + a costNote rather than guessing
|
|
23
31
|
}
|