trantor 0.18.76 → 0.18.77
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/agents.mjs +9 -14
- package/lib/subagent-manifest.mjs +24 -9
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trantor",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.77",
|
|
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/agents.mjs
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// trantor agents [sessionId] [--json] — the LIVE sub-agent manifest for a
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// on disk? Derived fresh from the on-disk transcripts every run (so it reflects CURRENT disk,
|
|
7
|
-
// catching files an agent finished that were later clobbered — the 2026-06-21 kill corrupted a
|
|
8
|
-
// completed 30KB lib down to a 17-byte stub).
|
|
9
|
-
//
|
|
10
|
-
// trantor agents → the session of the newest handoff for THIS project (the
|
|
11
|
-
// predecessor a fresh session is taking over from)
|
|
12
|
-
// trantor agents <sessionId> → that specific session
|
|
13
|
-
// trantor agents --json → structured manifest (for tools)
|
|
2
|
+
// trantor agents [sessionId] [--json] [--since <epochMs>] — the LIVE sub-agent manifest for a
|
|
3
|
+
// session: what was each sub-agent tasked with, did it return, what did it write, did it survive
|
|
4
|
+
// on disk — re-derived fresh from the transcripts every run. `--since <epochMs>` (#10007) reads
|
|
5
|
+
// agents launched before that instant "stale", never "in-flight"; the app polls this for liveness.
|
|
14
6
|
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
15
7
|
import { join, basename } from "node:path";
|
|
16
8
|
import { homedir } from "node:os";
|
|
@@ -18,7 +10,10 @@ import { deriveSubagentManifest, formatSubagentManifest, resolveTranscriptForSid
|
|
|
18
10
|
|
|
19
11
|
const args = process.argv.slice(2);
|
|
20
12
|
const json = args.includes("--json");
|
|
21
|
-
const
|
|
13
|
+
const sinceIdx = args.indexOf("--since");
|
|
14
|
+
const sinceVal = sinceIdx !== -1 ? args[sinceIdx + 1] : undefined;
|
|
15
|
+
const sinceMs = sinceVal != null ? Number(sinceVal) : undefined;
|
|
16
|
+
const sid = args.find((a) => !a.startsWith("--") && a !== sinceVal);
|
|
22
17
|
|
|
23
18
|
const HANDOFF_DIR = join(process.env.RELAY_DATA_DIR || join(homedir(), ".agent-bus"), "handoffs");
|
|
24
19
|
|
|
@@ -55,7 +50,7 @@ if (sid) {
|
|
|
55
50
|
projectRoot = h.project || projectRoot;
|
|
56
51
|
}
|
|
57
52
|
|
|
58
|
-
const manifest = deriveSubagentManifest(transcript, { projectRoot });
|
|
53
|
+
const manifest = deriveSubagentManifest(transcript, { projectRoot, sinceMs: Number.isFinite(sinceMs) ? sinceMs : undefined });
|
|
59
54
|
if (json) {
|
|
60
55
|
process.stdout.write(JSON.stringify(manifest, null, 2) + "\n");
|
|
61
56
|
} else {
|
|
@@ -68,11 +68,12 @@ function reconcileFile(absPath, agentBytes, projectRoot) {
|
|
|
68
68
|
return { path: shortPath(absPath, projectRoot), agentBytes, onDiskNow, suspect };
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
function analyzeAgent(meta, completedIds, projectRoot) {
|
|
71
|
+
function analyzeAgent(meta, completedIds, projectRoot, sinceMs) {
|
|
72
72
|
const wrote = new Map(); // absPath → last-write byte length
|
|
73
|
-
let lastMs = 0, result = "";
|
|
73
|
+
let lastMs = 0, launchMs = 0, result = "";
|
|
74
74
|
for (const r of parseLines(meta.jsonlPath)) {
|
|
75
75
|
const ts = r.timestamp ? Date.parse(r.timestamp) || 0 : 0;
|
|
76
|
+
if (!launchMs && ts) launchMs = ts; // jsonl is append-ordered: first timestamped line = launch
|
|
76
77
|
if (ts > lastMs) lastMs = ts;
|
|
77
78
|
const c = r?.message?.content;
|
|
78
79
|
if (!Array.isArray(c)) continue;
|
|
@@ -84,13 +85,22 @@ function analyzeAgent(meta, completedIds, projectRoot) {
|
|
|
84
85
|
if (b?.type === "text") { const text = asString(b.text)?.trim(); if (text) result = text; }
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
|
-
|
|
88
|
+
// #10007 liveness: "in-flight" means launched and never returned a result. But an agent whose
|
|
89
|
+
// launch predates the session's CURRENT process (sinceMs = process start) can never return —
|
|
90
|
+
// its turn died with the old process — so it reads "stale" and never counts as live.
|
|
91
|
+
const returned = meta.toolUseId && completedIds.has(meta.toolUseId);
|
|
92
|
+
let status = returned ? "completed" : "in-flight";
|
|
93
|
+
if (!returned) {
|
|
94
|
+
if (!launchMs) { try { launchMs = statSync(meta.jsonlPath).birthtimeMs || 0; } catch {} }
|
|
95
|
+
if (sinceMs != null && launchMs < sinceMs) status = "stale";
|
|
96
|
+
}
|
|
88
97
|
return {
|
|
89
98
|
name: meta.name || basename(meta.jsonlPath).replace(/\.jsonl$/, ""),
|
|
90
99
|
agentType: meta.agentType || "",
|
|
91
100
|
task: meta.description || "",
|
|
92
101
|
workflow: meta.workflow || null,
|
|
93
102
|
status,
|
|
103
|
+
launchMs: launchMs || null,
|
|
94
104
|
wrote: [...wrote.entries()].map(([p, n]) => reconcileFile(p, n, projectRoot)),
|
|
95
105
|
lastActivity: lastMs ? new Date(lastMs).toISOString() : null,
|
|
96
106
|
lastActivityMs: lastMs,
|
|
@@ -99,10 +109,12 @@ function analyzeAgent(meta, completedIds, projectRoot) {
|
|
|
99
109
|
};
|
|
100
110
|
}
|
|
101
111
|
|
|
102
|
-
// Derive the full manifest for a session from its parent transcript path. projectRoot
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
112
|
+
// Derive the full manifest for a session from its parent transcript path. projectRoot shortens
|
|
113
|
+
// displayed file paths. sinceMs (epoch ms, #10007) marks agents launched before it "stale" instead
|
|
114
|
+
// of "in-flight" — pass the session process's start when the answer feeds a LIVE claim (ACTIVE
|
|
115
|
+
// NOW), never for a handoff record. Returns { sessionId, subagents[], counts }.
|
|
116
|
+
export function deriveSubagentManifest(parentTranscript, { projectRoot, sinceMs } = {}) {
|
|
117
|
+
const out = { sessionId: "", subagents: [], counts: { total: 0, completed: 0, inFlight: 0, stale: 0, suspectFiles: 0 } };
|
|
106
118
|
try {
|
|
107
119
|
if (!parentTranscript || !existsSync(parentTranscript)) return out;
|
|
108
120
|
const sid = basename(parentTranscript).replace(/\.jsonl$/i, "");
|
|
@@ -118,12 +130,13 @@ export function deriveSubagentManifest(parentTranscript, { projectRoot } = {}) {
|
|
|
118
130
|
}
|
|
119
131
|
|
|
120
132
|
const metas = collectMetas(subdir);
|
|
121
|
-
out.subagents = metas.map((m) => analyzeAgent(m, completedIds, projectRoot))
|
|
133
|
+
out.subagents = metas.map((m) => analyzeAgent(m, completedIds, projectRoot, sinceMs))
|
|
122
134
|
.sort((a, b) => (a.lastActivityMs || 0) - (b.lastActivityMs || 0));
|
|
123
135
|
|
|
124
136
|
out.counts.total = out.subagents.length;
|
|
125
137
|
out.counts.completed = out.subagents.filter((s) => s.status === "completed").length;
|
|
126
138
|
out.counts.inFlight = out.subagents.filter((s) => s.status === "in-flight").length;
|
|
139
|
+
out.counts.stale = out.subagents.filter((s) => s.status === "stale").length;
|
|
127
140
|
out.counts.suspectFiles = out.subagents.reduce((n, s) => n + s.wrote.filter((w) => w.suspect).length, 0);
|
|
128
141
|
} catch {}
|
|
129
142
|
return out;
|
|
@@ -140,7 +153,9 @@ export function formatSubagentManifest(m, { heading = true } = {}) {
|
|
|
140
153
|
}
|
|
141
154
|
}
|
|
142
155
|
for (const s of m.subagents) {
|
|
143
|
-
const badge = s.status === "completed" ? "✅ completed"
|
|
156
|
+
const badge = s.status === "completed" ? "✅ completed"
|
|
157
|
+
: s.status === "stale" ? "⏸ never returned, and launched before the current session process — stale"
|
|
158
|
+
: "🛑 IN-FLIGHT at handoff";
|
|
144
159
|
L.push("");
|
|
145
160
|
L.push(`• ${s.name} [${s.agentType}]${s.workflow ? ` · wf:${s.workflow}` : ""} ${badge}`);
|
|
146
161
|
if (s.task) L.push(` task: ${s.task}`);
|