@tekmidian/pai 0.47.0 → 0.48.0
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/dist/cli/index.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import "../helpers-crDEr6S2.mjs";
|
|
|
7
7
|
import "../sync-Btcb6xA4.mjs";
|
|
8
8
|
import "../embeddings-DvRKcQyt.mjs";
|
|
9
9
|
import "../search-B69cQwDB.mjs";
|
|
10
|
-
import { n as drainStdio, t as buildProgram } from "../program-
|
|
10
|
+
import { n as drainStdio, t as buildProgram } from "../program-Bgv1lFFA.mjs";
|
|
11
11
|
import "../context-handover-cache-B9NKSjwR.mjs";
|
|
12
12
|
import "../config-DCu8THKi.mjs";
|
|
13
13
|
import "../config-DxMZjpvY.mjs";
|
package/dist/cli/program.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import "../helpers-crDEr6S2.mjs";
|
|
|
6
6
|
import "../sync-Btcb6xA4.mjs";
|
|
7
7
|
import "../embeddings-DvRKcQyt.mjs";
|
|
8
8
|
import "../search-B69cQwDB.mjs";
|
|
9
|
-
import { t as buildProgram } from "../program-
|
|
9
|
+
import { t as buildProgram } from "../program-Bgv1lFFA.mjs";
|
|
10
10
|
import "../context-handover-cache-B9NKSjwR.mjs";
|
|
11
11
|
import "../config-DCu8THKi.mjs";
|
|
12
12
|
import "../config-DxMZjpvY.mjs";
|
|
@@ -10076,6 +10076,24 @@ function parseCompactionEvent(line, turnIndex) {
|
|
|
10076
10076
|
};
|
|
10077
10077
|
}
|
|
10078
10078
|
/**
|
|
10079
|
+
* `type:"system"` `subtype:"model_refusal_fallback"` lines mark an
|
|
10080
|
+
* automatic model switch fired by an API safety refusal (e.g. "cyber"
|
|
10081
|
+
* category), not a user or config choice — worth flagging separately since
|
|
10082
|
+
* it can rebuild the whole prompt cache mid-session.
|
|
10083
|
+
*/
|
|
10084
|
+
function isModelFallback(line) {
|
|
10085
|
+
return line.type === "system" && line.subtype === "model_refusal_fallback";
|
|
10086
|
+
}
|
|
10087
|
+
function parseFallbackEvent(line, turnIndex) {
|
|
10088
|
+
return {
|
|
10089
|
+
turnIndex,
|
|
10090
|
+
from: line.originalModel ?? "unknown",
|
|
10091
|
+
to: line.fallbackModel ?? "unknown",
|
|
10092
|
+
category: line.apiRefusalCategory ?? "unknown",
|
|
10093
|
+
scope: line.scope ?? "unknown"
|
|
10094
|
+
};
|
|
10095
|
+
}
|
|
10096
|
+
/**
|
|
10079
10097
|
* Fold one already-parsed JSONL line into a report being accumulated.
|
|
10080
10098
|
* Exported separately so both the streaming file reader below and tests
|
|
10081
10099
|
* (which build fixtures as arrays of objects, not files) share one path.
|
|
@@ -10098,13 +10116,24 @@ function foldAssistantLine(report, seenIds, line, threshold) {
|
|
|
10098
10116
|
if (typeof v === "number") report.totals[key] += v;
|
|
10099
10117
|
}
|
|
10100
10118
|
const context = (Number(usage.cache_read_input_tokens) || 0) + (Number(usage.cache_creation_input_tokens) || 0) + (Number(usage.input_tokens) || 0);
|
|
10101
|
-
if (report.firstTurnContext === null)
|
|
10119
|
+
if (report.firstTurnContext === null) {
|
|
10120
|
+
report.firstTurnContext = context;
|
|
10121
|
+
report.firstTurnAt = line.timestamp ?? null;
|
|
10122
|
+
}
|
|
10102
10123
|
report.lastTurnContext = context;
|
|
10103
10124
|
report.maxContext = report.maxContext === null ? context : Math.max(report.maxContext, context);
|
|
10104
10125
|
if (context > threshold) report.turnsAboveThreshold++;
|
|
10105
10126
|
if ((Number(usage.cache_creation_input_tokens) || 0) > 2e4) report.cacheRebuildTurns++;
|
|
10106
10127
|
const model = message?.model ?? "unknown";
|
|
10107
10128
|
report.models[model] = (report.models[model] ?? 0) + 1;
|
|
10129
|
+
if (report.lastModel !== null && report.lastModel !== model) report.modelSwitches.push({
|
|
10130
|
+
turnIndex: report.turns,
|
|
10131
|
+
from: report.lastModel,
|
|
10132
|
+
to: model,
|
|
10133
|
+
cacheRead: Number(usage.cache_read_input_tokens) || 0,
|
|
10134
|
+
cacheCreation: Number(usage.cache_creation_input_tokens) || 0
|
|
10135
|
+
});
|
|
10136
|
+
report.lastModel = model;
|
|
10108
10137
|
const split = usage.cache_creation;
|
|
10109
10138
|
if (split) {
|
|
10110
10139
|
report.cacheCreationSplit.ephemeral5m += Number(split.ephemeral_5m_input_tokens) || 0;
|
|
@@ -10120,6 +10149,8 @@ function foldAssistantLine(report, seenIds, line, threshold) {
|
|
|
10120
10149
|
*/
|
|
10121
10150
|
function isRealUserPrompt(line) {
|
|
10122
10151
|
if (line.type !== "user") return false;
|
|
10152
|
+
if (line.isMeta) return false;
|
|
10153
|
+
if (line.origin?.kind && line.origin.kind !== "human") return false;
|
|
10123
10154
|
const content = line.message?.content;
|
|
10124
10155
|
if (typeof content === "string") return true;
|
|
10125
10156
|
if (Array.isArray(content)) return !content.some((block) => block?.type === "tool_result");
|
|
@@ -10145,7 +10176,11 @@ async function parseSessionUsage(path, threshold = 2e5) {
|
|
|
10145
10176
|
cacheRebuildTurns: 0,
|
|
10146
10177
|
userPrompts: 0,
|
|
10147
10178
|
promptExposure: 0,
|
|
10148
|
-
compactions: []
|
|
10179
|
+
compactions: [],
|
|
10180
|
+
firstTurnAt: null,
|
|
10181
|
+
lastModel: null,
|
|
10182
|
+
modelSwitches: [],
|
|
10183
|
+
fallbacks: []
|
|
10149
10184
|
};
|
|
10150
10185
|
const seenIds = /* @__PURE__ */ new Set();
|
|
10151
10186
|
let contextSum = 0;
|
|
@@ -10170,6 +10205,10 @@ async function parseSessionUsage(path, threshold = 2e5) {
|
|
|
10170
10205
|
if (event) report.compactions.push(event);
|
|
10171
10206
|
continue;
|
|
10172
10207
|
}
|
|
10208
|
+
if (isModelFallback(obj)) {
|
|
10209
|
+
report.fallbacks.push(parseFallbackEvent(obj, report.turns));
|
|
10210
|
+
continue;
|
|
10211
|
+
}
|
|
10173
10212
|
const context = foldAssistantLine(report, seenIds, obj, threshold);
|
|
10174
10213
|
if (context !== null) {
|
|
10175
10214
|
contextSum += context;
|
|
@@ -10239,6 +10278,44 @@ async function auditSession(path, threshold) {
|
|
|
10239
10278
|
percentages
|
|
10240
10279
|
};
|
|
10241
10280
|
}
|
|
10281
|
+
/**
|
|
10282
|
+
* First-turn context of every session transcript in this project's
|
|
10283
|
+
* `~/.claude/projects/<encoded-cwd>` directory, newest first — the readings
|
|
10284
|
+
* needed to see when a step change (model switch, hook growth) happened
|
|
10285
|
+
* across sessions without an ad-hoc script.
|
|
10286
|
+
*/
|
|
10287
|
+
async function sessionHistory(cwd, limit = 20) {
|
|
10288
|
+
const dir = join(homedir(), ".claude", "projects", encodeDir(cwd));
|
|
10289
|
+
let entries;
|
|
10290
|
+
try {
|
|
10291
|
+
entries = readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
|
|
10292
|
+
} catch {
|
|
10293
|
+
return [];
|
|
10294
|
+
}
|
|
10295
|
+
const rows = [];
|
|
10296
|
+
for (const entry of entries) {
|
|
10297
|
+
const path = join(dir, entry);
|
|
10298
|
+
const report = await parseSessionUsage(path);
|
|
10299
|
+
if (report.turns === 0) continue;
|
|
10300
|
+
rows.push({
|
|
10301
|
+
path,
|
|
10302
|
+
sessionId: entry.replace(/\.jsonl$/, ""),
|
|
10303
|
+
firstTurnAt: report.firstTurnAt,
|
|
10304
|
+
firstTurnContext: report.firstTurnContext,
|
|
10305
|
+
turns: report.turns,
|
|
10306
|
+
models: report.models,
|
|
10307
|
+
switches: report.modelSwitches.length,
|
|
10308
|
+
fallbacks: report.fallbacks.length
|
|
10309
|
+
});
|
|
10310
|
+
}
|
|
10311
|
+
rows.sort((a, b) => {
|
|
10312
|
+
if (a.firstTurnAt === null && b.firstTurnAt === null) return 0;
|
|
10313
|
+
if (a.firstTurnAt === null) return 1;
|
|
10314
|
+
if (b.firstTurnAt === null) return -1;
|
|
10315
|
+
return b.firstTurnAt.localeCompare(a.firstTurnAt);
|
|
10316
|
+
});
|
|
10317
|
+
return rows.slice(0, limit);
|
|
10318
|
+
}
|
|
10242
10319
|
|
|
10243
10320
|
//#endregion
|
|
10244
10321
|
//#region src/audit/spawn.ts
|
|
@@ -11026,6 +11103,7 @@ const FIRST_TURN_CONTEXT_LIMIT = 3e4;
|
|
|
11026
11103
|
const DAEMON_FAILURE_RATE_LIMIT = .05;
|
|
11027
11104
|
const CONTEXT_GROWTH_AVG_RED = 1e5;
|
|
11028
11105
|
const CONTEXT_GROWTH_MAX_AMBER = 15e4;
|
|
11106
|
+
const MODEL_SWITCH_REBUILD_LIMIT = 2e4;
|
|
11029
11107
|
const SEVERITY_RANK = {
|
|
11030
11108
|
RED: 0,
|
|
11031
11109
|
AMBER: 1,
|
|
@@ -11152,6 +11230,22 @@ function buildFindings(input) {
|
|
|
11152
11230
|
severity: overshoots.length > 0 ? "RED" : "GREEN",
|
|
11153
11231
|
evidence: overshoots.length > 0 ? `override not honoured: ${overshoots.map((c) => `preTokens=${c.preTokens}`).join(", ")} vs configured ${input.ctxThreshold}` : compactions.length === 0 ? `0 compactions in ${input.session.turns} turns` : `${compactions.length} compaction(s), all within 25% of configured ${input.ctxThreshold}`
|
|
11154
11232
|
});
|
|
11233
|
+
const switches = input.session.modelSwitches;
|
|
11234
|
+
const fallbacks = input.session.fallbacks;
|
|
11235
|
+
const rebuilds = switches.filter((s) => s.cacheCreation > MODEL_SWITCH_REBUILD_LIMIT);
|
|
11236
|
+
const short = (model) => model.replace(/^claude-/, "");
|
|
11237
|
+
let switchEvidence;
|
|
11238
|
+
if (switches.length === 0) switchEvidence = `0 switches in ${input.session.turns} turns, models ${JSON.stringify(input.session.models)}`;
|
|
11239
|
+
else {
|
|
11240
|
+
const list = switches.map((s) => `${short(s.from)}->${short(s.to)} at turn ${s.turnIndex} (cache_read ${s.cacheRead}, cache_creation ${s.cacheCreation})`).join("; ");
|
|
11241
|
+
switchEvidence = `${switches.length} switch(es): ${list}`;
|
|
11242
|
+
if (fallbacks.length > 0) switchEvidence += `; ${fallbacks.length} safeguard fallback(s): ${fallbacks.map((f) => f.category).join(",")}`;
|
|
11243
|
+
}
|
|
11244
|
+
findings.push({
|
|
11245
|
+
finding: "mid-session model switches",
|
|
11246
|
+
severity: switches.length === 0 ? "GREEN" : rebuilds.length > 0 ? "RED" : "AMBER",
|
|
11247
|
+
evidence: switchEvidence
|
|
11248
|
+
});
|
|
11155
11249
|
}
|
|
11156
11250
|
return sortFindings(findings);
|
|
11157
11251
|
}
|
|
@@ -11701,8 +11795,40 @@ function printSession(report) {
|
|
|
11701
11795
|
console.log(`avg context: ${report.avgContext ?? "n/a"}, max context: ${report.maxContext ?? "n/a"}, turns above threshold: ${report.turnsAboveThreshold}, cache-rebuild turns: ${report.cacheRebuildTurns}, user prompts: ${report.userPrompts}, prompt exposure: ${report.promptExposure}`);
|
|
11702
11796
|
console.log(`compactions: ${report.compactions.length}`);
|
|
11703
11797
|
for (const c of report.compactions) console.log(dim$1(` ${c.trigger} at turn ${c.turnIndex}: preTokens=${c.preTokens}`));
|
|
11798
|
+
console.log(`model switches: ${report.modelSwitches.length}`);
|
|
11799
|
+
for (const s of report.modelSwitches) console.log(dim$1(` ${s.from} -> ${s.to} at turn ${s.turnIndex}: cache_read=${s.cacheRead}, cache_creation=${s.cacheCreation}`));
|
|
11800
|
+
if (report.fallbacks.length > 0) {
|
|
11801
|
+
console.log(`safeguard fallbacks: ${report.fallbacks.length}`);
|
|
11802
|
+
for (const f of report.fallbacks) console.log(dim$1(` ${f.from} -> ${f.to} before turn ${f.turnIndex}: ${f.category} (${f.scope})`));
|
|
11803
|
+
}
|
|
11804
|
+
}
|
|
11805
|
+
function printSessionHistory(rows) {
|
|
11806
|
+
console.log(header("Session history (newest first)"));
|
|
11807
|
+
const tableRows = rows.map((r) => [
|
|
11808
|
+
r.firstTurnAt ?? "n/a",
|
|
11809
|
+
r.sessionId,
|
|
11810
|
+
r.firstTurnContext === null ? "n/a" : String(r.firstTurnContext),
|
|
11811
|
+
String(r.turns),
|
|
11812
|
+
String(r.switches),
|
|
11813
|
+
Object.entries(r.models).map(([name, count]) => `${name}:${count}`).join(",")
|
|
11814
|
+
]);
|
|
11815
|
+
console.log(renderTable([
|
|
11816
|
+
"first turn (UTC)",
|
|
11817
|
+
"session",
|
|
11818
|
+
"first-turn ctx",
|
|
11819
|
+
"turns",
|
|
11820
|
+
"switches",
|
|
11821
|
+
"models"
|
|
11822
|
+
], tableRows));
|
|
11704
11823
|
}
|
|
11705
11824
|
async function cmdSession(path, opts) {
|
|
11825
|
+
if (opts.history !== void 0) {
|
|
11826
|
+
const limit = typeof opts.history === "string" ? parseInt(opts.history, 10) : 20;
|
|
11827
|
+
const rows = await sessionHistory(process.cwd(), limit);
|
|
11828
|
+
if (opts.json) return printJson(rows);
|
|
11829
|
+
printSessionHistory(rows);
|
|
11830
|
+
return;
|
|
11831
|
+
}
|
|
11706
11832
|
const report = await auditSession(path, opts.ctxThreshold ? parseInt(opts.ctxThreshold, 10) : void 0);
|
|
11707
11833
|
if (!report) {
|
|
11708
11834
|
console.error(err(" No session transcript found."));
|
|
@@ -12186,7 +12312,7 @@ function registerAuditCommands(auditCmd) {
|
|
|
12186
12312
|
await cmdFiles(paths, jsonOpt());
|
|
12187
12313
|
});
|
|
12188
12314
|
tokensCmd.command("hooks").description("Token cost of every SessionStart / UserPromptSubmit hook").action(() => cmdHooks(jsonOpt()));
|
|
12189
|
-
tokensCmd.command("session").description("Cache/input/output token split for a session transcript (default: newest)").argument("[path]", "Session JSONL path (default: newest under ~/.claude/projects)").option("--ctx-threshold <n>", "Per-turn context size above which a turn counts as 'above threshold' (default 200000)").action(async (path, opts) => {
|
|
12315
|
+
tokensCmd.command("session").description("Cache/input/output token split for a session transcript (default: newest)").argument("[path]", "Session JSONL path (default: newest under ~/.claude/projects)").option("--ctx-threshold <n>", "Per-turn context size above which a turn counts as 'above threshold' (default 200000)").option("--history [n]", "List first-turn context of the newest n sessions in this project's transcript directory (default 20)").action(async (path, opts) => {
|
|
12190
12316
|
await cmdSession(path, {
|
|
12191
12317
|
...jsonOpt(),
|
|
12192
12318
|
...opts
|
|
@@ -20064,4 +20190,4 @@ claude() {
|
|
|
20064
20190
|
|
|
20065
20191
|
//#endregion
|
|
20066
20192
|
export { drainStdio as n, buildProgram as t };
|
|
20067
|
-
//# sourceMappingURL=program-
|
|
20193
|
+
//# sourceMappingURL=program-Bgv1lFFA.mjs.map
|