@compaction/cli 0.1.4 → 0.3.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/README.md +51 -12
- package/dist/cli/commands/activity.d.ts +8 -0
- package/dist/cli/commands/activity.js +35 -0
- package/dist/cli/commands/apply-context.js +68 -0
- package/dist/cli/commands/billing-delta.js +12 -1
- package/dist/cli/commands/capture-claude-code.d.ts +44 -0
- package/dist/cli/commands/capture-claude-code.js +206 -0
- package/dist/cli/commands/capture.js +283 -1
- package/dist/cli/commands/compact.js +24 -11
- package/dist/cli/commands/context.d.ts +2 -0
- package/dist/cli/commands/context.js +130 -0
- package/dist/cli/commands/dev.d.ts +17 -0
- package/dist/cli/commands/dev.js +131 -0
- package/dist/cli/commands/gateway.d.ts +31 -0
- package/dist/cli/commands/gateway.js +312 -0
- package/dist/cli/commands/hooks.d.ts +5 -0
- package/dist/cli/commands/hooks.js +181 -0
- package/dist/cli/commands/import.js +3 -1
- package/dist/cli/commands/init.js +422 -17
- package/dist/cli/commands/input-compaction-ab.d.ts +2 -0
- package/dist/cli/commands/input-compaction-ab.js +125 -0
- package/dist/cli/commands/optimize-hosted.d.ts +54 -0
- package/dist/cli/commands/optimize-hosted.js +123 -0
- package/dist/cli/commands/optimize.js +8 -0
- package/dist/cli/commands/output-shaping-ab.d.ts +2 -0
- package/dist/cli/commands/output-shaping-ab.js +132 -0
- package/dist/cli/commands/output-shaping.d.ts +7 -0
- package/dist/cli/commands/output-shaping.js +43 -0
- package/dist/cli/commands/policies.d.ts +2 -0
- package/dist/cli/commands/policies.js +80 -0
- package/dist/cli/commands/precall.d.ts +19 -0
- package/dist/cli/commands/precall.js +436 -0
- package/dist/cli/commands/recommend.js +1 -1
- package/dist/cli/commands/run.js +310 -18
- package/dist/cli/commands/upgrade-status.d.ts +42 -0
- package/dist/cli/commands/upgrade-status.js +152 -0
- package/dist/cli/cursor-export-read.d.ts +13 -0
- package/dist/cli/cursor-export-read.js +53 -0
- package/dist/cli/cursor-live-preflight.d.ts +19 -0
- package/dist/cli/cursor-live-preflight.js +46 -0
- package/dist/cli/index.js +39 -2
- package/dist/cli/onboarding/GatewayTui.d.ts +33 -0
- package/dist/cli/onboarding/GatewayTui.js +113 -0
- package/dist/cli/onboarding/model.d.ts +32 -1
- package/dist/cli/onboarding/model.js +27 -2
- package/dist/cli/terminal-logo.d.ts +27 -0
- package/dist/cli/terminal-logo.js +49 -0
- package/dist/core/activity-event.d.ts +107 -0
- package/dist/core/activity-event.js +182 -0
- package/dist/core/activity-store.d.ts +63 -0
- package/dist/core/activity-store.js +254 -0
- package/dist/core/activity-view.d.ts +76 -0
- package/dist/core/activity-view.js +120 -0
- package/dist/core/api-client/index.d.ts +3 -1
- package/dist/core/api-client/index.js +4 -0
- package/dist/core/api-client/payload.d.ts +5 -1
- package/dist/core/api-client/payload.js +8 -6
- package/dist/core/api-client/persisted-config.d.ts +57 -0
- package/dist/core/api-client/persisted-config.js +156 -0
- package/dist/core/api-client/tool.d.ts +29 -0
- package/dist/core/api-client/tool.js +50 -0
- package/dist/core/api-client/types.d.ts +15 -4
- package/dist/core/auto-apply-ask.d.ts +58 -0
- package/dist/core/auto-apply-ask.js +105 -0
- package/dist/core/auto-apply-gates.d.ts +76 -0
- package/dist/core/auto-apply-gates.js +113 -0
- package/dist/core/before-call-activity.d.ts +35 -0
- package/dist/core/before-call-activity.js +103 -0
- package/dist/core/before-call-recovery.d.ts +19 -0
- package/dist/core/before-call-recovery.js +46 -0
- package/dist/core/before-call-stdin.d.ts +59 -0
- package/dist/core/before-call-stdin.js +78 -0
- package/dist/core/before-call.d.ts +149 -0
- package/dist/core/before-call.js +358 -0
- package/dist/core/billing-delta/billing-delta-record.d.ts +28 -0
- package/dist/core/billing-delta/billing-delta-record.js +26 -0
- package/dist/core/capture-record.d.ts +40 -0
- package/dist/core/capture-record.js +55 -0
- package/dist/core/claude-code-before-call.d.ts +37 -0
- package/dist/core/claude-code-before-call.js +120 -0
- package/dist/core/claude-code-connect.d.ts +31 -0
- package/dist/core/claude-code-connect.js +87 -0
- package/dist/core/claude-code-hook-record.d.ts +71 -0
- package/dist/core/claude-code-hook-record.js +0 -0
- package/dist/core/claude-code-hooks.d.ts +77 -0
- package/dist/core/claude-code-hooks.js +141 -0
- package/dist/core/codex-capture.d.ts +45 -0
- package/dist/core/codex-capture.js +204 -0
- package/dist/core/command-runner.js +5 -1
- package/dist/core/compaction-artifacts.d.ts +51 -0
- package/dist/core/compaction-artifacts.js +242 -0
- package/dist/core/compactor.d.ts +9 -0
- package/dist/core/compactor.js +95 -0
- package/dist/core/context-store-eval-cases.d.ts +6 -0
- package/dist/core/context-store-eval-cases.js +331 -0
- package/dist/core/context-store-eval.d.ts +140 -0
- package/dist/core/context-store-eval.js +138 -0
- package/dist/core/context-store-fs.d.ts +73 -0
- package/dist/core/context-store-fs.js +157 -0
- package/dist/core/context-store-sufficiency.d.ts +98 -0
- package/dist/core/context-store-sufficiency.js +135 -0
- package/dist/core/context-store.d.ts +155 -0
- package/dist/core/context-store.js +228 -0
- package/dist/core/cross-surface-event.d.ts +306 -0
- package/dist/core/cross-surface-event.js +330 -0
- package/dist/core/cursor-capture.d.ts +54 -0
- package/dist/core/cursor-capture.js +215 -0
- package/dist/core/cursor-preflight-probe.d.ts +8 -0
- package/dist/core/cursor-preflight-probe.js +88 -0
- package/dist/core/cursor-preflight.d.ts +86 -0
- package/dist/core/cursor-preflight.js +126 -0
- package/dist/core/gateway/apply-activation.d.ts +39 -0
- package/dist/core/gateway/apply-activation.js +84 -0
- package/dist/core/gateway/apply-policy.d.ts +64 -0
- package/dist/core/gateway/apply-policy.js +221 -0
- package/dist/core/gateway/apply-receipt.d.ts +36 -0
- package/dist/core/gateway/apply-receipt.js +75 -0
- package/dist/core/gateway/cache-proof.d.ts +45 -0
- package/dist/core/gateway/cache-proof.js +65 -0
- package/dist/core/gateway/configure.d.ts +50 -0
- package/dist/core/gateway/configure.js +169 -0
- package/dist/core/gateway/openai-usage.d.ts +56 -0
- package/dist/core/gateway/openai-usage.js +128 -0
- package/dist/core/gateway/receipt.d.ts +138 -0
- package/dist/core/gateway/receipt.js +120 -0
- package/dist/core/gateway/recovery.d.ts +23 -0
- package/dist/core/gateway/recovery.js +68 -0
- package/dist/core/gateway/server.d.ts +51 -0
- package/dist/core/gateway/server.js +276 -0
- package/dist/core/gateway/status.d.ts +45 -0
- package/dist/core/gateway/status.js +109 -0
- package/dist/core/hook-usage-aggregate.d.ts +47 -0
- package/dist/core/hook-usage-aggregate.js +161 -0
- package/dist/core/input-compaction-ab.d.ts +111 -0
- package/dist/core/input-compaction-ab.js +158 -0
- package/dist/core/local-run-record.d.ts +109 -0
- package/dist/core/local-run-record.js +223 -0
- package/dist/core/output-shaping-ab.d.ts +140 -0
- package/dist/core/output-shaping-ab.js +146 -0
- package/dist/core/output-shaping-attach.d.ts +31 -0
- package/dist/core/output-shaping-attach.js +57 -0
- package/dist/core/output-shaping.d.ts +56 -0
- package/dist/core/output-shaping.js +89 -0
- package/dist/core/policy-middleware.d.ts +121 -0
- package/dist/core/policy-middleware.js +919 -0
- package/dist/core/policy-preferences.d.ts +99 -0
- package/dist/core/policy-preferences.js +232 -0
- package/dist/core/report-generator.d.ts +19 -1
- package/dist/core/report-generator.js +51 -0
- package/dist/core/run-aggregator.d.ts +56 -1
- package/dist/core/run-aggregator.js +93 -2
- package/dist/core/run-flow-report.d.ts +82 -0
- package/dist/core/run-flow-report.js +71 -0
- package/dist/core/safety-report.js +8 -1
- package/dist/core/shim-capture-bridge.d.ts +32 -0
- package/dist/core/shim-capture-bridge.js +88 -0
- package/dist/core/skill-injection-policy.d.ts +72 -0
- package/dist/core/skill-injection-policy.js +183 -0
- package/dist/core/spend-attribution.js +1 -1
- package/dist/core/token-accounting.d.ts +1 -1
- package/dist/core/tool-shim.d.ts +129 -0
- package/dist/core/tool-shim.js +447 -0
- package/dist/core/trace-adapters.d.ts +8 -0
- package/dist/core/trace-adapters.js +4 -0
- package/dist/core/trace-parser.d.ts +13 -13
- package/dist/core/trace-parser.js +6 -6
- package/dist/core/types.d.ts +45 -2
- package/dist/core/waste-detector.d.ts +20 -0
- package/dist/core/waste-detector.js +160 -6
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readdir, readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { DEFAULT_RUN_RECORDS_DIRECTORY, formatPerToolRunRecordLines, PER_TOOL_RECORDS_HEADER, readLocalRunTokenRecords, summarizePerToolRunRecords } from "./local-run-record.js";
|
|
3
4
|
import { classifyDistinctness } from "./trace-fingerprint.js";
|
|
4
5
|
const REQUIRED_STRING_FIELDS = ["run_id", "trace_title", "policy_name"];
|
|
5
6
|
const REQUIRED_NUMBER_FIELDS = [
|
|
@@ -26,6 +27,58 @@ function roundTo(value, digits) {
|
|
|
26
27
|
const factor = 10 ** digits;
|
|
27
28
|
return Math.round((value + Number.EPSILON) * factor) / factor;
|
|
28
29
|
}
|
|
30
|
+
/** Minimum DISTINCT runs for a measured (not single-anecdote) estimate delta. */
|
|
31
|
+
export const MIN_RUNS_FOR_MEASURED_DELTA = 3;
|
|
32
|
+
/**
|
|
33
|
+
* Compute the measured-across-distinct-runs estimate delta over a set of per-run percent
|
|
34
|
+
* reductions (PURE). Applies the accepted criterion: N ≥ 3 AND the mean ±2·SE interval excludes
|
|
35
|
+
* zero. Estimate-class only — the result is NEVER billing-confirmed or eval-backed. Uses the
|
|
36
|
+
* sample standard deviation (n−1); with n<2 there is no dispersion (SE 0) so it cannot qualify.
|
|
37
|
+
*/
|
|
38
|
+
export function computeMeasuredEstimateDelta(percentReductions) {
|
|
39
|
+
const n = percentReductions.length;
|
|
40
|
+
const mean = n === 0 ? 0 : percentReductions.reduce((sum, x) => sum + x, 0) / n;
|
|
41
|
+
let se = 0;
|
|
42
|
+
if (n >= 2) {
|
|
43
|
+
const variance = percentReductions.reduce((sum, x) => sum + (x - mean) ** 2, 0) / (n - 1);
|
|
44
|
+
se = Math.sqrt(variance) / Math.sqrt(n);
|
|
45
|
+
}
|
|
46
|
+
const ciLow = mean - 2 * se;
|
|
47
|
+
const ciHigh = mean + 2 * se;
|
|
48
|
+
const excludesZero = ciLow > 0;
|
|
49
|
+
const qualifies = n >= MIN_RUNS_FOR_MEASURED_DELTA && excludesZero;
|
|
50
|
+
const meanR = roundTo(mean, 2);
|
|
51
|
+
const lowR = roundTo(ciLow, 2);
|
|
52
|
+
const highR = roundTo(ciHigh, 2);
|
|
53
|
+
let label;
|
|
54
|
+
if (qualifies) {
|
|
55
|
+
label =
|
|
56
|
+
`Measured-caveated ESTIMATE delta across ${n} distinct runs: mean ${meanR}% reduction ` +
|
|
57
|
+
`(±2·SE [${lowR}%, ${highR}%], excludes zero). Estimate (chars/4 + price-table) — ` +
|
|
58
|
+
"NOT billing-confirmed, NOT eval-backed, NOT realized savings, NOT extrapolated.";
|
|
59
|
+
}
|
|
60
|
+
else if (n < MIN_RUNS_FOR_MEASURED_DELTA) {
|
|
61
|
+
label =
|
|
62
|
+
`Single-run/anecdote estimate (${n} distinct run(s); need ≥${MIN_RUNS_FOR_MEASURED_DELTA} ` +
|
|
63
|
+
"for a measured delta). Local estimate — NOT billing-confirmed.";
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
label =
|
|
67
|
+
`Estimate delta across ${n} distinct runs does NOT meet the measured bar ` +
|
|
68
|
+
`(±2·SE [${lowR}%, ${highR}%] includes zero — dispersion too wide). Local estimate — NOT billing-confirmed.`;
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
distinct_run_count: n,
|
|
72
|
+
mean_percent_reduction: meanR,
|
|
73
|
+
std_error: roundTo(se, 4),
|
|
74
|
+
ci_low: lowR,
|
|
75
|
+
ci_high: highR,
|
|
76
|
+
excludes_zero: excludesZero,
|
|
77
|
+
qualifies,
|
|
78
|
+
evidence_rung: qualifies ? "measured_caveated_estimate_delta" : "local_estimate_single_run",
|
|
79
|
+
label
|
|
80
|
+
};
|
|
81
|
+
}
|
|
29
82
|
/** Read the OPTIONAL content fingerprint digest from a report (older reports have none). */
|
|
30
83
|
function fingerprintDigestOf(report) {
|
|
31
84
|
const fp = report.trace_fingerprint;
|
|
@@ -153,11 +206,15 @@ export function aggregateCompactionReports(reports, skippedFiles = [], generated
|
|
|
153
206
|
const summary = {
|
|
154
207
|
...emptyBucket(),
|
|
155
208
|
average_percent_reduction: 0,
|
|
209
|
+
measured_estimate_delta: computeMeasuredEstimateDelta([]),
|
|
156
210
|
savings_by_policy: {},
|
|
157
211
|
savings_by_waste_pattern: {},
|
|
158
212
|
top_savings_runs: [],
|
|
159
213
|
skipped_files: skippedFiles,
|
|
160
214
|
distinctness,
|
|
215
|
+
per_tool_token_records: [],
|
|
216
|
+
run_records_source_glob: `${DEFAULT_RUN_RECORDS_DIRECTORY}/*.json`,
|
|
217
|
+
run_record_skipped_files: [],
|
|
161
218
|
generated_at: generatedAt,
|
|
162
219
|
source_glob: sourceGlob
|
|
163
220
|
};
|
|
@@ -183,6 +240,9 @@ export function aggregateCompactionReports(reports, skippedFiles = [], generated
|
|
|
183
240
|
}
|
|
184
241
|
summary.average_percent_reduction =
|
|
185
242
|
distinctReports.length === 0 ? 0 : roundTo(percentReductionTotal / distinctReports.length, 2);
|
|
243
|
+
// V0.2 Increment 2: the measured-across-distinct-runs estimate delta over the DISTINCT runs'
|
|
244
|
+
// per-run reductions. Qualifies for rung 1.5 only when N≥3 AND the mean ±2·SE excludes zero.
|
|
245
|
+
summary.measured_estimate_delta = computeMeasuredEstimateDelta(summary.top_savings_runs.map((run) => run.percent_reduction));
|
|
186
246
|
normalizeBucket(summary);
|
|
187
247
|
for (const bucket of Object.values(summary.savings_by_policy)) {
|
|
188
248
|
normalizeBucket(bucket);
|
|
@@ -200,9 +260,16 @@ export function aggregateCompactionReports(reports, skippedFiles = [], generated
|
|
|
200
260
|
.slice(0, 5);
|
|
201
261
|
return summary;
|
|
202
262
|
}
|
|
203
|
-
export async function createRunSummary(runsDirectory = ".compaction/runs") {
|
|
263
|
+
export async function createRunSummary(runsDirectory = ".compaction/runs", recordsDirectory = DEFAULT_RUN_RECORDS_DIRECTORY) {
|
|
204
264
|
const { reports, skippedFiles } = await readCompactionRunReports(runsDirectory);
|
|
205
|
-
|
|
265
|
+
const summary = aggregateCompactionReports(reports, skippedFiles, new Date().toISOString(), `${runsDirectory}/*/report.json`);
|
|
266
|
+
// ADDITIVE per-tool token_source rollup from the locally accumulated run records (LOCAL layer of the
|
|
267
|
+
// unified run flow). Counts + honest sources only — never content, never an output-savings figure.
|
|
268
|
+
const { records, skipped } = await readLocalRunTokenRecords(recordsDirectory);
|
|
269
|
+
summary.per_tool_token_records = summarizePerToolRunRecords(records.map((loaded) => loaded.record));
|
|
270
|
+
summary.run_records_source_glob = `${recordsDirectory}/*.json`;
|
|
271
|
+
summary.run_record_skipped_files = skipped;
|
|
272
|
+
return summary;
|
|
206
273
|
}
|
|
207
274
|
function formatCurrency(value) {
|
|
208
275
|
return `$${value.toFixed(6)}`;
|
|
@@ -250,6 +317,7 @@ export function formatRunSummary(summary) {
|
|
|
250
317
|
`- ${distinctnessLine(summary.distinctness)}`,
|
|
251
318
|
...formatBucketLines(summary).map((line) => `- ${line}`),
|
|
252
319
|
`- average percent reduction: ${summary.average_percent_reduction.toFixed(2)}%`,
|
|
320
|
+
`- measured estimate delta [${summary.measured_estimate_delta.evidence_rung}]: ${summary.measured_estimate_delta.label}`,
|
|
253
321
|
"",
|
|
254
322
|
"Savings by policy",
|
|
255
323
|
...(policyLines.length === 0 ? ["- none"] : policyLines),
|
|
@@ -260,6 +328,14 @@ export function formatRunSummary(summary) {
|
|
|
260
328
|
"Top savings runs",
|
|
261
329
|
...(topRunLines.length === 0 ? ["- none"] : topRunLines),
|
|
262
330
|
"",
|
|
331
|
+
// LOCAL layer of the unified run flow: per-tool token_source rollup of the accumulated
|
|
332
|
+
// run records. Counts + sources only — no cost/savings figures in this section.
|
|
333
|
+
"Per-tool run records (token sources)",
|
|
334
|
+
`Source: ${summary.run_records_source_glob}`,
|
|
335
|
+
PER_TOOL_RECORDS_HEADER,
|
|
336
|
+
...formatPerToolRunRecordLines(summary.per_tool_token_records),
|
|
337
|
+
...summary.run_record_skipped_files.map((file) => `- skipped record ${file.path}: ${file.reason}`),
|
|
338
|
+
"",
|
|
263
339
|
"Skipped files",
|
|
264
340
|
...(skippedLines.length === 0 ? ["- none"] : skippedLines)
|
|
265
341
|
].join("\n");
|
|
@@ -288,6 +364,13 @@ export function formatRunSummaryMarkdown(summary) {
|
|
|
288
364
|
`- Total saving per run: ${formatCurrency(summary.total_saving_per_run)}`,
|
|
289
365
|
`- Saving figures are estimated and summed over the ${summary.distinctness.distinct_runs} distinct recorded run${summary.distinctness.distinct_runs === 1 ? "" : "s"} (${summary.distinctness.duplicate_runs} duplicate(s) detected by content fingerprint and excluded).`,
|
|
290
366
|
"",
|
|
367
|
+
"## Measured Estimate Delta (across distinct runs)",
|
|
368
|
+
"",
|
|
369
|
+
`- Rung: ${summary.measured_estimate_delta.evidence_rung}`,
|
|
370
|
+
`- Distinct runs: ${summary.measured_estimate_delta.distinct_run_count}`,
|
|
371
|
+
`- Mean percent reduction: ${summary.measured_estimate_delta.mean_percent_reduction.toFixed(2)}% (±2·SE [${summary.measured_estimate_delta.ci_low.toFixed(2)}%, ${summary.measured_estimate_delta.ci_high.toFixed(2)}%])`,
|
|
372
|
+
`- ${summary.measured_estimate_delta.label}`,
|
|
373
|
+
"",
|
|
291
374
|
"## Savings by policy",
|
|
292
375
|
"",
|
|
293
376
|
"| Policy | Runs | Tokens saved | Saving per run |",
|
|
@@ -306,6 +389,14 @@ export function formatRunSummaryMarkdown(summary) {
|
|
|
306
389
|
"| --- | --- | ---: | ---: | ---: |",
|
|
307
390
|
...(topRunRows.length === 0 ? ["| none | none | 0 | 0.00% | $0.000000 |"] : topRunRows),
|
|
308
391
|
"",
|
|
392
|
+
"## Per-tool run records (token sources)",
|
|
393
|
+
"",
|
|
394
|
+
`- Source: ${summary.run_records_source_glob}`,
|
|
395
|
+
`- ${PER_TOOL_RECORDS_HEADER}`,
|
|
396
|
+
"",
|
|
397
|
+
...formatPerToolRunRecordLines(summary.per_tool_token_records),
|
|
398
|
+
...summary.run_record_skipped_files.map((file) => `- skipped record ${file.path}: ${file.reason}`),
|
|
399
|
+
"",
|
|
309
400
|
"## Skipped files",
|
|
310
401
|
"",
|
|
311
402
|
"| Path | Reason |",
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared HONEST report for the unified `compaction run <tool> -- …` flow (PUBLIC CLI/SDK — engine-free).
|
|
3
|
+
* Design: `docs/design/unified-run-flow.md` (ACCEPTED 2026-06-28, founder D1/D2-amended/D3), specifically
|
|
4
|
+
* the D2 honesty matrix.
|
|
5
|
+
*
|
|
6
|
+
* This renders the per-tool token honesty for a captured run, keeping the THREE axes distinct and never
|
|
7
|
+
* conflating them (founder D2):
|
|
8
|
+
* 1. provider-reported tokens (Codex / Claude Code)
|
|
9
|
+
* 2. locally observed / estimated tokens (Cursor input; Cursor output where separable)
|
|
10
|
+
* 3. savings ← output savings are NEVER shown here
|
|
11
|
+
*
|
|
12
|
+
* Binding rails encoded here (see the design's honesty matrix):
|
|
13
|
+
* - `token_source` is surfaced explicitly as **provider-reported | local-estimate | unavailable**.
|
|
14
|
+
* - **Input and output tokens are shown SEPARATELY.**
|
|
15
|
+
* - Output tokens are shown **as tokens, never as a saving** (output-shaping savings are a gated
|
|
16
|
+
* first-impl; no output-savings figure until measured + eval-confirmed).
|
|
17
|
+
* - Input before/after + reduction (the REAL compaction saving) may be shown by the caller from the
|
|
18
|
+
* compaction report; this module labels that reduction measured-vs-estimated per the token source.
|
|
19
|
+
*
|
|
20
|
+
* This module computes NOTHING proprietary and reads NO engine internals — it only formats counts and
|
|
21
|
+
* honest source labels already produced by the per-tool capture (`UsageMetadata` + output status).
|
|
22
|
+
*/
|
|
23
|
+
import type { UsageMetadata } from "./usage-metadata.js";
|
|
24
|
+
/** The honest per-FIELD token source, per the D2 honesty matrix. */
|
|
25
|
+
export type RunFlowTokenSource = "provider-reported" | "local-estimate" | "unavailable";
|
|
26
|
+
/** Whether the wrapper could safely separate/count output tokens for this run. */
|
|
27
|
+
export type RunFlowOutputStatus = "present" | "unavailable";
|
|
28
|
+
export interface RunFlowTokenReport {
|
|
29
|
+
tool: string;
|
|
30
|
+
/** Source for the INPUT token count (never stronger than the evidence). */
|
|
31
|
+
input_token_source: RunFlowTokenSource;
|
|
32
|
+
/** Source for the OUTPUT token count; `unavailable` when the field could not be safely counted. */
|
|
33
|
+
output_token_source: RunFlowTokenSource;
|
|
34
|
+
input_tokens?: number;
|
|
35
|
+
output_tokens?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Label for how the (real, input) compaction reduction shown by the caller should be read:
|
|
38
|
+
* `measured` when the capture is provider-reported, `estimated` when it is a local estimate.
|
|
39
|
+
*/
|
|
40
|
+
input_reduction_label: "measured" | "estimated";
|
|
41
|
+
/** Honest notes carried from the capture (e.g. "not billing-confirmed", "no separable result field"). */
|
|
42
|
+
notes: string[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build the honest per-run token report from a capture's `UsageMetadata` and the wrapper's output status.
|
|
46
|
+
* `outputStatus === "unavailable"` forces `output_token_source: "unavailable"` regardless of the input
|
|
47
|
+
* source — a genuinely-missing output field is never silently labeled provider-reported or local-estimate.
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildRunFlowTokenReport(params: {
|
|
50
|
+
tool: string;
|
|
51
|
+
usage: UsageMetadata;
|
|
52
|
+
outputStatus: RunFlowOutputStatus;
|
|
53
|
+
}): RunFlowTokenReport;
|
|
54
|
+
/** Optional TRUE per-axis unavailability reasons from the capture (printed on the token lines). */
|
|
55
|
+
export interface RunFlowUnavailableReasons {
|
|
56
|
+
input?: string;
|
|
57
|
+
output?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface RunFlowFormatOptions {
|
|
60
|
+
reasons?: RunFlowUnavailableReasons;
|
|
61
|
+
/**
|
|
62
|
+
* Set false on surfaces that do NOT print a compaction input-reduction below the block (e.g.
|
|
63
|
+
* `capture cursor`, which captures but does not compact) so the block never points the reader at
|
|
64
|
+
* reduction figures that are not there. Default true (the `run` front-ends compact + report below).
|
|
65
|
+
*/
|
|
66
|
+
inputReductionFollows?: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Render the shared honest token block for a run-flow front-end. This block:
|
|
70
|
+
* - states `token_source` explicitly per field (provider-reported | local-estimate | unavailable);
|
|
71
|
+
* - shows input and output SEPARATELY;
|
|
72
|
+
* - shows output as TOKENS only — it contains NO output-savings figure and no savings language for output.
|
|
73
|
+
*
|
|
74
|
+
* `options.reasons` carries the capture's TRUE per-axis unavailability reasons so an unavailable axis
|
|
75
|
+
* prints WHY (e.g. "a saved Cursor export does not contain the prompt …") instead of the generic fallback.
|
|
76
|
+
* The same reasons already ride in `report.notes` (capture limitations), so the persisted run record and
|
|
77
|
+
* the summary rollup carry the identical reason — no divergence between surfaces.
|
|
78
|
+
*
|
|
79
|
+
* The caller prints the (real) INPUT before/after reduction from the compaction report separately; the
|
|
80
|
+
* `input_reduction_label` in the report tells the caller whether that reduction is measured or estimated.
|
|
81
|
+
*/
|
|
82
|
+
export declare function formatRunFlowTokenReport(report: RunFlowTokenReport, options?: RunFlowFormatOptions): string[];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map a capture's whole-usage source to the per-field honest token source. Output can independently be
|
|
3
|
+
* `unavailable` even when input is counted (the Cursor case), so the two fields carry sources separately.
|
|
4
|
+
*/
|
|
5
|
+
function fieldSource(usage) {
|
|
6
|
+
if (usage.provider_reported_tokens)
|
|
7
|
+
return "provider-reported";
|
|
8
|
+
if (usage.estimated_tokens)
|
|
9
|
+
return "local-estimate";
|
|
10
|
+
return "unavailable";
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build the honest per-run token report from a capture's `UsageMetadata` and the wrapper's output status.
|
|
14
|
+
* `outputStatus === "unavailable"` forces `output_token_source: "unavailable"` regardless of the input
|
|
15
|
+
* source — a genuinely-missing output field is never silently labeled provider-reported or local-estimate.
|
|
16
|
+
*/
|
|
17
|
+
export function buildRunFlowTokenReport(params) {
|
|
18
|
+
const source = fieldSource(params.usage);
|
|
19
|
+
const inputTokens = params.usage.input_tokens;
|
|
20
|
+
const outputTokens = params.usage.output_tokens;
|
|
21
|
+
// Input source is `unavailable` when there is no input count at all; otherwise the capture's source.
|
|
22
|
+
const inputSource = inputTokens === undefined ? "unavailable" : source;
|
|
23
|
+
// Output source is `unavailable` when the wrapper could not separate output, OR there is no output count.
|
|
24
|
+
const outputSource = params.outputStatus === "unavailable" || outputTokens === undefined ? "unavailable" : source;
|
|
25
|
+
return {
|
|
26
|
+
tool: params.tool,
|
|
27
|
+
input_token_source: inputSource,
|
|
28
|
+
output_token_source: outputSource,
|
|
29
|
+
...(inputTokens !== undefined ? { input_tokens: inputTokens } : {}),
|
|
30
|
+
...(outputTokens !== undefined ? { output_tokens: outputTokens } : {}),
|
|
31
|
+
input_reduction_label: params.usage.provider_reported_tokens ? "measured" : "estimated",
|
|
32
|
+
notes: [...params.usage.limitations]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function tokenLine(label, tokens, source, reason) {
|
|
36
|
+
if (source === "unavailable" || tokens === undefined) {
|
|
37
|
+
// An unavailable axis always carries a reason: the capture's TRUE per-run reason when it has one,
|
|
38
|
+
// else the generic honest fallback — never a bare "unavailable"/"unknown" with nothing after it.
|
|
39
|
+
const fallback = source === "unavailable" ? "not safely separable / not reported" : "no count";
|
|
40
|
+
return ` ${label} tokens: unavailable (${reason ?? fallback})`;
|
|
41
|
+
}
|
|
42
|
+
return ` ${label} tokens: ${tokens} (source: ${source})`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Render the shared honest token block for a run-flow front-end. This block:
|
|
46
|
+
* - states `token_source` explicitly per field (provider-reported | local-estimate | unavailable);
|
|
47
|
+
* - shows input and output SEPARATELY;
|
|
48
|
+
* - shows output as TOKENS only — it contains NO output-savings figure and no savings language for output.
|
|
49
|
+
*
|
|
50
|
+
* `options.reasons` carries the capture's TRUE per-axis unavailability reasons so an unavailable axis
|
|
51
|
+
* prints WHY (e.g. "a saved Cursor export does not contain the prompt …") instead of the generic fallback.
|
|
52
|
+
* The same reasons already ride in `report.notes` (capture limitations), so the persisted run record and
|
|
53
|
+
* the summary rollup carry the identical reason — no divergence between surfaces.
|
|
54
|
+
*
|
|
55
|
+
* The caller prints the (real) INPUT before/after reduction from the compaction report separately; the
|
|
56
|
+
* `input_reduction_label` in the report tells the caller whether that reduction is measured or estimated.
|
|
57
|
+
*/
|
|
58
|
+
export function formatRunFlowTokenReport(report, options) {
|
|
59
|
+
return [
|
|
60
|
+
`Token reality for ${report.tool} (honest per-field source):`,
|
|
61
|
+
tokenLine("input", report.input_tokens, report.input_token_source, options?.reasons?.input),
|
|
62
|
+
tokenLine("output", report.output_tokens, report.output_token_source, options?.reasons?.output),
|
|
63
|
+
// The one binding output rail, stated plainly so no reader mistakes observed output for a saving.
|
|
64
|
+
" output is shown as TOKENS ONLY — output-token savings are not claimed (gated on measured + eval-confirmed output-shaping).",
|
|
65
|
+
...(options?.inputReductionFollows === false
|
|
66
|
+
? []
|
|
67
|
+
: [` input-reduction figures below are ${report.input_reduction_label} (from the token source above).`]),
|
|
68
|
+
...report.notes.map((n) => ` note: ${n}`)
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=run-flow-report.js.map
|
|
@@ -83,7 +83,14 @@ export function evidenceSourceTypeFromTrace(source) {
|
|
|
83
83
|
// "real_captured" does), so a passing report on a codex_import trace caps at
|
|
84
84
|
// "conditional". This mirrors the provider_usage classification below
|
|
85
85
|
// (docs/design/provider-api-tracesource-classification.md, Option 2).
|
|
86
|
-
|
|
86
|
+
//
|
|
87
|
+
// `cursor_import` is the SAME class for an imported real Cursor EXPORT
|
|
88
|
+
// (docs/design/cursor-capture-adapter.md, D2): genuine local input, stronger than a
|
|
89
|
+
// `manual` fixture, strictly BELOW `real_captured`, caps at "conditional".
|
|
90
|
+
if (source === "cli_wrapper" ||
|
|
91
|
+
source === "local_command" ||
|
|
92
|
+
source === "codex_import" ||
|
|
93
|
+
source === "cursor_import") {
|
|
87
94
|
return "imported_local";
|
|
88
95
|
}
|
|
89
96
|
if (source === "real_captured") {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type AppendActivityEventResult } from "./activity-store.js";
|
|
2
|
+
export interface CodexShimBridgeResult {
|
|
3
|
+
result: AppendActivityEventResult;
|
|
4
|
+
/** "present" when a turn.completed.usage block was found (provider-reported), else "missing". */
|
|
5
|
+
tokenMetadataStatus: "present" | "missing";
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Bridge a captured `codex exec --json` output copy into ONE metrics-only activity event.
|
|
9
|
+
* `surface: "codex"`, `provider: "openai"`; provider-reported tokens where usage existed, else
|
|
10
|
+
* unavailable-with-reason. Content-free + best-effort: a local failure is returned as an unappended
|
|
11
|
+
* result, never thrown, so it can never break the transparent shim.
|
|
12
|
+
*/
|
|
13
|
+
export declare function bridgeCodexShimActivity(input: {
|
|
14
|
+
rawOutput: string;
|
|
15
|
+
cwd?: string;
|
|
16
|
+
}): Promise<CodexShimBridgeResult>;
|
|
17
|
+
export interface CursorShimBridgeResult {
|
|
18
|
+
result: AppendActivityEventResult;
|
|
19
|
+
inputStatus: "present" | "unavailable";
|
|
20
|
+
outputStatus: "present" | "unavailable";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Bridge a captured Cursor headless output copy into ONE metrics-only activity event.
|
|
24
|
+
* `surface: "cursor"`, `provider: "cursor"`; LOCAL-ESTIMATE only (NEVER provider-reported), output
|
|
25
|
+
* unavailable-with-reason when the result is not separable. `commandParts` (the ORIGINAL invocation)
|
|
26
|
+
* lets input be locally estimated from the prompt (counted, never stored). Content-free, best-effort.
|
|
27
|
+
*/
|
|
28
|
+
export declare function bridgeCursorShimActivity(input: {
|
|
29
|
+
rawOutput: string;
|
|
30
|
+
commandParts?: string[];
|
|
31
|
+
cwd?: string;
|
|
32
|
+
}): Promise<CursorShimBridgeResult>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shim → metrics-only activity BRIDGE (PUBLIC CLI/SDK code — engine-free, ships in the npm package).
|
|
3
|
+
*
|
|
4
|
+
* This is the exact analogue of the run-8 from-hook → activity bridge (`appendClaudeCodeHookActivity`
|
|
5
|
+
* in `src/cli/commands/capture-claude-code.ts`), reused verbatim in shape for the Codex/Cursor PATH
|
|
6
|
+
* shims (charter 2026-07-04-run-9): parse CONTENT-FREE usage → build the honest per-field token report
|
|
7
|
+
* → build the cross-surface event → wrap as a measure-only activity event → append ONE line to the
|
|
8
|
+
* local activity store. THIS is what makes a subsequent `codex exec --json` / `cursor-agent …
|
|
9
|
+
* --output-format json` run appear in `compaction activity` with NO manual import.
|
|
10
|
+
*
|
|
11
|
+
* Honesty (identical rails to the reused parsers/validators):
|
|
12
|
+
* - Codex: provider-reported tokens from `turn.completed.usage` when present, else
|
|
13
|
+
* unavailable-with-reason — never invented, never a silent zero.
|
|
14
|
+
* - Cursor: LOCAL-ESTIMATE only (chars/4); NEVER provider-reported; output unavailable-with-reason
|
|
15
|
+
* when the `result` field is not separable. The cross-surface tier table structurally rejects a
|
|
16
|
+
* provider-reported label on the `cursor` surface.
|
|
17
|
+
* - CONTENT-FREE: only token COUNTS + honest source labels + opaque ids ride on the event. No
|
|
18
|
+
* prompt/response/message text is ever read into the event (the raw output is parsed for usage
|
|
19
|
+
* fields only, and for Cursor the prompt is counted, never stored).
|
|
20
|
+
* - Measurement only: approval "not-required", auto-apply OFF (not eligible, ask-each-time,
|
|
21
|
+
* applied_automatically false), recovery original_retained=false, sync "local-only".
|
|
22
|
+
* - Deterministic id → the store DEDUPES a re-parse of the same run (same state = one event).
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
import { normalizeCodexExecEvents, CODEX_UNKNOWN_MODEL } from "./codex-capture.js";
|
|
27
|
+
import { normalizeCursorAgentOutput } from "./cursor-capture.js";
|
|
28
|
+
import { buildRunFlowTokenReport } from "./run-flow-report.js";
|
|
29
|
+
import { buildRunCrossSurfaceEvent } from "./cross-surface-event.js";
|
|
30
|
+
import { buildMeasureOnlyActivityEvent } from "./activity-event.js";
|
|
31
|
+
import { appendActivityEvent, DEFAULT_ACTIVITY_DIRECTORY } from "./activity-store.js";
|
|
32
|
+
/** A content-free, deterministic run id from opaque ids + token counts (never any content). */
|
|
33
|
+
function contentFreeRunId(prefix, parts) {
|
|
34
|
+
const digest = createHash("sha256").update(parts.map((p) => String(p ?? "")).join("|")).digest("hex");
|
|
35
|
+
return `${prefix}-${digest.slice(0, 16)}`;
|
|
36
|
+
}
|
|
37
|
+
/** The activity directory under a given cwd (mirrors the from-hook bridge: cwd-scoped). */
|
|
38
|
+
function activityDirFor(cwd) {
|
|
39
|
+
return cwd ? path.join(cwd, ".compaction", "activity") : DEFAULT_ACTIVITY_DIRECTORY;
|
|
40
|
+
}
|
|
41
|
+
const CODEX_MISSING_USAGE_REASON = "the captured codex exec output carried no turn.completed.usage block (provider usage unavailable — not invented)";
|
|
42
|
+
/**
|
|
43
|
+
* Bridge a captured `codex exec --json` output copy into ONE metrics-only activity event.
|
|
44
|
+
* `surface: "codex"`, `provider: "openai"`; provider-reported tokens where usage existed, else
|
|
45
|
+
* unavailable-with-reason. Content-free + best-effort: a local failure is returned as an unappended
|
|
46
|
+
* result, never thrown, so it can never break the transparent shim.
|
|
47
|
+
*/
|
|
48
|
+
export async function bridgeCodexShimActivity(input) {
|
|
49
|
+
const norm = normalizeCodexExecEvents({ captureId: "shim", rawOutput: input.rawOutput });
|
|
50
|
+
const usage = norm.usageMetadata;
|
|
51
|
+
const present = norm.tokenMetadataStatus === "present";
|
|
52
|
+
const tokenReport = buildRunFlowTokenReport({ tool: "codex", usage, outputStatus: present ? "present" : "unavailable" });
|
|
53
|
+
const threadId = norm.trace.id && !norm.trace.id.startsWith("trace_codex_") ? norm.trace.id : undefined;
|
|
54
|
+
const runId = contentFreeRunId("codex-shim", [threadId, usage.input_tokens, usage.output_tokens, usage.cache_read_input_tokens]);
|
|
55
|
+
const event = buildRunCrossSurfaceEvent("codex", {
|
|
56
|
+
runId,
|
|
57
|
+
tokenReport,
|
|
58
|
+
reasons: present ? {} : { input: CODEX_MISSING_USAGE_REASON, output: CODEX_MISSING_USAGE_REASON },
|
|
59
|
+
...(usage.model && usage.model !== CODEX_UNKNOWN_MODEL ? { modelLabel: usage.model } : {})
|
|
60
|
+
});
|
|
61
|
+
const eventWithSession = threadId ? { ...event, session_id: threadId } : event;
|
|
62
|
+
const activityEvent = buildMeasureOnlyActivityEvent(eventWithSession, { original_retained: false });
|
|
63
|
+
const result = await appendActivityEvent(activityEvent, activityDirFor(input.cwd));
|
|
64
|
+
return { result, tokenMetadataStatus: norm.tokenMetadataStatus };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Bridge a captured Cursor headless output copy into ONE metrics-only activity event.
|
|
68
|
+
* `surface: "cursor"`, `provider: "cursor"`; LOCAL-ESTIMATE only (NEVER provider-reported), output
|
|
69
|
+
* unavailable-with-reason when the result is not separable. `commandParts` (the ORIGINAL invocation)
|
|
70
|
+
* lets input be locally estimated from the prompt (counted, never stored). Content-free, best-effort.
|
|
71
|
+
*/
|
|
72
|
+
export async function bridgeCursorShimActivity(input) {
|
|
73
|
+
const norm = normalizeCursorAgentOutput({ captureId: "shim", rawOutput: input.rawOutput, commandParts: input.commandParts });
|
|
74
|
+
const usage = norm.usageMetadata;
|
|
75
|
+
const tokenReport = buildRunFlowTokenReport({ tool: "cursor", usage, outputStatus: norm.outputStatus });
|
|
76
|
+
const sessionId = norm.trace.id && !norm.trace.id.startsWith("trace_cursor_") ? norm.trace.id : undefined;
|
|
77
|
+
const runId = contentFreeRunId("cursor-shim", [sessionId, usage.input_tokens, usage.output_tokens]);
|
|
78
|
+
const event = buildRunCrossSurfaceEvent("cursor", {
|
|
79
|
+
runId,
|
|
80
|
+
tokenReport,
|
|
81
|
+
reasons: { input: norm.inputUnavailableReason, output: norm.outputUnavailableReason }
|
|
82
|
+
});
|
|
83
|
+
const eventWithSession = sessionId ? { ...event, session_id: sessionId } : event;
|
|
84
|
+
const activityEvent = buildMeasureOnlyActivityEvent(eventWithSession, { original_retained: false });
|
|
85
|
+
const result = await appendActivityEvent(activityEvent, activityDirFor(input.cwd));
|
|
86
|
+
return { result, inputStatus: norm.inputStatus, outputStatus: norm.outputStatus };
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=shim-capture-bridge.js.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { SKILL_INJECTION_POLICY_NAME, type PolicyMiddlewareStateCapsule, type SkillInjectionProvenance } from "./policy-types.js";
|
|
2
|
+
import type { AgentTrace, StateCapsule, TraceMessage } from "./types.js";
|
|
3
|
+
export { SKILL_INJECTION_POLICY_NAME };
|
|
4
|
+
export type { SkillInjectionProvenance };
|
|
5
|
+
/**
|
|
6
|
+
* APPROVAL-REQUIRED rung of the role:user skill-injection compaction policy.
|
|
7
|
+
*
|
|
8
|
+
* SCOPE (per docs/design/skill-injection-compaction-policy.md §1–§5, approval-required rung):
|
|
9
|
+
* This module builds the byte-identical, same-skill `role:user` skill-injection compaction
|
|
10
|
+
* candidates and their state capsules. It is the SECOND, separately-named compaction policy
|
|
11
|
+
* (`repeated_skill_injection_to_state_capsule`). It NEVER runs by default: `applyCompactionPolicy`
|
|
12
|
+
* only invokes it when `compactSkillInjections === true`, which is set ONLY on explicit approval
|
|
13
|
+
* in the existing approve/apply loop (optimization-approval.ts). There is NO auto-apply path.
|
|
14
|
+
*
|
|
15
|
+
* Byte-identical-only boundary (design §2): the dedup key is the report-only detector's exact
|
|
16
|
+
* `skill:normalizeExactContent(content)` key. Near-identical (same skill, divergent trailing
|
|
17
|
+
* `ARGUMENTS:`) produces a DIFFERENT key and can NEVER be compacted. No prefix/fuzzy matching.
|
|
18
|
+
*
|
|
19
|
+
* Provenance (design §5 / Q2): the capture step prefixes SUBAGENT message ids `<agentId>-<uuid>`,
|
|
20
|
+
* but ROOT-agent injections carry a bare UUID (no encoded agent id). So every compacted skill
|
|
21
|
+
* injection's capsule carries an ADDITIVE `skillInjectionProvenance` field recording the owning
|
|
22
|
+
* agent id (parsed from the id prefix, or "root" when absent) AND the replaced copy's trace
|
|
23
|
+
* position — so a consumer can reconstruct per-agent / per-position receipt even for the bare-UUID
|
|
24
|
+
* root copies. This is additive metadata on the capsule; it does NOT change the AgentTrace /
|
|
25
|
+
* TraceSource / TraceMessage schema.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* A skill-injection compaction candidate: a later byte-identical copy to be replaced by a
|
|
29
|
+
* `whole_message` state capsule pointing at the FIRST copy of its `skill:exactContent` group.
|
|
30
|
+
*/
|
|
31
|
+
export interface SkillInjectionCompactionCandidate {
|
|
32
|
+
skillName: string;
|
|
33
|
+
firstCopyMessage: TraceMessage;
|
|
34
|
+
firstCopyIndex: number;
|
|
35
|
+
replacedMessage: TraceMessage;
|
|
36
|
+
replacedIndex: number;
|
|
37
|
+
replacedTokens: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Recover the owning agent id of a captured message. Subagent message ids are prefixed
|
|
41
|
+
* `<agentId>-<uuid>` by the claude-code adapter; root-agent ids are bare UUIDs (no prefix).
|
|
42
|
+
* Returns "root" with recovered=false when no agent prefix is present.
|
|
43
|
+
*/
|
|
44
|
+
export declare function owningAgentIdForMessage(message: TraceMessage): {
|
|
45
|
+
owningAgentId: string;
|
|
46
|
+
recoveredFromPrefix: boolean;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Find byte-identical, same-skill later skill-injection copies (design §1/§2). First occurrence
|
|
50
|
+
* of each `skill:exactContent` group is retained; each later exact-key copy is a candidate.
|
|
51
|
+
* Pure: returns candidates; never mutates the trace.
|
|
52
|
+
*/
|
|
53
|
+
export declare function findSkillInjectionCompactionCandidates(trace: AgentTrace): SkillInjectionCompactionCandidate[];
|
|
54
|
+
/**
|
|
55
|
+
* Build `whole_message` state capsules for skill-injection candidates, mirroring the tool-output
|
|
56
|
+
* policy's capsule shape (`PolicyMiddlewareStateCapsule`) and reusing the same SourcePointer /
|
|
57
|
+
* provenance machinery. Each capsule additionally carries the §5 `skillInjectionProvenance`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function createSkillInjectionCapsules(trace: AgentTrace, candidates: SkillInjectionCompactionCandidate[], baseCapsule: Pick<StateCapsule, "retainedFacts" | "openQuestions" | "safetyNotes">, idOffset?: number): PolicyMiddlewareStateCapsule[];
|
|
60
|
+
/**
|
|
61
|
+
* Rollback / recovery (design §Q5): re-expand a skill-injection capsule's first-copy pointer to
|
|
62
|
+
* restore the original later-copy content. Because the bytes are byte-identical by construction,
|
|
63
|
+
* the restored content equals the retained first copy. Returns the restored content, or null when
|
|
64
|
+
* the pointer no longer resolves (which a safety check would already have failed).
|
|
65
|
+
*/
|
|
66
|
+
export declare function restoreCompactedSkillInjectionContent(originalTrace: AgentTrace, capsule: PolicyMiddlewareStateCapsule): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* Round-trip rollback (design §Q5): given the compacted messages and the skill-injection capsules,
|
|
69
|
+
* restore the ORIGINAL trace messages by re-expanding each capsule pointer back to the first-copy
|
|
70
|
+
* bytes. Asserts byte-for-byte recovery of the later copies. Pure; returns a restored message list.
|
|
71
|
+
*/
|
|
72
|
+
export declare function rollbackSkillInjectionCompaction(originalTrace: AgentTrace, compactedMessages: TraceMessage[], capsules: PolicyMiddlewareStateCapsule[]): TraceMessage[];
|