@jentrix/runner 0.5.20 → 0.5.21
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/README.md +2 -2
- package/dist/chunk-UN63BYMS.js +7 -0
- package/dist/{chunk-FWAHAHAM.js.map → chunk-UN63BYMS.js.map} +1 -1
- package/dist/runner-cli.js +2 -2
- package/dist/{session-host-P6W3PH3S.js → session-host-L6XF4T2P.js} +163 -9
- package/dist/session-host-L6XF4T2P.js.map +7 -0
- package/dist/workflow-runner.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-FWAHAHAM.js +0 -7
- package/dist/session-host-P6W3PH3S.js.map +0 -7
package/README.md
CHANGED
|
@@ -132,10 +132,10 @@ Setup:
|
|
|
132
132
|
|
|
133
133
|
| Context loaded up front | Bytes | ≈ Tokens |
|
|
134
134
|
| --- | --- | --- |
|
|
135
|
-
| MCP agent — all 242 tool definitions |
|
|
135
|
+
| MCP agent — all 242 tool definitions | 375 KB | ~101k |
|
|
136
136
|
| CLI agent — `CLI_STANDUP_SYSTEM_PROMPT` only | 2 KB | ~510 |
|
|
137
137
|
|
|
138
|
-
That is a **~
|
|
138
|
+
That is a **~199× smaller** Jentrix-specific up-front context.
|
|
139
139
|
<!-- END GENERATED: mcp-tool-count:agent-token-cost -->
|
|
140
140
|
|
|
141
141
|
Reproduce the two numbers directly:
|
package/dist/runner-cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
RUNNER_VERSION
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UN63BYMS.js";
|
|
5
5
|
|
|
6
6
|
// runner-cli.ts
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -219,7 +219,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
219
219
|
return runConfiguredRunner(args.includes("--once"));
|
|
220
220
|
}
|
|
221
221
|
if (command === "session-run" && (args.includes("--plan-stdin") || args.includes("--plan-file"))) {
|
|
222
|
-
const { runSessionHost } = await import("./session-host-
|
|
222
|
+
const { runSessionHost } = await import("./session-host-L6XF4T2P.js");
|
|
223
223
|
let raw;
|
|
224
224
|
const planFile = valueAfter(args, "--plan-file");
|
|
225
225
|
if (planFile) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RUNNER_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UN63BYMS.js";
|
|
4
4
|
import {
|
|
5
5
|
appendHookEvent,
|
|
6
6
|
readHookLines,
|
|
@@ -180,6 +180,126 @@ function serializeSessionEvent(event) {
|
|
|
180
180
|
`;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
// lib/session-skeleton.ts
|
|
184
|
+
var SKELETON_VERSION = 1;
|
|
185
|
+
var MAX_SKELETON_BYTES = 32 * 1024;
|
|
186
|
+
var MAX_DISTINCT_TOOLS = 100;
|
|
187
|
+
var MAX_TRACKED_FILES = 300;
|
|
188
|
+
var MAX_HOURLY_BUCKETS = 500;
|
|
189
|
+
var MAX_PATH_CHARS = 300;
|
|
190
|
+
var PATH_KEYS = ["file_path", "path", "notebook_path", "filePath"];
|
|
191
|
+
function pathOf(value) {
|
|
192
|
+
if (typeof value !== "string") return null;
|
|
193
|
+
const trimmed = value.trim();
|
|
194
|
+
if (!trimmed || /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) return null;
|
|
195
|
+
return trimmed.slice(0, MAX_PATH_CHARS);
|
|
196
|
+
}
|
|
197
|
+
var SessionSkeleton = class {
|
|
198
|
+
constructor(provider) {
|
|
199
|
+
this.provider = provider;
|
|
200
|
+
}
|
|
201
|
+
provider;
|
|
202
|
+
turns = 0;
|
|
203
|
+
firstEventAt = null;
|
|
204
|
+
lastEventAt = null;
|
|
205
|
+
eventCounts = /* @__PURE__ */ new Map();
|
|
206
|
+
toolCounts = /* @__PURE__ */ new Map();
|
|
207
|
+
files = /* @__PURE__ */ new Set();
|
|
208
|
+
filesOverflow = 0;
|
|
209
|
+
hourly = /* @__PURE__ */ new Map();
|
|
210
|
+
coalesced = false;
|
|
211
|
+
get observedAnything() {
|
|
212
|
+
return this.firstEventAt !== null;
|
|
213
|
+
}
|
|
214
|
+
/** Feed one REDACTED observed event. Pure accumulation, never throws. */
|
|
215
|
+
observe(event) {
|
|
216
|
+
this.eventCounts.set(
|
|
217
|
+
event.kind,
|
|
218
|
+
(this.eventCounts.get(event.kind) ?? 0) + 1
|
|
219
|
+
);
|
|
220
|
+
if (event.kind === "user_message") this.turns += 1;
|
|
221
|
+
if (this.firstEventAt === null) this.firstEventAt = event.at;
|
|
222
|
+
this.lastEventAt = event.at;
|
|
223
|
+
const hour = event.at.slice(0, 13);
|
|
224
|
+
if (this.hourly.has(hour) || this.hourly.size < MAX_HOURLY_BUCKETS) {
|
|
225
|
+
this.hourly.set(hour, (this.hourly.get(hour) ?? 0) + 1);
|
|
226
|
+
} else {
|
|
227
|
+
this.coalesced = true;
|
|
228
|
+
}
|
|
229
|
+
const payload = event.payload ?? {};
|
|
230
|
+
if (event.kind === "tool_call") {
|
|
231
|
+
const name = typeof payload.name === "string" && payload.name.trim() ? payload.name.trim().slice(0, 120) : "(unnamed)";
|
|
232
|
+
if (this.toolCounts.has(name) || this.toolCounts.size < MAX_DISTINCT_TOOLS) {
|
|
233
|
+
this.toolCounts.set(name, (this.toolCounts.get(name) ?? 0) + 1);
|
|
234
|
+
} else {
|
|
235
|
+
this.coalesced = true;
|
|
236
|
+
this.toolCounts.set(
|
|
237
|
+
"(other)",
|
|
238
|
+
(this.toolCounts.get("(other)") ?? 0) + 1
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
const input = payload.input;
|
|
242
|
+
if (input && typeof input === "object" && !Array.isArray(input)) {
|
|
243
|
+
for (const key of PATH_KEYS) {
|
|
244
|
+
const path = pathOf(input[key]);
|
|
245
|
+
if (path) this.touch(path);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (event.kind === "file_change") {
|
|
250
|
+
const changes = payload.changes;
|
|
251
|
+
if (Array.isArray(changes)) {
|
|
252
|
+
for (const change of changes) {
|
|
253
|
+
const path = pathOf(change?.path);
|
|
254
|
+
if (path) this.touch(path);
|
|
255
|
+
}
|
|
256
|
+
} else if (changes && typeof changes === "object") {
|
|
257
|
+
for (const key of Object.keys(changes)) {
|
|
258
|
+
const path = pathOf(key);
|
|
259
|
+
if (path) this.touch(path);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
touch(path) {
|
|
265
|
+
if (this.files.has(path)) return;
|
|
266
|
+
if (this.files.size >= MAX_TRACKED_FILES) {
|
|
267
|
+
this.filesOverflow += 1;
|
|
268
|
+
this.coalesced = true;
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
this.files.add(path);
|
|
272
|
+
}
|
|
273
|
+
/** The bounded v1 JSON. ≤ 32 KB serialized — file-list tail drops first. */
|
|
274
|
+
snapshot() {
|
|
275
|
+
const build = (paths2, listTruncated2) => ({
|
|
276
|
+
version: SKELETON_VERSION,
|
|
277
|
+
provider: this.provider,
|
|
278
|
+
turns: this.turns,
|
|
279
|
+
firstEventAt: this.firstEventAt,
|
|
280
|
+
lastEventAt: this.lastEventAt,
|
|
281
|
+
eventCounts: Object.fromEntries(this.eventCounts),
|
|
282
|
+
toolCounts: Object.fromEntries(this.toolCounts),
|
|
283
|
+
filesTouched: {
|
|
284
|
+
total: this.files.size + this.filesOverflow,
|
|
285
|
+
paths: paths2,
|
|
286
|
+
...listTruncated2 ? { listTruncated: true } : {}
|
|
287
|
+
},
|
|
288
|
+
hourlyBuckets: Object.fromEntries(this.hourly),
|
|
289
|
+
...this.coalesced || listTruncated2 ? { truncated: true } : {}
|
|
290
|
+
});
|
|
291
|
+
let paths = [...this.files];
|
|
292
|
+
let listTruncated = this.filesOverflow > 0;
|
|
293
|
+
let skeleton = build(paths, listTruncated);
|
|
294
|
+
while (Buffer.byteLength(JSON.stringify(skeleton), "utf8") > MAX_SKELETON_BYTES && paths.length > 0) {
|
|
295
|
+
paths = paths.slice(0, Math.max(0, Math.floor(paths.length / 2)));
|
|
296
|
+
listTruncated = true;
|
|
297
|
+
skeleton = build(paths, listTruncated);
|
|
298
|
+
}
|
|
299
|
+
return skeleton;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
183
303
|
// lib/session-usage.ts
|
|
184
304
|
function insideOneRange(ranges, from, to) {
|
|
185
305
|
return ranges.some((r) => r.from <= from && to <= r.to);
|
|
@@ -362,6 +482,7 @@ var HEARTBEAT_MIN_INTERVAL_MS = 3e4;
|
|
|
362
482
|
var SessionBridge = class {
|
|
363
483
|
constructor(deps) {
|
|
364
484
|
this.deps = deps;
|
|
485
|
+
this.skeleton = deps.collectSkeleton === false ? null : new SessionSkeleton(deps.provider);
|
|
365
486
|
}
|
|
366
487
|
deps;
|
|
367
488
|
sequence = 0;
|
|
@@ -393,6 +514,13 @@ var SessionBridge = class {
|
|
|
393
514
|
* session keeps its telemetry and loses what it actually concluded).
|
|
394
515
|
*/
|
|
395
516
|
lastAssistantMessage = null;
|
|
517
|
+
/**
|
|
518
|
+
* Session evidence floor (PRD §4): the v1 activity skeleton, accumulated
|
|
519
|
+
* from every REDACTED event this bridge records — capture-off included —
|
|
520
|
+
* and submitted on the existing heartbeats plus the forced beat at close.
|
|
521
|
+
* Null when the operator opted out at align (D3).
|
|
522
|
+
*/
|
|
523
|
+
skeleton;
|
|
396
524
|
/**
|
|
397
525
|
* True after a heartbeat came back 409 SESSION_NOT_ACTIVE — the session is
|
|
398
526
|
* terminal server-side. The watch host uses this as its end signal when no
|
|
@@ -463,6 +591,7 @@ var SessionBridge = class {
|
|
|
463
591
|
this.deps.redactor.text(serializeSessionEvent(full))
|
|
464
592
|
);
|
|
465
593
|
}
|
|
594
|
+
this.skeleton?.observe(full);
|
|
466
595
|
if (full.kind === "assistant_message") {
|
|
467
596
|
const text2 = full.payload?.text;
|
|
468
597
|
if (typeof text2 === "string" && text2.trim().length > 0) {
|
|
@@ -601,7 +730,12 @@ var SessionBridge = class {
|
|
|
601
730
|
// Sent only once a receipt has actually been observed — an empty
|
|
602
731
|
// rollup would overwrite the session's totals with nulls and
|
|
603
732
|
// report UNAVAILABLE for a session that had already reported.
|
|
604
|
-
...this.receipts.length > 0 ? { usage: this.usageRollup() } : {}
|
|
733
|
+
...this.receipts.length > 0 ? { usage: this.usageRollup() } : {},
|
|
734
|
+
// Evidence floor (PRD §4/D2): the bounded activity skeleton rides
|
|
735
|
+
// the same beat (tolerant server parse — an old server strips the
|
|
736
|
+
// unknown key). Sent only once something was observed: null column
|
|
737
|
+
// means "never observed", never an empty object.
|
|
738
|
+
...this.skeleton?.observedAnything ? { activitySkeleton: this.skeleton.snapshot() } : {}
|
|
605
739
|
})
|
|
606
740
|
}
|
|
607
741
|
);
|
|
@@ -801,6 +935,7 @@ var SessionBridge = class {
|
|
|
801
935
|
*/
|
|
802
936
|
async complete(opts) {
|
|
803
937
|
const { pending } = await this.flushParts();
|
|
938
|
+
await this.flushUsageNow().catch(() => false);
|
|
804
939
|
const finalResponseArtifactId = await this.pushFinalResponse();
|
|
805
940
|
const rollup = this.usageRollup();
|
|
806
941
|
const current = await this.deps.callTool("get_agent_session", {
|
|
@@ -1230,6 +1365,13 @@ function mapCodexRolloutLine(rawLine) {
|
|
|
1230
1365
|
return { event: null, modelId: null, turn: null };
|
|
1231
1366
|
}
|
|
1232
1367
|
}
|
|
1368
|
+
function lastCodexRolloutModelOf(rollout) {
|
|
1369
|
+
let modelId = null;
|
|
1370
|
+
for (const rawLine of rollout.split("\n")) {
|
|
1371
|
+
modelId = mapCodexRolloutLine(rawLine).modelId ?? modelId;
|
|
1372
|
+
}
|
|
1373
|
+
return modelId;
|
|
1374
|
+
}
|
|
1233
1375
|
|
|
1234
1376
|
// lib/session-codex-hooks.ts
|
|
1235
1377
|
function text(value) {
|
|
@@ -1664,6 +1806,7 @@ async function runClaudeSessionHost(plan, deps = {}) {
|
|
|
1664
1806
|
callTool: deps.callTool ?? sessionCallTool(plan.mcpUrl, bearerSource, plan.sessionId),
|
|
1665
1807
|
fetchImpl: deps.fetchImpl,
|
|
1666
1808
|
traceCapture,
|
|
1809
|
+
collectSkeleton: plan.collectSkeleton !== false,
|
|
1667
1810
|
log
|
|
1668
1811
|
});
|
|
1669
1812
|
bridge.recordCapabilities(
|
|
@@ -1724,6 +1867,9 @@ async function runClaudeSessionHost(plan, deps = {}) {
|
|
|
1724
1867
|
}
|
|
1725
1868
|
let transcriptPath = watch ? plan.transcriptPath ?? null : null;
|
|
1726
1869
|
let transcriptOffset = 0;
|
|
1870
|
+
let codexTurnId = null;
|
|
1871
|
+
let codexModelId = null;
|
|
1872
|
+
const codexRolloutTurnStarts = /* @__PURE__ */ new Map();
|
|
1727
1873
|
const timing = new ClaudeTimingTracker();
|
|
1728
1874
|
let transcriptSeen = false;
|
|
1729
1875
|
let transcriptWarned = false;
|
|
@@ -1734,17 +1880,26 @@ async function runClaudeSessionHost(plan, deps = {}) {
|
|
|
1734
1880
|
markHostTranscript(sessionDir, true, transcriptPath ?? void 0);
|
|
1735
1881
|
}
|
|
1736
1882
|
};
|
|
1883
|
+
const observePriorCodexModel = (path) => {
|
|
1884
|
+
if (plan.provider !== "codex" || plan.importHistory) return;
|
|
1885
|
+
try {
|
|
1886
|
+
const modelId = lastCodexRolloutModelOf(readFileSync3(path, "utf8"));
|
|
1887
|
+
if (modelId) {
|
|
1888
|
+
codexModelId = modelId;
|
|
1889
|
+
bridge.observeModel(modelId);
|
|
1890
|
+
}
|
|
1891
|
+
} catch {
|
|
1892
|
+
}
|
|
1893
|
+
};
|
|
1737
1894
|
if (watch && transcriptPath && !plan.importHistory) {
|
|
1738
1895
|
try {
|
|
1739
1896
|
transcriptOffset = statSync2(transcriptPath).size;
|
|
1897
|
+
observePriorCodexModel(transcriptPath);
|
|
1740
1898
|
noteTranscriptSeen();
|
|
1741
1899
|
} catch {
|
|
1742
1900
|
}
|
|
1743
1901
|
}
|
|
1744
1902
|
let bound = watch;
|
|
1745
|
-
let codexTurnId = null;
|
|
1746
|
-
let codexModelId = null;
|
|
1747
|
-
const codexRolloutTurnStarts = /* @__PURE__ */ new Map();
|
|
1748
1903
|
let sessionEnded = false;
|
|
1749
1904
|
let lastPeriodicFlushAt = 0;
|
|
1750
1905
|
let lastPromptAttemptAt = 0;
|
|
@@ -1841,6 +1996,7 @@ async function runClaudeSessionHost(plan, deps = {}) {
|
|
|
1841
1996
|
transcriptPath = hookTranscript;
|
|
1842
1997
|
try {
|
|
1843
1998
|
transcriptOffset = plan.importHistory ? 0 : statSync2(transcriptPath).size;
|
|
1999
|
+
observePriorCodexModel(transcriptPath);
|
|
1844
2000
|
noteTranscriptSeen();
|
|
1845
2001
|
} catch {
|
|
1846
2002
|
transcriptOffset = 0;
|
|
@@ -2020,9 +2176,6 @@ async function runClaudeSessionHost(plan, deps = {}) {
|
|
|
2020
2176
|
for (const id of codexRolloutTurnStarts.keys()) {
|
|
2021
2177
|
bridge.recordUnclosedInterval("turn", id);
|
|
2022
2178
|
}
|
|
2023
|
-
if (plan.provider === "codex") {
|
|
2024
|
-
await bridge.flushUsageNow().catch(() => false);
|
|
2025
|
-
}
|
|
2026
2179
|
const end = await endRepoState(plan.repoRoot);
|
|
2027
2180
|
const result = await bridge.complete({
|
|
2028
2181
|
outcome: exitCode === 0 ? "COMPLETED" : "INTERRUPTED",
|
|
@@ -2066,6 +2219,7 @@ async function runCodexSessionHost(plan, deps = {}) {
|
|
|
2066
2219
|
redactor: createSessionRedactor({ homedir: homedir() }),
|
|
2067
2220
|
callTool: sessionCallTool(plan.mcpUrl, codexBearerSource, plan.sessionId),
|
|
2068
2221
|
fetchImpl: deps.fetchImpl,
|
|
2222
|
+
collectSkeleton: plan.collectSkeleton !== false,
|
|
2069
2223
|
log
|
|
2070
2224
|
});
|
|
2071
2225
|
bridge.recordCapabilities({
|
|
@@ -2204,4 +2358,4 @@ export {
|
|
|
2204
2358
|
safeParse,
|
|
2205
2359
|
sessionCallTool
|
|
2206
2360
|
};
|
|
2207
|
-
//# sourceMappingURL=session-host-
|
|
2361
|
+
//# sourceMappingURL=session-host-L6XF4T2P.js.map
|