trantor 0.17.93 → 0.17.95
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/hooks/lib/handoff.mjs +50 -9
- package/hub.mjs +7 -1
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trantor",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.95",
|
|
4
4
|
"description": "Trantor \u2014 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/hooks/lib/handoff.mjs
CHANGED
|
@@ -187,9 +187,26 @@ function digest(turns, budget = 56_000) {
|
|
|
187
187
|
return out;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
190
|
+
// Where scrooge actually lives. `command -v` alone was the bug: it installs into ~/.local/bin,
|
|
191
|
+
// which is NOT on a default PATH, so every handoff written from a hook, launchd job or precompact
|
|
192
|
+
// silently failed the check and dumped raw transcript instead. Those are precisely the automatic
|
|
193
|
+
// paths, so the summarizer was missing exactly when nobody was watching. Resolve to an absolute
|
|
194
|
+
// path and exec THAT.
|
|
195
|
+
const SCROOGE_DIRS = [
|
|
196
|
+
join(homedir(), ".local", "bin"),
|
|
197
|
+
"/opt/homebrew/bin",
|
|
198
|
+
"/usr/local/bin",
|
|
199
|
+
"/usr/bin",
|
|
200
|
+
];
|
|
201
|
+
function resolveScrooge() {
|
|
202
|
+
if (process.env.TRANTOR_NO_SCROOGE === "1") return ""; // opt out (tests / no-LLM summary)
|
|
203
|
+
if (process.env.TRANTOR_SCROOGE_BIN && existsSync(process.env.TRANTOR_SCROOGE_BIN)) return process.env.TRANTOR_SCROOGE_BIN;
|
|
204
|
+
try {
|
|
205
|
+
const p = execSync("command -v scrooge", { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
206
|
+
if (p && existsSync(p)) return p;
|
|
207
|
+
} catch {}
|
|
208
|
+
for (const d of SCROOGE_DIRS) { const p = join(d, "scrooge"); if (existsSync(p)) return p; }
|
|
209
|
+
return "";
|
|
193
210
|
}
|
|
194
211
|
|
|
195
212
|
export function buildSummary(transcriptPath) {
|
|
@@ -198,14 +215,38 @@ export function buildSummary(transcriptPath) {
|
|
|
198
215
|
try { convo = digest(collectTurns(transcriptPath)); } catch { convo = ""; }
|
|
199
216
|
if (!convo) return "*(transcript unreadable)*";
|
|
200
217
|
const sys = "You are writing a SESSION HANDOFF so a fresh Claude Code session can take over without losing context. The text spans an entire (possibly multi-hour) session: opening turns, an even sample of the middle, and the recent tail. Produce a concise but COMPLETE markdown handoff with these sections: TASK (what we're doing + the goal), STATE (done / in-progress), KEY DECISIONS, OPEN THREADS & NEXT STEPS (concrete actions), KEY FILES & locations (exact paths). Be specific. Cover the whole arc, not just the end. Do not pad.";
|
|
201
|
-
|
|
218
|
+
// Cut the raw tail on a TURN boundary. A blind slice(-12000) opens mid-sentence, which is how the
|
|
219
|
+
// 2026-08-24 handoff began, and a successor cannot tell a truncated thought from a complete one.
|
|
220
|
+
const tail = (n) => {
|
|
221
|
+
// Only trim to a turn boundary when we ACTUALLY truncated. When the whole digest fits, trimming
|
|
222
|
+
// would throw away the session's opening — which is the part a successor needs most, and which
|
|
223
|
+
// test-handoff.mjs rightly insists on.
|
|
224
|
+
if (convo.length <= n) return convo;
|
|
225
|
+
const cut = convo.slice(-n);
|
|
226
|
+
const b = cut.indexOf("\n\n");
|
|
227
|
+
return b > 0 && b < 2000 ? cut.slice(b + 2) : cut;
|
|
228
|
+
};
|
|
229
|
+
// Say WHICH failure this was. One string for "not installed" and "the call died" meant nobody
|
|
230
|
+
// could tell a missing tool from a broken one, and the reason only ever reached stderr.
|
|
231
|
+
const degraded = (why) =>
|
|
232
|
+
`*(⚠️ DEGRADED HANDOFF — this is a raw transcript tail, not a written summary.*\n`
|
|
233
|
+
+ `*Reason: ${why}. It may open mid-thought and it OMITS anything older than the tail;*\n`
|
|
234
|
+
+ `*treat the project's memory files as the reliable record and re-read them before acting.)*\n\n${tail(12000)}`;
|
|
235
|
+
|
|
236
|
+
const bin = resolveScrooge();
|
|
237
|
+
if (bin) {
|
|
202
238
|
try {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
239
|
+
// 29s observed summarizing a 56KB digest, so 60s left almost no headroom on a slow provider.
|
|
240
|
+
return execSync(`${JSON.stringify(bin)} -t summarize -d medium --system ${JSON.stringify(sys)}`, {
|
|
241
|
+
input: convo, encoding: "utf8", timeout: 180_000, maxBuffer: 8 * 1024 * 1024,
|
|
242
|
+
}).trim() || degraded("the summarizer returned nothing");
|
|
243
|
+
} catch (e) {
|
|
244
|
+
const why = `the summarizer failed: ${String(e?.message || e).slice(0, 200)}`;
|
|
245
|
+
process.stderr.write(`[trantor] scrooge summarize failed: ${e?.message}\n`);
|
|
246
|
+
return degraded(why);
|
|
247
|
+
}
|
|
207
248
|
}
|
|
208
|
-
return
|
|
249
|
+
return degraded("no summarizer is installed (scrooge was not found on PATH or in the usual locations)");
|
|
209
250
|
}
|
|
210
251
|
|
|
211
252
|
// The exact recent exchange, VERBATIM (not summarized/sampled) — so a baton-pass handoff carries the
|
package/hub.mjs
CHANGED
|
@@ -388,7 +388,13 @@ function dutyTick() {
|
|
|
388
388
|
const floor = now() - 24 * 3600 * 1000; // never escalate ancient history
|
|
389
389
|
for (const m of state.messages) {
|
|
390
390
|
if (m.ts > cutoff || m.ts < floor) continue;
|
|
391
|
-
|
|
391
|
+
// `hub:*` is the hub's own pseudo-identity, not a session: nothing polls it and nothing ever
|
|
392
|
+
// will, so a message addressed there can never be "delivered". Escalating it is a category
|
|
393
|
+
// error that feeds itself — the duty seat acks the escalation to hub:duty, that ack is
|
|
394
|
+
// undelivered too, and since dutyEscalated prunes its oldest ids at 5,000 the same ones come
|
|
395
|
+
// back around. Reported from the seat as "a fresh identical echo every stop-hook cycle".
|
|
396
|
+
// Skipping the FROM side was already here; the TO side is the half that loops.
|
|
397
|
+
if (!m.to || m.to === "all" || m.to === DUTY_SESSION || m.from === "hub:duty" || m.to.startsWith("hub:")) continue;
|
|
392
398
|
if (dutyEscalated.has(m.id)) continue;
|
|
393
399
|
if ((state.peers[m.to]?.deliveredUpTo || 0) >= m.id) continue;
|
|
394
400
|
dutyEscalated.add(m.id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trantor",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.95",
|
|
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 bin/slop-gate.mjs --surface desktop/src && node test.mjs && node test-scenarios.mjs && node test-failure.mjs && node test-redelivery.mjs && node test-crew-completion.mjs && node test-contracts.mjs && node test-handoff.mjs && node test-agents.mjs && node test-update.mjs && node test-handoff-guard.mjs && node test-baton-latest.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-message-re.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-duty-seat.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-identity-drift.mjs && node test-seats.mjs && node test-seat-identity.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-crew-completion.mjs && node test-contracts.mjs && node test-handoff.mjs && node test-handoff-summarizer.mjs && node test-agents.mjs && node test-update.mjs && node test-handoff-guard.mjs && node test-baton-latest.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-message-re.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-duty-seat.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-identity-drift.mjs && node test-inbox-staleness.mjs && node test-seats.mjs && node test-seat-identity.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": [
|