@compaction/cli 0.1.2 → 0.2.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 +11 -1
- package/dist/cli/commands/compact.js +15 -0
- package/dist/cli/commands/context.d.ts +2 -0
- package/dist/cli/commands/context.js +130 -0
- package/dist/cli/commands/import.js +3 -1
- package/dist/cli/commands/init.js +184 -157
- package/dist/cli/commands/run.js +13 -1
- package/dist/cli/index.js +4 -1
- package/dist/cli/onboarding/InitTui.d.ts +52 -0
- package/dist/cli/onboarding/InitTui.js +153 -0
- package/dist/cli/onboarding/model.d.ts +39 -0
- package/dist/cli/onboarding/model.js +77 -0
- package/dist/cli/onboarding/should-use-tui.d.ts +25 -0
- package/dist/cli/onboarding/should-use-tui.js +35 -0
- package/dist/cli/onboarding/wordmark.d.ts +28 -0
- package/dist/cli/onboarding/wordmark.js +72 -0
- package/dist/core/aggregate-format.d.ts +3 -1
- package/dist/core/aggregate-format.js +18 -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/report-generator.d.ts +32 -1
- package/dist/core/report-generator.js +67 -0
- package/dist/core/run-aggregator.d.ts +64 -0
- package/dist/core/run-aggregator.js +132 -3
- package/dist/core/session-aggregate.d.ts +43 -0
- package/dist/core/session-aggregate.js +89 -7
- package/dist/core/trace-adapters.d.ts +8 -0
- package/dist/core/trace-adapters.js +4 -0
- package/dist/core/types.d.ts +55 -0
- package/package.json +5 -1
|
@@ -46,6 +46,14 @@ export interface RunRecord {
|
|
|
46
46
|
run_directory: string;
|
|
47
47
|
report_path: string;
|
|
48
48
|
model: string;
|
|
49
|
+
/**
|
|
50
|
+
* Content-addressed distinctness fingerprint digest of the source trace, when the run's
|
|
51
|
+
* report.json carried one (`trace_fingerprint.content_sha256`). Used to de-duplicate a
|
|
52
|
+
* run captured/aggregated twice (same digest → counted once) so headline savings are never
|
|
53
|
+
* inflated. `null` for older reports without a fingerprint — those are treated as distinct
|
|
54
|
+
* (cannot dedup what cannot be identified) and noted honestly, never collapsed.
|
|
55
|
+
*/
|
|
56
|
+
trace_fingerprint: string | null;
|
|
49
57
|
/** Token evidence tier for this run (drives the provider-reported vs local-estimate split). */
|
|
50
58
|
token_evidence: RunTokenEvidence;
|
|
51
59
|
input_tokens_before: number;
|
|
@@ -73,6 +81,32 @@ export interface SkippedRunDirectory {
|
|
|
73
81
|
run_directory: string;
|
|
74
82
|
reason: string;
|
|
75
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Distinctness (duplicate-safety) summary over a set of runs, by content fingerprint.
|
|
86
|
+
*
|
|
87
|
+
* This is a count of DISTINCT captured runs by one-way content fingerprint — NOT a billing,
|
|
88
|
+
* provider, or semantic claim. A run captured/aggregated twice with the SAME fingerprint is a
|
|
89
|
+
* duplicate and is counted once; a run WITHOUT a fingerprint cannot be de-duplicated and is
|
|
90
|
+
* treated as distinct (and reported in `runs_without_fingerprint`), never collapsed.
|
|
91
|
+
*
|
|
92
|
+
* The headline token/cost/savings totals in `AggregateTotals` are computed over the
|
|
93
|
+
* duplicate-safe set (each distinct run once), so a re-captured run never double-counts.
|
|
94
|
+
*/
|
|
95
|
+
export interface DistinctnessSummary {
|
|
96
|
+
/** Total runs considered before de-duplication (M in "distinct N of M"). */
|
|
97
|
+
total_runs: number;
|
|
98
|
+
/**
|
|
99
|
+
* Distinct runs after de-duplication (N): distinct verified fingerprints + every run without
|
|
100
|
+
* a fingerprint (each unidentifiable run is its own distinct contribution).
|
|
101
|
+
*/
|
|
102
|
+
distinct_runs: number;
|
|
103
|
+
/** Duplicate runs detected and excluded from the headline double-count (K = total - distinct). */
|
|
104
|
+
duplicate_runs: number;
|
|
105
|
+
/** Runs whose fingerprint was present and verifiable (contributed to `distinct_runs`, de-duplicated). */
|
|
106
|
+
runs_with_fingerprint: number;
|
|
107
|
+
/** Runs with NO fingerprint (older reports): each counted as distinct; cannot be de-duplicated. */
|
|
108
|
+
runs_without_fingerprint: number;
|
|
109
|
+
}
|
|
76
110
|
/** Aggregated token/cost/savings totals over a set of runs, with the evidence split. */
|
|
77
111
|
export interface AggregateTotals {
|
|
78
112
|
run_count: number;
|
|
@@ -134,6 +168,8 @@ export interface SessionSummary {
|
|
|
134
168
|
};
|
|
135
169
|
totals: AggregateTotals;
|
|
136
170
|
verification: VerificationCounts;
|
|
171
|
+
/** Duplicate-safety summary for this session's runs (distinct N of M by content fingerprint). */
|
|
172
|
+
distinctness: DistinctnessSummary;
|
|
137
173
|
}
|
|
138
174
|
export interface AggregateReport {
|
|
139
175
|
aggregate_id: string;
|
|
@@ -146,6 +182,12 @@ export interface AggregateReport {
|
|
|
146
182
|
session_count: number;
|
|
147
183
|
totals: AggregateTotals;
|
|
148
184
|
verification: VerificationCounts;
|
|
185
|
+
/**
|
|
186
|
+
* Cross-session duplicate-safety summary (distinct N of M captured runs by content fingerprint).
|
|
187
|
+
* The headline `totals` above are computed over the duplicate-safe set, so a run captured/
|
|
188
|
+
* aggregated twice with the same fingerprint is counted once and never inflates savings.
|
|
189
|
+
*/
|
|
190
|
+
distinctness: DistinctnessSummary;
|
|
149
191
|
sessions: SessionSummary[];
|
|
150
192
|
/** Provider/runtime breakdown (operator-asserted provider label; unlabeled when absent). */
|
|
151
193
|
by_provider: BreakdownBucket[];
|
|
@@ -166,6 +208,7 @@ export interface AggregateEvidenceLabels {
|
|
|
166
208
|
compactions: string;
|
|
167
209
|
verification: string;
|
|
168
210
|
provider_runtime: string;
|
|
211
|
+
distinctness: string;
|
|
169
212
|
}
|
|
170
213
|
export declare const AGGREGATE_EVIDENCE_LABELS: AggregateEvidenceLabels;
|
|
171
214
|
/** True when a run passes every supplied local-label filter (case-sensitive exact match). */
|
|
@@ -33,6 +33,7 @@ import { randomUUID } from "node:crypto";
|
|
|
33
33
|
import { readdir, readFile } from "node:fs/promises";
|
|
34
34
|
import { join } from "node:path";
|
|
35
35
|
import { NO_LABEL, parseRunLabels } from "./run-labels.js";
|
|
36
|
+
import { classifyDistinctness } from "./trace-fingerprint.js";
|
|
36
37
|
export const AGGREGATE_ARTIFACT_DIRECTORY = ".compaction/aggregates";
|
|
37
38
|
export const AGGREGATE_EVIDENCE_LABELS = {
|
|
38
39
|
input_tokens: "input tokens: provider-reported where the run's token accounting was measured (provider usage " +
|
|
@@ -50,7 +51,12 @@ export const AGGREGATE_EVIDENCE_LABELS = {
|
|
|
50
51
|
"task-check outcomes copied from each run's strong-eval; runs without a strong-eval are not_evaluated, " +
|
|
51
52
|
"never counted as a pass. These are DETERMINISTIC recoverability checks, NOT semantic guarantees.",
|
|
52
53
|
provider_runtime: "provider/runtime: operator-ASSERTED local label (never provider-verified); runs without a provider " +
|
|
53
|
-
"label are grouped under 'unlabeled'. This label is distinct from the token-evidence tier."
|
|
54
|
+
"label are grouped under 'unlabeled'. This label is distinct from the token-evidence tier.",
|
|
55
|
+
distinctness: "distinct runs: a count of DISTINCT captured runs by one-way content fingerprint (SHA-256 over the " +
|
|
56
|
+
"normalized trace). A run captured/aggregated twice with the SAME fingerprint is a duplicate and is " +
|
|
57
|
+
"counted once, so headline savings are never inflated; runs without a fingerprint cannot be " +
|
|
58
|
+
"de-duplicated and are each counted as distinct. This is a local content-distinctness count — NOT a " +
|
|
59
|
+
"billing, provider, or semantic claim."
|
|
54
60
|
};
|
|
55
61
|
const STANDARD_LIMITATIONS = [
|
|
56
62
|
"Aggregated from LOCAL artifacts only (.compaction/runs/*/{report.json,strong-eval.json,run-labels.json}); no provider calls, no upload, no telemetry.",
|
|
@@ -59,11 +65,66 @@ const STANDARD_LIMITATIONS = [
|
|
|
59
65
|
"Output tokens are summed only over runs that recorded them; runs without a strong-eval contribute unknown output tokens and are counted separately, never inferred.",
|
|
60
66
|
"Verification outcomes are deterministic recoverability checks copied from each run's strong-eval; they are NOT semantic or meaning-preservation guarantees. Runs without a strong-eval are not_evaluated, never a pass.",
|
|
61
67
|
"Labels (project/workflow/provider/user-label/session) are operator-supplied local tags with no evidence weight; provider here is operator-asserted, not provider-verified.",
|
|
68
|
+
"Headline savings are duplicate-safe: runs sharing a content fingerprint (a re-captured/re-aggregated run) are counted once; runs without a fingerprint cannot be de-duplicated and are each counted as distinct. This is a local content-distinctness count, not a billing/provider/semantic claim.",
|
|
62
69
|
"This is a LOCAL, file-based rollup. There is no hosted dashboard, database, auth, billing, model routing, or provider integration."
|
|
63
70
|
];
|
|
64
71
|
function round6(value) {
|
|
65
72
|
return Number((value + Number.EPSILON).toFixed(6));
|
|
66
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* De-duplicate a set of runs by content fingerprint for duplicate-safe headline totals.
|
|
76
|
+
*
|
|
77
|
+
* Keeps the FIRST run per distinct verified fingerprint; later runs with the same fingerprint
|
|
78
|
+
* are duplicates and are dropped from the headline (so a re-captured run never double-counts its
|
|
79
|
+
* savings). Runs WITHOUT a fingerprint are kept as-is — each is its own distinct contribution,
|
|
80
|
+
* because distinctness that cannot be verified is never collapsed (the anti-overcount contract in
|
|
81
|
+
* `classifyDistinctness`). Returns the duplicate-safe run set plus an honest distinctness summary.
|
|
82
|
+
*
|
|
83
|
+
* PURE: order-stable for the kept runs; makes savings MORE conservative, never larger.
|
|
84
|
+
*/
|
|
85
|
+
function dedupeRunsByFingerprint(runs) {
|
|
86
|
+
const seenFingerprints = new Set();
|
|
87
|
+
const distinctRuns = [];
|
|
88
|
+
let runsWithFingerprint = 0;
|
|
89
|
+
let runsWithoutFingerprint = 0;
|
|
90
|
+
for (const run of runs) {
|
|
91
|
+
const fp = run.trace_fingerprint;
|
|
92
|
+
if (typeof fp === "string" && fp.length > 0) {
|
|
93
|
+
runsWithFingerprint += 1;
|
|
94
|
+
if (seenFingerprints.has(fp))
|
|
95
|
+
continue; // duplicate fingerprint: excluded from headline.
|
|
96
|
+
seenFingerprints.add(fp);
|
|
97
|
+
distinctRuns.push(run);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
runsWithoutFingerprint += 1;
|
|
101
|
+
distinctRuns.push(run); // unidentifiable: always its own distinct contribution.
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// classifyDistinctness gives the verified-distinct count over the SAME records; distinct_runs is
|
|
105
|
+
// that verified-distinct count plus every unidentifiable run (each distinct by construction).
|
|
106
|
+
const classified = classifyDistinctness(runs.map((run) => ({ runId: run.run_id, content_sha256: run.trace_fingerprint })));
|
|
107
|
+
const distinctRunCount = classified.distinct_verified_count + runsWithoutFingerprint;
|
|
108
|
+
return {
|
|
109
|
+
distinctRuns,
|
|
110
|
+
distinctness: {
|
|
111
|
+
total_runs: runs.length,
|
|
112
|
+
distinct_runs: distinctRunCount,
|
|
113
|
+
duplicate_runs: runs.length - distinctRunCount,
|
|
114
|
+
runs_with_fingerprint: runsWithFingerprint,
|
|
115
|
+
runs_without_fingerprint: runsWithoutFingerprint
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function emptyDistinctness() {
|
|
120
|
+
return {
|
|
121
|
+
total_runs: 0,
|
|
122
|
+
distinct_runs: 0,
|
|
123
|
+
duplicate_runs: 0,
|
|
124
|
+
runs_with_fingerprint: 0,
|
|
125
|
+
runs_without_fingerprint: 0
|
|
126
|
+
};
|
|
127
|
+
}
|
|
67
128
|
function emptyTotals() {
|
|
68
129
|
return {
|
|
69
130
|
run_count: 0,
|
|
@@ -209,9 +270,12 @@ function buildSessions(runs) {
|
|
|
209
270
|
}
|
|
210
271
|
return [...sessions.entries()]
|
|
211
272
|
.map(([sessionId, { explicit, runs: sessionRuns }]) => {
|
|
273
|
+
// Duplicate-safe: fold totals/verification over distinct runs only (re-captured runs with
|
|
274
|
+
// the same fingerprint are excluded from the double-count). run_ids still lists every run.
|
|
275
|
+
const { distinctRuns, distinctness } = dedupeRunsByFingerprint(sessionRuns);
|
|
212
276
|
const totals = emptyTotals();
|
|
213
277
|
const verification = emptyVerification();
|
|
214
|
-
for (const run of
|
|
278
|
+
for (const run of distinctRuns) {
|
|
215
279
|
addRunToTotals(totals, run);
|
|
216
280
|
addRunToVerification(verification, run);
|
|
217
281
|
}
|
|
@@ -227,7 +291,8 @@ function buildSessions(runs) {
|
|
|
227
291
|
user_labels: distinct(sessionRuns.map((r) => r.labels.user_label))
|
|
228
292
|
},
|
|
229
293
|
totals,
|
|
230
|
-
verification
|
|
294
|
+
verification,
|
|
295
|
+
distinctness
|
|
231
296
|
};
|
|
232
297
|
})
|
|
233
298
|
.sort((a, b) => b.totals.total_input_tokens_saved - a.totals.total_input_tokens_saved ||
|
|
@@ -258,9 +323,13 @@ export function buildAggregateReport(runs, options = {}) {
|
|
|
258
323
|
const generatedAt = options.generatedAt ?? new Date().toISOString();
|
|
259
324
|
const filters = options.filters ?? {};
|
|
260
325
|
const matched = runs.filter((run) => runMatchesFilters(run, filters));
|
|
326
|
+
// Duplicate-safe headline: fold cross-session totals/verification over the distinct run set only.
|
|
327
|
+
// A run captured/aggregated twice with the same content fingerprint is counted once; runs without
|
|
328
|
+
// a fingerprint are each distinct (cannot dedup what cannot be identified). Never inflates savings.
|
|
329
|
+
const { distinctRuns, distinctness } = dedupeRunsByFingerprint(matched);
|
|
261
330
|
const totals = emptyTotals();
|
|
262
331
|
const verification = emptyVerification();
|
|
263
|
-
for (const run of
|
|
332
|
+
for (const run of distinctRuns) {
|
|
264
333
|
addRunToTotals(totals, run);
|
|
265
334
|
addRunToVerification(verification, run);
|
|
266
335
|
}
|
|
@@ -275,10 +344,13 @@ export function buildAggregateReport(runs, options = {}) {
|
|
|
275
344
|
session_count: sessions.length,
|
|
276
345
|
totals,
|
|
277
346
|
verification,
|
|
347
|
+
distinctness,
|
|
278
348
|
sessions,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
349
|
+
// Breakdowns also fold over the duplicate-safe distinct run set so per-bucket savings match the
|
|
350
|
+
// duplicate-safe headline (a re-captured run never double-counts inside its label bucket either).
|
|
351
|
+
by_provider: buildBreakdown(distinctRuns, (run) => run.labels.provider ?? NO_LABEL),
|
|
352
|
+
by_project: buildBreakdown(distinctRuns, (run) => run.labels.project ?? NO_LABEL),
|
|
353
|
+
by_workflow: buildBreakdown(distinctRuns, (run) => run.labels.workflow ?? NO_LABEL),
|
|
282
354
|
skipped_run_directories: options.skippedRunDirectories ?? [],
|
|
283
355
|
evidence_labels: AGGREGATE_EVIDENCE_LABELS,
|
|
284
356
|
limitations: STANDARD_LIMITATIONS
|
|
@@ -320,6 +392,15 @@ function readinessFrom(status) {
|
|
|
320
392
|
export function assembleRunRecord(input) {
|
|
321
393
|
const { report, strongEval } = input;
|
|
322
394
|
const notes = [];
|
|
395
|
+
// Content-addressed run identity: read the OPTIONAL trace_fingerprint digest if the report
|
|
396
|
+
// carried one. Older reports omit it; such a run cannot be de-duplicated and is treated as
|
|
397
|
+
// distinct (noted), never collapsed. Read defensively (digest string only; no content).
|
|
398
|
+
const fingerprintDigest = isRecord(report.trace_fingerprint) && typeof report.trace_fingerprint.content_sha256 === "string"
|
|
399
|
+
? report.trace_fingerprint.content_sha256
|
|
400
|
+
: null;
|
|
401
|
+
if (fingerprintDigest === null) {
|
|
402
|
+
notes.push("No trace_fingerprint on report.json: this run is treated as distinct (cannot de-duplicate an unidentifiable run).");
|
|
403
|
+
}
|
|
323
404
|
let tokenEvidence = "local_estimate";
|
|
324
405
|
let inputBefore = report.original_input_tokens;
|
|
325
406
|
let inputAfter = report.compacted_input_tokens;
|
|
@@ -389,6 +470,7 @@ export function assembleRunRecord(input) {
|
|
|
389
470
|
run_directory: input.runDirectory,
|
|
390
471
|
report_path: input.reportPath,
|
|
391
472
|
model,
|
|
473
|
+
trace_fingerprint: fingerprintDigest,
|
|
392
474
|
token_evidence: tokenEvidence,
|
|
393
475
|
input_tokens_before: inputBefore,
|
|
394
476
|
input_tokens_after: inputAfter,
|
|
@@ -19,6 +19,14 @@ export interface TraceAdapter {
|
|
|
19
19
|
displayName: string;
|
|
20
20
|
description: string;
|
|
21
21
|
supportedSource: TraceAdapterSource;
|
|
22
|
+
/**
|
|
23
|
+
* Honest integration-readiness label for this source (AGENTS.md Level 0–6 +
|
|
24
|
+
* validation status). All import sources are Level 1 (local export/import — no live
|
|
25
|
+
* integration); this string also discloses how PROVEN the source is (e.g. validated
|
|
26
|
+
* on a synthetic fixture vs real-artifact validation still open), so `--list-sources`
|
|
27
|
+
* never reads as a stronger integration claim than the evidence supports.
|
|
28
|
+
*/
|
|
29
|
+
readiness: string;
|
|
22
30
|
limitations: string[];
|
|
23
31
|
canHandle(input: unknown): boolean;
|
|
24
32
|
normalize(input: unknown, options: TraceAdapterOptions): TraceAdapterResult;
|
|
@@ -504,6 +504,7 @@ export const agentTraceAdapter = {
|
|
|
504
504
|
displayName: "Internal AgentTrace",
|
|
505
505
|
description: "Validates a local file that already matches the internal AgentTrace JSON shape.",
|
|
506
506
|
supportedSource: "agent-trace",
|
|
507
|
+
readiness: "Level 1 (local import) · canonical internal format, schema-validated",
|
|
507
508
|
limitations: [
|
|
508
509
|
"Only local JSON files are supported.",
|
|
509
510
|
"Live provider/runtime capture is future work.",
|
|
@@ -519,6 +520,7 @@ export const messagesAdapter = {
|
|
|
519
520
|
displayName: "Simple messages",
|
|
520
521
|
description: "Normalizes a local array of role/content messages, or an object with a messages array, into AgentTrace.",
|
|
521
522
|
supportedSource: "messages",
|
|
523
|
+
readiness: "Level 1 (local import) · generic role/content messages shape",
|
|
522
524
|
limitations: [
|
|
523
525
|
"Only local JSON files are supported.",
|
|
524
526
|
"Messages need supported roles and string content.",
|
|
@@ -535,6 +537,7 @@ export const codexExecJsonlAdapter = {
|
|
|
535
537
|
displayName: "Codex exec JSONL",
|
|
536
538
|
description: "Normalizes a local JSONL stream produced by Codex exec --json into AgentTrace without launching Codex or calling provider APIs.",
|
|
537
539
|
supportedSource: "codex-exec-jsonl",
|
|
540
|
+
readiness: "Level 1 (local export import) · validated end-to-end on a synthetic fixture; real-artifact validation OPEN (a real `codex exec --json` export has not yet been walked)",
|
|
538
541
|
limitations: [
|
|
539
542
|
"Only local user-supplied JSONL files are supported.",
|
|
540
543
|
"This adapter does not launch Codex, call OpenAI APIs, scrape private data, or upload artifacts.",
|
|
@@ -564,6 +567,7 @@ export const unknownAdapter = {
|
|
|
564
567
|
displayName: "Unknown local trace source",
|
|
565
568
|
description: "Detects supported local AgentTrace and simple messages shapes before normalizing with the matching adapter.",
|
|
566
569
|
supportedSource: "unknown",
|
|
570
|
+
readiness: "Level 1 (local import) · best-effort shape detection (AgentTrace / messages / Codex JSONL)",
|
|
567
571
|
limitations: [
|
|
568
572
|
"Detection is shape-based and local-file only.",
|
|
569
573
|
"AgentTrace, simple messages, and Codex exec JSONL shapes are detected in v0.",
|
package/dist/core/types.d.ts
CHANGED
|
@@ -225,6 +225,61 @@ export interface CompactionReport {
|
|
|
225
225
|
* price-table), NOT billing-confirmed and NOT realized savings.
|
|
226
226
|
*/
|
|
227
227
|
spend_by_source?: SpendBySourceSummary;
|
|
228
|
+
/**
|
|
229
|
+
* Content-addressed per-run distinctness fingerprint of the source trace (additive,
|
|
230
|
+
* OPTIONAL, backward-compatible). When present, it lets the aggregate attribute this
|
|
231
|
+
* run's savings to a verifiable run identity and de-duplicate a run captured/aggregated
|
|
232
|
+
* twice (same fingerprint → counted once) so the headline savings are never inflated.
|
|
233
|
+
*
|
|
234
|
+
* Older reports omit this field; a run WITHOUT a fingerprint cannot be de-duplicated and
|
|
235
|
+
* is treated as distinct (you cannot dedup what you cannot identify) and noted honestly.
|
|
236
|
+
*
|
|
237
|
+
* Privacy: this is a one-way SHA-256 digest plus non-sensitive structural counts — NO
|
|
238
|
+
* message/prompt/tool-output text. Structurally matches `TraceFingerprint` from
|
|
239
|
+
* `src/core/trace-fingerprint.ts` (kept inline here so `types.ts` stays import-free).
|
|
240
|
+
* This is an attribution/integrity signal, NOT a billing, provider, or semantic claim.
|
|
241
|
+
*/
|
|
242
|
+
trace_fingerprint?: {
|
|
243
|
+
/** Algorithm + canonicalization version (e.g. `sha256-canonical-content-v1`). */
|
|
244
|
+
algorithm: string;
|
|
245
|
+
/** Hex SHA-256 digest over the canonical normalized trace content (one-way; never raw content). */
|
|
246
|
+
content_sha256: string;
|
|
247
|
+
/** Non-sensitive structural count: number of messages hashed. */
|
|
248
|
+
message_count: number;
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* Composed per-run SAVINGS EVIDENCE record (additive, OPTIONAL, backward-compatible) —
|
|
252
|
+
* the V0.2 "evidence labels per result" surface. Older reports omit it. See
|
|
253
|
+
* `SavingsEvidence`. Composes EXISTING report numbers; makes no new/stronger claim.
|
|
254
|
+
*/
|
|
255
|
+
savings_evidence?: SavingsEvidence;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Composed, per-run savings-evidence record (V0.2 "evidence labels per result"). Ties the
|
|
259
|
+
* evidence chain into ONE place and states, per run, the cost-source label, the
|
|
260
|
+
* recoverability/semantic status, and exactly which `claims-and-evidence-ladder` rung the
|
|
261
|
+
* figure stands on — so no consumer reads a stronger claim than the evidence supports.
|
|
262
|
+
*
|
|
263
|
+
* A single run is rung 1 (local estimate). The measured rung 1.5
|
|
264
|
+
* (`measured_caveated_estimate_delta`) is produced ONLY by the aggregate increment across
|
|
265
|
+
* N≥3 distinct runs, NEVER here. `billing_confirmed` is ALWAYS false; `semantic_preservation`
|
|
266
|
+
* is ALWAYS `not_evaluated` (never auto-certified).
|
|
267
|
+
*/
|
|
268
|
+
export interface SavingsEvidence {
|
|
269
|
+
/** Is the run attributable to a verifiable identity (trace fingerprint present)? */
|
|
270
|
+
trace_identity: "fingerprinted" | "unidentified";
|
|
271
|
+
/** Honest cost-source label for this run's figures (mirrors `CostSource`). */
|
|
272
|
+
cost_source: "provider_reported" | "price_table_estimate" | "local_estimate" | "missing" | "unknown";
|
|
273
|
+
/** Deterministic recoverability status when an eval ran (`compact --eval`); else `not_evaluated`. */
|
|
274
|
+
recoverability: "passed" | "failed" | "not_computed" | "not_evaluated";
|
|
275
|
+
/** Semantic / meaning preservation is NEVER auto-certified — always `not_evaluated`. */
|
|
276
|
+
semantic_preservation: "not_evaluated";
|
|
277
|
+
/** Which claims-and-evidence-ladder rung this single-run figure stands on. */
|
|
278
|
+
evidence_rung: "local_estimate_single_run";
|
|
279
|
+
/** No v0 per-run figure is billing-confirmed. Always `false`. */
|
|
280
|
+
billing_confirmed: false;
|
|
281
|
+
/** One-line honest label (never asserts a stronger claim than the rung). */
|
|
282
|
+
label: string;
|
|
228
283
|
}
|
|
229
284
|
export interface CompactionPolicy {
|
|
230
285
|
policy_name: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@compaction/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -79,10 +79,14 @@
|
|
|
79
79
|
"dependencies": {
|
|
80
80
|
"chalk": "^5.4.1",
|
|
81
81
|
"commander": "^12.1.0",
|
|
82
|
+
"ink": "^5.2.1",
|
|
83
|
+
"react": "^18.3.1",
|
|
82
84
|
"zod": "^3.24.1"
|
|
83
85
|
},
|
|
84
86
|
"devDependencies": {
|
|
85
87
|
"@types/node": "^22.10.2",
|
|
88
|
+
"@types/react": "^18.3.31",
|
|
89
|
+
"ink-testing-library": "^4.0.0",
|
|
86
90
|
"tsx": "^4.19.2",
|
|
87
91
|
"typescript": "^5.7.2",
|
|
88
92
|
"vitest": "^4.1.8"
|