trantor 0.18.94 → 0.18.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/bin/crew-runner.mjs +10 -6
- package/hooks/lib/memory-start.mjs +10 -5
- package/lib/model-catalog.mjs +9 -0
- package/lib/seat-compaction.mjs +34 -11
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trantor",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.95",
|
|
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/crew-runner.mjs
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
} from "../lib/turn-policy.mjs";
|
|
30
30
|
import { snapshotBeforeCut } from "./cut-snapshot.mjs";
|
|
31
31
|
import { ocTurnUsage, dshTurnUsage, usageTotal } from "../lib/turn-usage.mjs";
|
|
32
|
-
import { ocCompactionContent } from "../lib/seat-compaction.mjs";
|
|
32
|
+
import { ocCompactionContent, ocCompactionLogLine } from "../lib/seat-compaction.mjs";
|
|
33
33
|
import { listContainers, newContainers, recordContainers, statePathFor as dockerStatePathFor, sweep as dockerSweep } from "../lib/docker-janitor.mjs";
|
|
34
34
|
import {
|
|
35
35
|
auditDutyNudges, claimDutyNudges, claudeTranscriptDir, dutyEscalations, dutyNudgeDirective,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
} from "../lib/duty-nudges.mjs";
|
|
38
38
|
import { reserveNudgeIds, releaseNudgeIds } from "../lib/nudge-ledger.mjs";
|
|
39
39
|
import { dutyRecipientResolver } from "../lib/duty-recipient.mjs";
|
|
40
|
-
import { cliEffortFlag } from "../lib/model-catalog.mjs";
|
|
40
|
+
import { cliEffortFlag, contextWindow } from "../lib/model-catalog.mjs";
|
|
41
41
|
import {
|
|
42
42
|
BREAKER_WINDOW, STATE_ENV, TURN_RESULT_SCHEMA,
|
|
43
43
|
breakerVerdict, describeTurn, hasJsonSchemaFlag, parseEnvelope, renderCardTail, runStep,
|
|
@@ -758,10 +758,12 @@ let cardStartedAt = releaseState?.cardStartedAt || 0;
|
|
|
758
758
|
let budgetStop = releaseState?.budgetStop || null; // { card, tokens } once the card crossed
|
|
759
759
|
let budgetRetryAt = releaseState?.budgetRetryAt || 0; // paces the paused card's OWN redeliveries
|
|
760
760
|
|
|
761
|
-
// #11559: the compaction block injected to opencode-family seats via OPENCODE_CONFIG_CONTENT
|
|
762
|
-
//
|
|
763
|
-
//
|
|
764
|
-
|
|
761
|
+
// #11559: the compaction block injected to opencode-family seats via OPENCODE_CONFIG_CONTENT (""
|
|
762
|
+
// injects nothing: TRANTOR_OC_COMPACTION=off or invalid JSON). opencode has no absolute-threshold
|
|
763
|
+
// knob — auto-compact fires at window − reserved (opencode.ai/config.json; calculateCeiling in
|
|
764
|
+
// core/src/session/compaction.ts) — so reserved is sized from the seat model's catalog window.
|
|
765
|
+
const OC_COMPACTION_WINDOW = contextWindow(MODEL);
|
|
766
|
+
const OC_COMPACTION = ocCompactionContent(process.env, OC_COMPACTION_WINDOW);
|
|
765
767
|
|
|
766
768
|
let sid = releaseState?.sid || "";
|
|
767
769
|
// #6206: the watchdog is DETACHED, so a runner that dies without ending it leaves an orphan
|
|
@@ -836,6 +838,8 @@ async function runTurn(prompt, isFirst, trigger = "kickoff", opts = {}) {
|
|
|
836
838
|
log(`turn starting (${isFirst ? "fresh session" : "resume"})${MODEL ? ` · model=${MODEL}` : ""}`);
|
|
837
839
|
// #7777: the one effort line — which effort parameters were set, or that the model is uncatalogued.
|
|
838
840
|
if (EFFORT && EFFORT_FLAG.text) log(EFFORT_FLAG.text);
|
|
841
|
+
// #11559: the one compaction line — only where the block actually rides the spawn.
|
|
842
|
+
if (cli.pinned && OC_COMPACTION) log(ocCompactionLogLine(OC_COMPACTION_WINDOW));
|
|
839
843
|
cmuxStatus("building", "#4a90d9", "hammer", { priority: 50 }); herdrAgent("working");
|
|
840
844
|
// inherit stdio so the window shows the agent working live; also capture for sid-parsing.
|
|
841
845
|
// Tee stderr to ERRF (still shown live in the window) so a failed turn can be classified.
|
|
@@ -4,6 +4,8 @@ import { isMainThread, parentPort, Worker, workerData } from "node:worker_thread
|
|
|
4
4
|
import { gitCheckoutRoot } from "./git-checkout.mjs";
|
|
5
5
|
|
|
6
6
|
const SLOW = "pin check skipped (slow), run trantor memory check";
|
|
7
|
+
// Tests on a loaded CI runner raise this; the hook itself keeps 500 ms.
|
|
8
|
+
const BUDGET_MS = Number(process.env.TRANTOR_PIN_CHECK_BUDGET_MS) || 500;
|
|
7
9
|
|
|
8
10
|
export function memoryStartLine(project) {
|
|
9
11
|
return new Promise(resolveLine => {
|
|
@@ -17,7 +19,7 @@ export function memoryStartLine(project) {
|
|
|
17
19
|
if (worker) void worker.terminate().catch(() => {});
|
|
18
20
|
};
|
|
19
21
|
// Keep synchronous graph parsing and file reads off the hook's deadline timer.
|
|
20
|
-
const timer = setTimeout(() => finish(SLOW),
|
|
22
|
+
const timer = setTimeout(() => finish(SLOW), BUDGET_MS);
|
|
21
23
|
try {
|
|
22
24
|
worker = new Worker(new URL(import.meta.url), { workerData: { project } });
|
|
23
25
|
worker.once("message", finish);
|
|
@@ -37,7 +39,7 @@ async function cachedMemoryLine(directory) {
|
|
|
37
39
|
const graph = JSON.parse(readFileSync(graphPath, "utf8"));
|
|
38
40
|
const nodes = new Map(graph.nodes.map(node => [node.id, node]));
|
|
39
41
|
const graphTime = statSync(graphPath).mtimeMs;
|
|
40
|
-
|
|
42
|
+
const unchecked = new Set();
|
|
41
43
|
const stale = new Set(), broken = new Set();
|
|
42
44
|
const notes = readdirSync(memoryDir, { withFileTypes: true })
|
|
43
45
|
.filter(entry => entry.isFile() && entry.name.endsWith(".md") && entry.name !== "MEMORY.md");
|
|
@@ -45,7 +47,7 @@ async function cachedMemoryLine(directory) {
|
|
|
45
47
|
try {
|
|
46
48
|
const pins = notePins(readFileSync(join(memoryDir, note.name), "utf8"));
|
|
47
49
|
for (const pin of pins) {
|
|
48
|
-
if (pinFileNewer(project, pin, graphTime)) { unchecked
|
|
50
|
+
if (pinFileNewer(project, pin, graphTime)) { unchecked.add(note.name); continue; }
|
|
49
51
|
const { status } = checkPin(pin, project, nodes);
|
|
50
52
|
if (status === "stale") stale.add(note.name);
|
|
51
53
|
if (status === "broken") broken.add(note.name);
|
|
@@ -53,8 +55,11 @@ async function cachedMemoryLine(directory) {
|
|
|
53
55
|
} catch { /* A concurrent edit or invalid span must not suppress other notes. */ }
|
|
54
56
|
}
|
|
55
57
|
const names = [...new Set([...stale, ...broken])].sort().map(name => name.replace(/[\s\x00-\x1f\x7f]+/g, " "));
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
+
const parts = [];
|
|
59
|
+
if (names.length) parts.push(`${stale.size} stale, ${broken.size} broken notes: ${names.join(", ")}`);
|
|
60
|
+
if (unchecked.size) parts.push(`${unchecked.size} pinned notes unchecked (graph older than code)`);
|
|
61
|
+
if (!parts.length) return "";
|
|
62
|
+
return `${parts.join("; ")}${unchecked.size ? "," : ";"} run trantor memory check`;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
if (!isMainThread) {
|
package/lib/model-catalog.mjs
CHANGED
|
@@ -40,6 +40,15 @@ export function lookup(modelId, cat = loadCatalog()) {
|
|
|
40
40
|
return uncatalogued(id);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// The model's context window from its catalog entry: a positive finite number, or undefined
|
|
44
|
+
// when the id is uncatalogued or its context is absent/invalid — callers keep their own
|
|
45
|
+
// default instead of guessing a window.
|
|
46
|
+
export function contextWindow(modelId, cat = loadCatalog()) {
|
|
47
|
+
const entry = lookup(modelId, cat);
|
|
48
|
+
const context = entry.found ? entry.context : undefined;
|
|
49
|
+
return Number.isFinite(context) && context > 0 ? context : undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
// The request parameters one difficulty level maps to for one API kind. Returns null when the
|
|
44
53
|
// model is uncatalogued (or the level is missing); an EMPTY object means catalogued but the
|
|
45
54
|
// model takes no effort parameters (provider default — e.g. K2.7 Code thinks always).
|
package/lib/seat-compaction.mjs
CHANGED
|
@@ -1,29 +1,45 @@
|
|
|
1
|
-
// #11559: seats run long cards in
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// #11559: seats run long cards in one opencode session (#6134), and opencode auto-compacts only at
|
|
2
|
+
// window − reserved — there is NO absolute-threshold knob (opencode.ai/config.json,
|
|
3
|
+
// additionalProperties:false; calculateCeiling in packages/core/src/session/compaction.ts). So the
|
|
4
|
+
// seat target "compact near 100K" is reached by sizing reserved from the model's window.
|
|
5
5
|
|
|
6
6
|
export const OC_COMPACTION_ENV = "TRANTOR_OC_COMPACTION";
|
|
7
|
+
// The context level a seat card should stay near: on a 1M-window model reserved becomes 900K
|
|
8
|
+
// so auto-compact fires at ~100K, not the ~936K a fixed 64K buffer leaves.
|
|
9
|
+
export const OC_COMPACT_AT = 100_000;
|
|
10
|
+
// The buffer floor: kept when the model's window is unknown, or smaller than would still
|
|
11
|
+
// leave OC_COMPACT_AT free.
|
|
12
|
+
export const OC_RESERVED_FLOOR = 64_000;
|
|
7
13
|
|
|
8
14
|
// The seat default: compaction on, old tool outputs pruned, a small recent window preserved.
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// TRANTOR_OC_COMPACTION=<json>, or set it to "off" to ship no config content at all.
|
|
15
|
+
// Override the whole block with TRANTOR_OC_COMPACTION=<json>, or set it to "off" to ship no
|
|
16
|
+
// config content at all.
|
|
12
17
|
export const OC_COMPACTION_DEFAULT = {
|
|
13
18
|
auto: true,
|
|
14
19
|
prune: true,
|
|
15
20
|
tail_turns: 4,
|
|
16
21
|
preserve_recent_tokens: 24_000,
|
|
17
|
-
reserved:
|
|
22
|
+
reserved: OC_RESERVED_FLOOR,
|
|
18
23
|
};
|
|
19
24
|
|
|
25
|
+
// reservedForContext(window) — the buffer that makes opencode compact near OC_COMPACT_AT, or
|
|
26
|
+
// undefined when the window is unknown/invalid (the caller keeps the floor default).
|
|
27
|
+
export function reservedForContext(window) {
|
|
28
|
+
if (!Number.isFinite(window) || window <= 0) return undefined;
|
|
29
|
+
return Math.max(OC_RESERVED_FLOOR, window - OC_COMPACT_AT);
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
/// A JSON string for OPENCODE_CONFIG_CONTENT, or "" when nothing should be injected: "off" opts
|
|
21
33
|
/// out, invalid JSON opts out loudly at the caller (never arms a half-parsed block), custom JSON
|
|
22
|
-
/// is wrapped as { compaction: <the value> } so the env stays one knob.
|
|
23
|
-
|
|
34
|
+
/// is wrapped as { compaction: <the value> } so the env stays one knob. Given the model's catalog
|
|
35
|
+
/// window the DEFAULT block's reserved is sized; a custom TRANTOR_OC_COMPACTION wins verbatim.
|
|
36
|
+
export function ocCompactionContent(env = process.env, window = undefined) {
|
|
24
37
|
const raw = String(env[OC_COMPACTION_ENV] || "").trim();
|
|
25
38
|
if (raw === "off") return "";
|
|
26
|
-
if (!raw)
|
|
39
|
+
if (!raw) {
|
|
40
|
+
const reserved = reservedForContext(window) ?? OC_COMPACTION_DEFAULT.reserved;
|
|
41
|
+
return JSON.stringify({ compaction: { ...OC_COMPACTION_DEFAULT, reserved } });
|
|
42
|
+
}
|
|
27
43
|
let parsed;
|
|
28
44
|
try { parsed = JSON.parse(raw); } catch { return ""; }
|
|
29
45
|
// SAFETY: the operator's raw JSON arrives here untyped — a bare number, string or null must
|
|
@@ -32,3 +48,10 @@ export function ocCompactionContent(env = process.env) {
|
|
|
32
48
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return "";
|
|
33
49
|
return JSON.stringify({ compaction: parsed });
|
|
34
50
|
}
|
|
51
|
+
|
|
52
|
+
// The one per-turn runner line: what the injected block will actually do for this seat.
|
|
53
|
+
export function ocCompactionLogLine(window = undefined) {
|
|
54
|
+
const reserved = reservedForContext(window);
|
|
55
|
+
if (reserved === undefined) return `compaction: reserved=${OC_RESERVED_FLOOR} (floor — model window unknown)`;
|
|
56
|
+
return `compaction: reserved=${reserved} — auto-compact fires near ${OC_COMPACT_AT} tokens (window ${window} − reserved; opencode has no absolute-threshold knob)`;
|
|
57
|
+
}
|