trantor 0.18.82 → 0.18.85
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/cli.mjs +2 -0
- package/bin/crew/state.mjs +17 -2
- package/bin/crew-runner.mjs +142 -31
- package/bin/cut-snapshot.mjs +37 -0
- package/bin/gate.mjs +30 -0
- package/bin/turn-watchdog.mjs +14 -9
- package/bin/wake-nudge.mjs +21 -2
- package/hooks/file-claim.mjs +19 -7
- package/hooks/file-hold.mjs +49 -0
- package/hooks/heartbeat.mjs +2 -1
- package/hooks/hooks.json +10 -1
- package/hooks/lib/git-checkout.mjs +10 -0
- package/hooks/lib/overseer-level-cache.mjs +25 -0
- package/hooks/overseer-warn.mjs +1 -1
- package/hub/auth.mjs +6 -51
- package/hub/overseer.mjs +100 -34
- package/hub/routes/admin.mjs +58 -12
- package/hub/store.mjs +3 -2
- package/lib/duty-nudges.mjs +12 -0
- package/lib/nudge-ledger.mjs +95 -0
- package/lib/overseer.mjs +82 -24
- package/lib/remote-overlap.mjs +193 -0
- package/lib/seat-why.mjs +6 -1
- package/lib/session-liveness.mjs +81 -0
- package/lib/store-pg.mjs +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// CLI-owned records let read-only turns and their sub-agents prove liveness (#10497).
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
|
|
7
|
+
const canonical = p => { try { return realpathSync(p); } catch { return p; } };
|
|
8
|
+
const sql = s => `'${String(s).replaceAll("'", "''")}'`;
|
|
9
|
+
const entries = p => { try { return readdirSync(p, { withFileTypes: true }); } catch { return []; } };
|
|
10
|
+
const mtime = p => { try { return statSync(p).mtimeMs; } catch { return 0; } };
|
|
11
|
+
function logTime(dir, budget = { left: 20000 }) {
|
|
12
|
+
let best = 0;
|
|
13
|
+
for (const e of entries(dir)) {
|
|
14
|
+
if (--budget.left < 0) break;
|
|
15
|
+
const path = join(dir, e.name);
|
|
16
|
+
if (e.isDirectory()) best = Math.max(best, logTime(path, budget));
|
|
17
|
+
else if (e.name.endsWith(".jsonl")) best = Math.max(best, mtime(path));
|
|
18
|
+
}
|
|
19
|
+
return best;
|
|
20
|
+
}
|
|
21
|
+
function query(db, statement) {
|
|
22
|
+
try {
|
|
23
|
+
const r = spawnSync("sqlite3", ["-readonly", db, statement], {
|
|
24
|
+
encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 2000,
|
|
25
|
+
});
|
|
26
|
+
return r.status === 0 ? r.stdout.trim() : "";
|
|
27
|
+
} catch { return ""; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Pin the root once discovered. A fresh root must have been created during this turn;
|
|
31
|
+
// a resumed root is supplied by the runner. Never use global DB/WAL mtimes.
|
|
32
|
+
export function sessionActivityReader({ kind, sid = "", db, workDir, home, startedAt, transcriptDir }) {
|
|
33
|
+
let root = sid, logDir = "";
|
|
34
|
+
const cwd = canonical(workDir);
|
|
35
|
+
return () => {
|
|
36
|
+
if (kind === "opencode") {
|
|
37
|
+
if (!root) root = query(db, `SELECT id FROM session WHERE directory IN (${sql(workDir)},${sql(cwd)})`
|
|
38
|
+
+ ` AND parent_id IS NULL AND time_created>=${Math.floor(startedAt)} ORDER BY time_created DESC LIMIT 1;`);
|
|
39
|
+
if (!/^ses_[A-Za-z0-9]+$/.test(root)) return 0;
|
|
40
|
+
const value = query(db, `WITH RECURSIVE own(id) AS (SELECT id FROM session WHERE id=${sql(root)}`
|
|
41
|
+
+ ` UNION SELECT s.id FROM session s JOIN own ON s.parent_id=own.id)`
|
|
42
|
+
+ ` SELECT MAX(t) FROM (SELECT MAX(time_created,time_updated) t FROM message WHERE session_id IN (SELECT id FROM own)`
|
|
43
|
+
+ ` UNION ALL SELECT MAX(time_created,time_updated) t FROM part WHERE session_id IN (SELECT id FROM own));`);
|
|
44
|
+
return Number(value) || 0;
|
|
45
|
+
}
|
|
46
|
+
if (kind === "claude") {
|
|
47
|
+
if (!root) {
|
|
48
|
+
const newest = entries(transcriptDir).filter(e => e.isFile() && e.name.endsWith(".jsonl"))
|
|
49
|
+
.map(e => ({ name: e.name, ts: mtime(join(transcriptDir, e.name)) }))
|
|
50
|
+
.filter(e => e.ts >= startedAt).sort((a, b) => b.ts - a.ts)[0];
|
|
51
|
+
if (newest) root = newest.name.slice(0, -6);
|
|
52
|
+
}
|
|
53
|
+
if (!root || root.includes("/")) return 0;
|
|
54
|
+
return Math.max(mtime(join(transcriptDir, `${root}.jsonl`)), logTime(join(transcriptDir, root)));
|
|
55
|
+
}
|
|
56
|
+
if (kind !== "kimi") return 0;
|
|
57
|
+
if (!logDir) {
|
|
58
|
+
// kimi-code indexes the actual workdir and session directory; avoid guessing its hash.
|
|
59
|
+
try {
|
|
60
|
+
const rows = readFileSync(join(home, ".kimi-code", "session_index.jsonl"), "utf8").trim().split("\n");
|
|
61
|
+
for (const line of rows.reverse()) {
|
|
62
|
+
let row; try { row = JSON.parse(line); } catch { continue; }
|
|
63
|
+
if (canonical(row.workDir || "") !== cwd || !row.sessionDir || (root && row.sessionId !== root)) continue;
|
|
64
|
+
if (!root && mtime(row.sessionDir) < startedAt) continue;
|
|
65
|
+
root = row.sessionId; logDir = row.sessionDir; break;
|
|
66
|
+
}
|
|
67
|
+
} catch {}
|
|
68
|
+
// Python kimi-cli uses md5(canonical cwd)/session-id.
|
|
69
|
+
if (!logDir) {
|
|
70
|
+
const parent = join(home, ".kimi", "sessions", createHash("md5").update(cwd).digest("hex"));
|
|
71
|
+
if (root && !root.includes("/")) logDir = join(parent, root);
|
|
72
|
+
else {
|
|
73
|
+
const newest = entries(parent).filter(e => e.isDirectory()).map(e => ({ name: e.name, ts: mtime(join(parent, e.name)) }))
|
|
74
|
+
.filter(e => e.ts >= startedAt).sort((a, b) => b.ts - a.ts)[0];
|
|
75
|
+
if (newest) { root = newest.name; logDir = join(parent, root); }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return logDir ? logTime(logDir) : 0;
|
|
80
|
+
};
|
|
81
|
+
}
|
package/lib/store-pg.mjs
CHANGED
|
@@ -154,6 +154,7 @@ function kvFromState(state) {
|
|
|
154
154
|
proposals: state.proposals || [],
|
|
155
155
|
contractReap: state.contractReap || {},
|
|
156
156
|
meta: {
|
|
157
|
+
overseerState: state.overseerState || {},
|
|
157
158
|
taskSeq: Number(state.taskSeq || 0),
|
|
158
159
|
verifyGateSeq: Number(state.verifyGateSeq || 0),
|
|
159
160
|
proposalSeq: Number(state.proposalSeq || 0),
|
|
@@ -731,6 +732,7 @@ export class PgStore {
|
|
|
731
732
|
inviteTokens: meta.inviteTokens && typeof meta.inviteTokens === "object" ? meta.inviteTokens : {},
|
|
732
733
|
instances: meta.instances && typeof meta.instances === "object" ? meta.instances : {},
|
|
733
734
|
focus: kv.focus && typeof kv.focus === "object" ? kv.focus : {},
|
|
735
|
+
overseerState: meta.overseerState || {},
|
|
734
736
|
orgPolicy: kv.orgPolicy && typeof kv.orgPolicy === "object" ? kv.orgPolicy : {},
|
|
735
737
|
contractReap: kv.contractReap && typeof kv.contractReap === "object" ? kv.contractReap : {},
|
|
736
738
|
eventSeq: Math.max(Number(eventMaxRow?.rows?.[0]?.max_id || 0), Number(meta.eventSeq || 0)),
|