@compaction/cli 0.1.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/LICENSE +663 -0
- package/README.md +319 -0
- package/dist/cli/commands/adapter.d.ts +2 -0
- package/dist/cli/commands/adapter.js +49 -0
- package/dist/cli/commands/aggregate.d.ts +2 -0
- package/dist/cli/commands/aggregate.js +60 -0
- package/dist/cli/commands/analyze.d.ts +2 -0
- package/dist/cli/commands/analyze.js +55 -0
- package/dist/cli/commands/apply-context.d.ts +12 -0
- package/dist/cli/commands/apply-context.js +80 -0
- package/dist/cli/commands/apply.d.ts +2 -0
- package/dist/cli/commands/apply.js +65 -0
- package/dist/cli/commands/approve.d.ts +2 -0
- package/dist/cli/commands/approve.js +55 -0
- package/dist/cli/commands/audit.d.ts +2 -0
- package/dist/cli/commands/audit.js +63 -0
- package/dist/cli/commands/billing-delta.d.ts +2 -0
- package/dist/cli/commands/billing-delta.js +246 -0
- package/dist/cli/commands/capture-claude-code.d.ts +19 -0
- package/dist/cli/commands/capture-claude-code.js +198 -0
- package/dist/cli/commands/capture-provider-usage.d.ts +35 -0
- package/dist/cli/commands/capture-provider-usage.js +113 -0
- package/dist/cli/commands/capture.d.ts +2 -0
- package/dist/cli/commands/capture.js +76 -0
- package/dist/cli/commands/compact.d.ts +2 -0
- package/dist/cli/commands/compact.js +206 -0
- package/dist/cli/commands/eval.d.ts +2 -0
- package/dist/cli/commands/eval.js +66 -0
- package/dist/cli/commands/export-session-seed.d.ts +11 -0
- package/dist/cli/commands/export-session-seed.js +42 -0
- package/dist/cli/commands/feedback.d.ts +2 -0
- package/dist/cli/commands/feedback.js +233 -0
- package/dist/cli/commands/import.d.ts +2 -0
- package/dist/cli/commands/import.js +60 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.js +216 -0
- package/dist/cli/commands/integrations.d.ts +2 -0
- package/dist/cli/commands/integrations.js +29 -0
- package/dist/cli/commands/launch-treatment-session.d.ts +14 -0
- package/dist/cli/commands/launch-treatment-session.js +50 -0
- package/dist/cli/commands/optimize.d.ts +2 -0
- package/dist/cli/commands/optimize.js +33 -0
- package/dist/cli/commands/recommend.d.ts +4 -0
- package/dist/cli/commands/recommend.js +66 -0
- package/dist/cli/commands/review.d.ts +2 -0
- package/dist/cli/commands/review.js +60 -0
- package/dist/cli/commands/run.d.ts +2 -0
- package/dist/cli/commands/run.js +49 -0
- package/dist/cli/commands/spend.d.ts +2 -0
- package/dist/cli/commands/spend.js +24 -0
- package/dist/cli/commands/summary.d.ts +2 -0
- package/dist/cli/commands/summary.js +19 -0
- package/dist/cli/engine-degrade.d.ts +40 -0
- package/dist/cli/engine-degrade.js +59 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +59 -0
- package/dist/core/adapter-validation.d.ts +32 -0
- package/dist/core/adapter-validation.js +185 -0
- package/dist/core/adapters/claude-code-adapter.d.ts +51 -0
- package/dist/core/adapters/claude-code-adapter.js +539 -0
- package/dist/core/adapters/claude-code-discovery.d.ts +40 -0
- package/dist/core/adapters/claude-code-discovery.js +100 -0
- package/dist/core/aggregate-format.d.ts +62 -0
- package/dist/core/aggregate-format.js +349 -0
- package/dist/core/api-client/client.d.ts +43 -0
- package/dist/core/api-client/client.js +143 -0
- package/dist/core/api-client/config.d.ts +36 -0
- package/dist/core/api-client/config.js +44 -0
- package/dist/core/api-client/index.d.ts +13 -0
- package/dist/core/api-client/index.js +14 -0
- package/dist/core/api-client/payload.d.ts +93 -0
- package/dist/core/api-client/payload.js +180 -0
- package/dist/core/api-client/types.d.ts +95 -0
- package/dist/core/api-client/types.js +21 -0
- package/dist/core/artifact-writer.d.ts +2 -0
- package/dist/core/artifact-writer.js +14 -0
- package/dist/core/audit-report.d.ts +91 -0
- package/dist/core/audit-report.js +422 -0
- package/dist/core/billing-delta/billing-delta-record.d.ts +290 -0
- package/dist/core/billing-delta/billing-delta-record.js +394 -0
- package/dist/core/capture-adapter.d.ts +74 -0
- package/dist/core/capture-adapter.js +2 -0
- package/dist/core/command-runner.d.ts +29 -0
- package/dist/core/command-runner.js +96 -0
- package/dist/core/content-hash.d.ts +1 -0
- package/dist/core/content-hash.js +5 -0
- package/dist/core/cost-calculator.d.ts +6 -0
- package/dist/core/cost-calculator.js +20 -0
- package/dist/core/feedback-bundle.d.ts +159 -0
- package/dist/core/feedback-bundle.js +406 -0
- package/dist/core/openai-agents-capture.d.ts +66 -0
- package/dist/core/openai-agents-capture.js +475 -0
- package/dist/core/policy-types.d.ts +85 -0
- package/dist/core/policy-types.js +29 -0
- package/dist/core/pricing.d.ts +23 -0
- package/dist/core/pricing.js +55 -0
- package/dist/core/provider-usage/credential-redaction.d.ts +23 -0
- package/dist/core/provider-usage/credential-redaction.js +60 -0
- package/dist/core/provider-usage/provider-usage-adapter.d.ts +65 -0
- package/dist/core/provider-usage/provider-usage-adapter.js +147 -0
- package/dist/core/provider-usage/provider-usage-client.d.ts +89 -0
- package/dist/core/provider-usage/provider-usage-client.js +96 -0
- package/dist/core/report-generator.d.ts +7 -0
- package/dist/core/report-generator.js +275 -0
- package/dist/core/run-aggregator.d.ts +50 -0
- package/dist/core/run-aggregator.js +251 -0
- package/dist/core/run-labels.d.ts +52 -0
- package/dist/core/run-labels.js +80 -0
- package/dist/core/run-trace-converter.d.ts +3 -0
- package/dist/core/run-trace-converter.js +94 -0
- package/dist/core/safety-report.d.ts +52 -0
- package/dist/core/safety-report.js +440 -0
- package/dist/core/savings.d.ts +22 -0
- package/dist/core/savings.js +36 -0
- package/dist/core/session-aggregate.d.ts +211 -0
- package/dist/core/session-aggregate.js +490 -0
- package/dist/core/skill-injection-detector.d.ts +51 -0
- package/dist/core/skill-injection-detector.js +146 -0
- package/dist/core/spend-attribution.d.ts +169 -0
- package/dist/core/spend-attribution.js +611 -0
- package/dist/core/standalone-capsule-provenance.d.ts +4 -0
- package/dist/core/standalone-capsule-provenance.js +62 -0
- package/dist/core/state-capsule.d.ts +3 -0
- package/dist/core/state-capsule.js +147 -0
- package/dist/core/token-accounting.d.ts +62 -0
- package/dist/core/token-accounting.js +81 -0
- package/dist/core/token-estimator.d.ts +3 -0
- package/dist/core/token-estimator.js +18 -0
- package/dist/core/trace-adapters.d.ts +53 -0
- package/dist/core/trace-adapters.js +714 -0
- package/dist/core/trace-fingerprint.d.ts +79 -0
- package/dist/core/trace-fingerprint.js +102 -0
- package/dist/core/trace-intake.d.ts +41 -0
- package/dist/core/trace-intake.js +164 -0
- package/dist/core/trace-parser.d.ts +203 -0
- package/dist/core/trace-parser.js +143 -0
- package/dist/core/types.d.ts +238 -0
- package/dist/core/types.js +2 -0
- package/dist/core/usage-metadata.d.ts +51 -0
- package/dist/core/usage-metadata.js +122 -0
- package/dist/core/waste-detector.d.ts +8 -0
- package/dist/core/waste-detector.js +58 -0
- package/package.json +90 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const CURRENT_AGENT_TRACE_ARTIFACT_VERSION = "agent-trace-v1";
|
|
4
|
+
const isoTimestampSchema = z.string().datetime({ offset: true });
|
|
5
|
+
const traceMessageSchema = z.object({
|
|
6
|
+
id: z.string().min(1),
|
|
7
|
+
role: z.enum(["system", "user", "assistant", "tool", "stdout", "stderr"]),
|
|
8
|
+
content: z.string(),
|
|
9
|
+
timestamp: isoTimestampSchema,
|
|
10
|
+
toolName: z.string().min(1).optional(),
|
|
11
|
+
metadata: z.record(z.unknown()).optional()
|
|
12
|
+
});
|
|
13
|
+
const commandMetadataSchema = z.object({
|
|
14
|
+
command: z.string().min(1),
|
|
15
|
+
args: z.array(z.string()),
|
|
16
|
+
cwd: z.string().min(1).optional(),
|
|
17
|
+
shell: z.string().min(1).optional()
|
|
18
|
+
});
|
|
19
|
+
// `codex_import` is deliberately NOT a command trace: an imported `codex exec --json`
|
|
20
|
+
// JSONL export carries no command/exitCode/durationMs provenance, so it must not be
|
|
21
|
+
// subjected to command-trace validation (that is why the Codex import path could not
|
|
22
|
+
// reuse `cli_wrapper`/`local_command`). Keep this exclusion in lockstep with the
|
|
23
|
+
// `imported_local` evidence mapping in safety-report.ts.
|
|
24
|
+
function isCommandTrace(source) {
|
|
25
|
+
return source === "cli_wrapper" || source === "local_command";
|
|
26
|
+
}
|
|
27
|
+
const rawAgentTraceSchema = z
|
|
28
|
+
.object({
|
|
29
|
+
id: z.string().min(1),
|
|
30
|
+
title: z.string().min(1),
|
|
31
|
+
artifactVersion: z.string().min(1).optional(),
|
|
32
|
+
artifact_version: z.string().min(1).optional(),
|
|
33
|
+
source: z.enum(["manual", "cli_wrapper", "local_command", "demo", "real_captured", "provider_usage", "codex_import"]).optional(),
|
|
34
|
+
createdAt: isoTimestampSchema.optional(),
|
|
35
|
+
created_at: isoTimestampSchema.optional(),
|
|
36
|
+
generatedAt: isoTimestampSchema.optional(),
|
|
37
|
+
generated_at: isoTimestampSchema.optional(),
|
|
38
|
+
model: z.string().min(1),
|
|
39
|
+
command: commandMetadataSchema.optional(),
|
|
40
|
+
durationMs: z.number().int().nonnegative().optional(),
|
|
41
|
+
duration_ms: z.number().int().nonnegative().optional(),
|
|
42
|
+
exitCode: z.number().int().min(0).max(255).optional(),
|
|
43
|
+
exit_code: z.number().int().min(0).max(255).optional(),
|
|
44
|
+
messages: z.array(traceMessageSchema)
|
|
45
|
+
})
|
|
46
|
+
.superRefine((trace, context) => {
|
|
47
|
+
const createdAt = trace.createdAt ?? trace.created_at;
|
|
48
|
+
const generatedAt = trace.generatedAt ?? trace.generated_at;
|
|
49
|
+
if (!createdAt) {
|
|
50
|
+
context.addIssue({
|
|
51
|
+
code: z.ZodIssueCode.custom,
|
|
52
|
+
message: "Trace must include createdAt or created_at.",
|
|
53
|
+
path: ["createdAt"]
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (trace.createdAt && trace.created_at && trace.createdAt !== trace.created_at) {
|
|
57
|
+
context.addIssue({
|
|
58
|
+
code: z.ZodIssueCode.custom,
|
|
59
|
+
message: "createdAt and created_at must match when both are present.",
|
|
60
|
+
path: ["created_at"]
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (trace.generatedAt && trace.generated_at && trace.generatedAt !== trace.generated_at) {
|
|
64
|
+
context.addIssue({
|
|
65
|
+
code: z.ZodIssueCode.custom,
|
|
66
|
+
message: "generatedAt and generated_at must match when both are present.",
|
|
67
|
+
path: ["generated_at"]
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (trace.artifactVersion && trace.artifact_version && trace.artifactVersion !== trace.artifact_version) {
|
|
71
|
+
context.addIssue({
|
|
72
|
+
code: z.ZodIssueCode.custom,
|
|
73
|
+
message: "artifactVersion and artifact_version must match when both are present.",
|
|
74
|
+
path: ["artifact_version"]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (trace.durationMs !== undefined && trace.duration_ms !== undefined && trace.durationMs !== trace.duration_ms) {
|
|
78
|
+
context.addIssue({
|
|
79
|
+
code: z.ZodIssueCode.custom,
|
|
80
|
+
message: "durationMs and duration_ms must match when both are present.",
|
|
81
|
+
path: ["duration_ms"]
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (trace.exitCode !== undefined && trace.exit_code !== undefined && trace.exitCode !== trace.exit_code) {
|
|
85
|
+
context.addIssue({
|
|
86
|
+
code: z.ZodIssueCode.custom,
|
|
87
|
+
message: "exitCode and exit_code must match when both are present.",
|
|
88
|
+
path: ["exit_code"]
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (isCommandTrace(trace.source) && !trace.command) {
|
|
92
|
+
context.addIssue({
|
|
93
|
+
code: z.ZodIssueCode.custom,
|
|
94
|
+
message: "Command traces must include command metadata.",
|
|
95
|
+
path: ["command"]
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (isCommandTrace(trace.source) && trace.exitCode === undefined && trace.exit_code === undefined) {
|
|
99
|
+
context.addIssue({
|
|
100
|
+
code: z.ZodIssueCode.custom,
|
|
101
|
+
message: "Command traces must include exitCode or exit_code.",
|
|
102
|
+
path: ["exitCode"]
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (isCommandTrace(trace.source) && trace.durationMs === undefined && trace.duration_ms === undefined) {
|
|
106
|
+
context.addIssue({
|
|
107
|
+
code: z.ZodIssueCode.custom,
|
|
108
|
+
message: "Command traces must include durationMs or duration_ms.",
|
|
109
|
+
path: ["durationMs"]
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (generatedAt && createdAt && new Date(generatedAt).getTime() < new Date(createdAt).getTime()) {
|
|
113
|
+
context.addIssue({
|
|
114
|
+
code: z.ZodIssueCode.custom,
|
|
115
|
+
message: "generatedAt must not be earlier than createdAt.",
|
|
116
|
+
path: ["generatedAt"]
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
export const agentTraceSchema = rawAgentTraceSchema.transform((trace) => {
|
|
121
|
+
const createdAt = trace.createdAt ?? trace.created_at;
|
|
122
|
+
if (!createdAt) {
|
|
123
|
+
throw new Error("Trace must include createdAt or created_at.");
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
id: trace.id,
|
|
127
|
+
title: trace.title,
|
|
128
|
+
artifactVersion: trace.artifactVersion ?? trace.artifact_version ?? CURRENT_AGENT_TRACE_ARTIFACT_VERSION,
|
|
129
|
+
source: trace.source ?? "manual",
|
|
130
|
+
createdAt,
|
|
131
|
+
generatedAt: trace.generatedAt ?? trace.generated_at ?? createdAt,
|
|
132
|
+
model: trace.model,
|
|
133
|
+
command: trace.command,
|
|
134
|
+
durationMs: trace.durationMs ?? trace.duration_ms,
|
|
135
|
+
exitCode: trace.exitCode ?? trace.exit_code,
|
|
136
|
+
messages: trace.messages
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
export async function parseTraceFile(traceFile) {
|
|
140
|
+
const rawTrace = await readFile(traceFile, "utf8");
|
|
141
|
+
return agentTraceSchema.parse(JSON.parse(rawTrace));
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=trace-parser.js.map
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
export type TraceRole = "system" | "user" | "assistant" | "tool" | "stdout" | "stderr";
|
|
2
|
+
export type TraceSource = "manual" | "cli_wrapper" | "local_command" | "demo" | "real_captured" | "provider_usage" | "codex_import";
|
|
3
|
+
export interface TraceCommandMetadata {
|
|
4
|
+
command: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
cwd?: string;
|
|
7
|
+
shell?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TraceMessage {
|
|
10
|
+
id: string;
|
|
11
|
+
role: TraceRole;
|
|
12
|
+
content: string;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
toolName?: string;
|
|
15
|
+
metadata?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export interface AgentTrace {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
artifactVersion: string;
|
|
21
|
+
source: TraceSource;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
generatedAt: string;
|
|
24
|
+
model: string;
|
|
25
|
+
command?: TraceCommandMetadata;
|
|
26
|
+
durationMs?: number;
|
|
27
|
+
exitCode?: number;
|
|
28
|
+
messages: TraceMessage[];
|
|
29
|
+
}
|
|
30
|
+
export interface TokenEstimate {
|
|
31
|
+
inputTokens: number;
|
|
32
|
+
outputTokens: number;
|
|
33
|
+
totalTokens: number;
|
|
34
|
+
}
|
|
35
|
+
export interface CostEstimate {
|
|
36
|
+
model: string;
|
|
37
|
+
inputCostUsd: number;
|
|
38
|
+
outputCostUsd: number;
|
|
39
|
+
/** Present only when cache_read_input_tokens > 0 and priced at ~10% of standard input rate (estimated). */
|
|
40
|
+
cacheReadCostUsd?: number;
|
|
41
|
+
/** Present only when cache_creation_input_tokens > 0 and priced at ~125% of standard input rate (estimated). */
|
|
42
|
+
cacheCreationCostUsd?: number;
|
|
43
|
+
totalCostUsd: number;
|
|
44
|
+
}
|
|
45
|
+
export interface WasteFinding {
|
|
46
|
+
category: "repeated_tool_output" | "stale_context" | "verbose_observation" | "repeated_skill_injection";
|
|
47
|
+
messageIds: string[];
|
|
48
|
+
summary: string;
|
|
49
|
+
estimatedTokens: number;
|
|
50
|
+
}
|
|
51
|
+
export interface StateCapsuleProvenanceEntry {
|
|
52
|
+
source_trace_id?: string;
|
|
53
|
+
source_message_id?: string;
|
|
54
|
+
source_pointer?: string;
|
|
55
|
+
source_hash?: string;
|
|
56
|
+
source_hash_algorithm?: "sha256";
|
|
57
|
+
source_excerpt_preview?: string;
|
|
58
|
+
source_recoverable: boolean | "unknown";
|
|
59
|
+
source_recovery_path?: string;
|
|
60
|
+
original_payload_token_count?: number;
|
|
61
|
+
capsule_token_count?: number;
|
|
62
|
+
compacted_message_ids?: string[];
|
|
63
|
+
replacement_mode?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface StateCapsule {
|
|
66
|
+
traceId: string;
|
|
67
|
+
source_trace_id?: string;
|
|
68
|
+
source_message_id?: string;
|
|
69
|
+
source_pointer?: string;
|
|
70
|
+
source_hash?: string;
|
|
71
|
+
source_hash_algorithm?: "sha256";
|
|
72
|
+
source_excerpt_preview?: string;
|
|
73
|
+
source_recoverable?: boolean | "unknown";
|
|
74
|
+
source_recovery_path?: string;
|
|
75
|
+
original_payload_token_count?: number;
|
|
76
|
+
capsule_token_count?: number;
|
|
77
|
+
preserved_commitments?: string[];
|
|
78
|
+
omitted_or_unknown_commitments?: string[];
|
|
79
|
+
unsupported_additions_check?: string;
|
|
80
|
+
provenance_limitations?: string[];
|
|
81
|
+
provenance_entries?: StateCapsuleProvenanceEntry[];
|
|
82
|
+
retainedFacts: string[];
|
|
83
|
+
openQuestions: string[];
|
|
84
|
+
safetyNotes: string[];
|
|
85
|
+
}
|
|
86
|
+
export type ApprovalReadinessStatus = "not_ready" | "conditional" | "ready";
|
|
87
|
+
/**
|
|
88
|
+
* Report-only advisory for repeated byte-identical, same-skill role:user skill injections.
|
|
89
|
+
* Detection/reporting ONLY: nothing in this advisory is compacted, removed, or applied.
|
|
90
|
+
* All token figures are estimated (chars/4), not billing-confirmed, not realized savings.
|
|
91
|
+
*/
|
|
92
|
+
export interface SkillInjectionAdvisorySummary {
|
|
93
|
+
report_only: true;
|
|
94
|
+
skills: {
|
|
95
|
+
skill_name: string;
|
|
96
|
+
redundant_byte_identical_copies: number;
|
|
97
|
+
estimated_tokens: number;
|
|
98
|
+
redundant_copy_message_ids: string[];
|
|
99
|
+
}[];
|
|
100
|
+
total_redundant_byte_identical_copies: number;
|
|
101
|
+
total_addressable_estimated_tokens: number;
|
|
102
|
+
/** Price-table est cost equivalent of the addressable token volume (advisory, not realized). */
|
|
103
|
+
estimated_cost_equivalent_usd: number;
|
|
104
|
+
first_copy_count: number;
|
|
105
|
+
estimate_label: string;
|
|
106
|
+
recommendation: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Value-proof classification of the report's token/cost deltas against the four
|
|
110
|
+
* ROADMAP "MVP value path" claims (strongest last, never conflated). This object
|
|
111
|
+
* does NOT introduce new numbers — it classifies the already-computed
|
|
112
|
+
* tokens_saved / cost deltas into their honest tier and states plainly which
|
|
113
|
+
* stronger claims are NOT made by this artifact.
|
|
114
|
+
*
|
|
115
|
+
* - Tier 1 (trace-token reduction): the local-estimate token reduction. CLAIMED.
|
|
116
|
+
* - Tier 2 (estimated provider cost reduction): price-table estimate from the token
|
|
117
|
+
* delta, NOT billing-confirmed. CLAIMED as an estimate only.
|
|
118
|
+
* - Tier 3 (billing-confirmed savings): a measured provider usage/billing delta.
|
|
119
|
+
* NOT claimed here — no provider usage/billing delta is measured.
|
|
120
|
+
* - Tier 4 (fixed-plan workflow-extension value): more work under a fixed-price cap.
|
|
121
|
+
* NOT quantified here.
|
|
122
|
+
*/
|
|
123
|
+
export interface ValueProof {
|
|
124
|
+
/** Tier 1 — fewer tokens in the persisted/compacted trace (local estimate). */
|
|
125
|
+
trace_token_reduction: {
|
|
126
|
+
claim: "trace-token reduction (estimated, local heuristic)";
|
|
127
|
+
tier: 1;
|
|
128
|
+
estimated: true;
|
|
129
|
+
tokens_saved: number;
|
|
130
|
+
percent_reduction: number;
|
|
131
|
+
};
|
|
132
|
+
/** Tier 2 — price-table estimate from the token delta, NOT billing-confirmed. */
|
|
133
|
+
estimated_provider_cost_reduction: {
|
|
134
|
+
claim: "estimated provider cost reduction (price-table estimate, NOT billing-confirmed)";
|
|
135
|
+
tier: 2;
|
|
136
|
+
estimated: true;
|
|
137
|
+
billing_confirmed: false;
|
|
138
|
+
saving_per_run_usd: number;
|
|
139
|
+
};
|
|
140
|
+
/** Tier 3 — measured provider usage/billing delta. NOT claimed here. */
|
|
141
|
+
billing_confirmed_savings: {
|
|
142
|
+
claim: "billing-confirmed savings (measured provider usage/billing delta)";
|
|
143
|
+
tier: 3;
|
|
144
|
+
claimed: false;
|
|
145
|
+
billing_confirmed: false;
|
|
146
|
+
note: string;
|
|
147
|
+
};
|
|
148
|
+
/** Tier 4 — more useful work under a fixed-price cap. NOT quantified here. */
|
|
149
|
+
fixed_plan_workflow_extension_value: {
|
|
150
|
+
claim: "fixed-plan workflow-extension value";
|
|
151
|
+
tier: 4;
|
|
152
|
+
quantified: false;
|
|
153
|
+
note: string;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Concise "where spend came from" summary surfaced in the compaction report
|
|
158
|
+
* (additive, optional, backward-compatible). Built by REUSING the spend-attribution
|
|
159
|
+
* computation (buildSpendSummaryFromTrace) — it does not reimplement attribution.
|
|
160
|
+
*
|
|
161
|
+
* All figures are LOCAL ESTIMATES: tokens are a chars/4 trace-token heuristic and
|
|
162
|
+
* costs are price-table estimates. These describe WHERE estimated context spend
|
|
163
|
+
* comes from. They are NOT billing-confirmed and NOT realized/applied savings.
|
|
164
|
+
*/
|
|
165
|
+
export interface SpendBySourceSummary {
|
|
166
|
+
estimated: true;
|
|
167
|
+
billing_confirmed: false;
|
|
168
|
+
/** Human label clarifying the estimate basis. */
|
|
169
|
+
estimate_label: string;
|
|
170
|
+
/** Top role/category buckets by estimated tokens. */
|
|
171
|
+
top_roles: {
|
|
172
|
+
name: string;
|
|
173
|
+
estimated_tokens: number;
|
|
174
|
+
estimated_cost_usd: number;
|
|
175
|
+
}[];
|
|
176
|
+
/** Top repeated tool-output buckets by estimated tokens. */
|
|
177
|
+
top_tool_outputs: {
|
|
178
|
+
tool_name: string;
|
|
179
|
+
estimated_tokens: number;
|
|
180
|
+
repeated_count: number;
|
|
181
|
+
estimated_cost_usd: number;
|
|
182
|
+
}[];
|
|
183
|
+
/** Top policy candidate by estimated tokens saved, if any (null when none). */
|
|
184
|
+
top_policy_candidate: {
|
|
185
|
+
policy_name: string;
|
|
186
|
+
estimated_tokens_saved: number;
|
|
187
|
+
estimated_saving_per_run_usd: number;
|
|
188
|
+
} | null;
|
|
189
|
+
}
|
|
190
|
+
export interface CompactionReport {
|
|
191
|
+
run_id: string;
|
|
192
|
+
trace_title: string;
|
|
193
|
+
model: string;
|
|
194
|
+
original_input_tokens: number;
|
|
195
|
+
compacted_input_tokens: number;
|
|
196
|
+
tokens_saved: number;
|
|
197
|
+
percent_reduction: number;
|
|
198
|
+
savings_scope?: string;
|
|
199
|
+
cost_before_per_run: number;
|
|
200
|
+
cost_after_per_run: number;
|
|
201
|
+
saving_per_run: number;
|
|
202
|
+
policy_name: string;
|
|
203
|
+
waste_pattern: WasteFinding["category"] | null;
|
|
204
|
+
source_message_id: string | null;
|
|
205
|
+
repeated_count: number;
|
|
206
|
+
compacted_message_ids: string[];
|
|
207
|
+
artifact_version: string;
|
|
208
|
+
created_at: string;
|
|
209
|
+
generated_at: string;
|
|
210
|
+
approval_readiness_status: ApprovalReadinessStatus;
|
|
211
|
+
approval_readiness_reason: string;
|
|
212
|
+
/**
|
|
213
|
+
* Value-proof tier classification of the token/cost deltas above (additive,
|
|
214
|
+
* backward-compatible). Classifies existing numbers only; makes no new claim.
|
|
215
|
+
*/
|
|
216
|
+
value_proof?: ValueProof;
|
|
217
|
+
/**
|
|
218
|
+
* Report-only advisory section (repeated_skill_injection). Distinct from the actual
|
|
219
|
+
* compaction summary above. Detection/reporting only — nothing here is compacted.
|
|
220
|
+
*/
|
|
221
|
+
skill_injection_advisory?: SkillInjectionAdvisorySummary;
|
|
222
|
+
/**
|
|
223
|
+
* Concise "where spend came from" summary (additive, optional, backward-compatible).
|
|
224
|
+
* Reuses the spend-attribution computation. Figures are local estimates (chars/4 /
|
|
225
|
+
* price-table), NOT billing-confirmed and NOT realized savings.
|
|
226
|
+
*/
|
|
227
|
+
spend_by_source?: SpendBySourceSummary;
|
|
228
|
+
}
|
|
229
|
+
export interface CompactionPolicy {
|
|
230
|
+
policy_name: string;
|
|
231
|
+
policy_version: string;
|
|
232
|
+
trigger: string;
|
|
233
|
+
condition: string;
|
|
234
|
+
action: string;
|
|
235
|
+
safety_guarantees: string[];
|
|
236
|
+
expected_savings: string;
|
|
237
|
+
risk_notes: string[];
|
|
238
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AgentTrace } from "./types.js";
|
|
2
|
+
export type CostSource = "provider_reported" | "price_table_estimate" | "local_estimate" | "missing" | "unknown";
|
|
3
|
+
export type CostConfidence = "high" | "medium" | "low" | "unknown";
|
|
4
|
+
export interface UsageMetadata {
|
|
5
|
+
input_tokens?: number;
|
|
6
|
+
output_tokens?: number;
|
|
7
|
+
total_tokens?: number;
|
|
8
|
+
/** Cache read tokens (priced at ~10% of standard input rate — estimated, not billing-confirmed). */
|
|
9
|
+
cache_read_input_tokens?: number;
|
|
10
|
+
/** Cache creation tokens (priced at ~125% of standard input rate — estimated, not billing-confirmed). */
|
|
11
|
+
cache_creation_input_tokens?: number;
|
|
12
|
+
provider_reported_tokens: boolean;
|
|
13
|
+
estimated_tokens: boolean;
|
|
14
|
+
synthetic_demo?: boolean;
|
|
15
|
+
cost_source: CostSource;
|
|
16
|
+
cost_confidence: CostConfidence;
|
|
17
|
+
currency?: string;
|
|
18
|
+
model?: string;
|
|
19
|
+
provider?: string;
|
|
20
|
+
pricing_assumption?: string;
|
|
21
|
+
limitations: string[];
|
|
22
|
+
}
|
|
23
|
+
interface UsageMetadataInput {
|
|
24
|
+
inputTokens?: number;
|
|
25
|
+
outputTokens?: number;
|
|
26
|
+
totalTokens?: number;
|
|
27
|
+
/** Cache read tokens from the provider API response. Priced at reduced rate in cost estimates. */
|
|
28
|
+
cacheReadInputTokens?: number;
|
|
29
|
+
/** Cache creation tokens from the provider API response. Priced at elevated rate in cost estimates. */
|
|
30
|
+
cacheCreationInputTokens?: number;
|
|
31
|
+
providerReportedTokens: boolean;
|
|
32
|
+
estimatedTokens: boolean;
|
|
33
|
+
syntheticDemo?: boolean;
|
|
34
|
+
providerReportedCost?: boolean;
|
|
35
|
+
currency?: string;
|
|
36
|
+
model?: string;
|
|
37
|
+
provider?: string;
|
|
38
|
+
pricingAssumption?: string;
|
|
39
|
+
limitations?: string[];
|
|
40
|
+
}
|
|
41
|
+
export declare function createUsageMetadata(input: UsageMetadataInput): UsageMetadata;
|
|
42
|
+
export declare function missingUsageMetadata(input?: {
|
|
43
|
+
model?: string;
|
|
44
|
+
provider?: string;
|
|
45
|
+
limitations?: string[];
|
|
46
|
+
}): UsageMetadata;
|
|
47
|
+
export declare function localEstimateUsageMetadata(trace: AgentTrace, limitations?: string[]): UsageMetadata;
|
|
48
|
+
export declare function priceTableEstimateForUsage(metadata: UsageMetadata): number | null;
|
|
49
|
+
export declare function describeTokenMetadata(metadata: UsageMetadata): string[];
|
|
50
|
+
export declare function describeCostMetadata(metadata: UsageMetadata): string[];
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { calculateCost } from "./cost-calculator.js";
|
|
2
|
+
import { hasModelPricing } from "./pricing.js";
|
|
3
|
+
import { estimateTraceTokens } from "./token-estimator.js";
|
|
4
|
+
function cleanNumber(value) {
|
|
5
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
6
|
+
}
|
|
7
|
+
function costSourceFor(input) {
|
|
8
|
+
if (input.providerReportedCost)
|
|
9
|
+
return "provider_reported";
|
|
10
|
+
if (input.providerReportedTokens && input.model && hasModelPricing(input.model))
|
|
11
|
+
return "price_table_estimate";
|
|
12
|
+
if (input.estimatedTokens)
|
|
13
|
+
return "local_estimate";
|
|
14
|
+
if (!input.providerReportedTokens && !input.estimatedTokens)
|
|
15
|
+
return "missing";
|
|
16
|
+
return "unknown";
|
|
17
|
+
}
|
|
18
|
+
function costConfidenceFor(source, providerReportedTokens) {
|
|
19
|
+
if (source === "provider_reported")
|
|
20
|
+
return "high";
|
|
21
|
+
if (source === "price_table_estimate")
|
|
22
|
+
return providerReportedTokens ? "medium" : "low";
|
|
23
|
+
if (source === "local_estimate")
|
|
24
|
+
return "low";
|
|
25
|
+
return "unknown";
|
|
26
|
+
}
|
|
27
|
+
export function createUsageMetadata(input) {
|
|
28
|
+
const inputTokens = cleanNumber(input.inputTokens);
|
|
29
|
+
const outputTokens = cleanNumber(input.outputTokens);
|
|
30
|
+
const cacheReadInputTokens = cleanNumber(input.cacheReadInputTokens);
|
|
31
|
+
const cacheCreationInputTokens = cleanNumber(input.cacheCreationInputTokens);
|
|
32
|
+
const totalTokens = cleanNumber(input.totalTokens) ?? (inputTokens !== undefined || outputTokens !== undefined ? (inputTokens ?? 0) + (outputTokens ?? 0) : undefined);
|
|
33
|
+
const costSource = costSourceFor(input);
|
|
34
|
+
const pricingAssumption = input.pricingAssumption ??
|
|
35
|
+
(costSource === "price_table_estimate" && input.model ? `price table estimate for ${input.model}` : costSource === "local_estimate" && input.model ? `local token estimate priced with fallback table for ${input.model}` : undefined);
|
|
36
|
+
return {
|
|
37
|
+
...(inputTokens !== undefined ? { input_tokens: inputTokens } : {}),
|
|
38
|
+
...(outputTokens !== undefined ? { output_tokens: outputTokens } : {}),
|
|
39
|
+
...(totalTokens !== undefined ? { total_tokens: totalTokens } : {}),
|
|
40
|
+
...(cacheReadInputTokens !== undefined && cacheReadInputTokens > 0 ? { cache_read_input_tokens: cacheReadInputTokens } : {}),
|
|
41
|
+
...(cacheCreationInputTokens !== undefined && cacheCreationInputTokens > 0 ? { cache_creation_input_tokens: cacheCreationInputTokens } : {}),
|
|
42
|
+
provider_reported_tokens: input.providerReportedTokens,
|
|
43
|
+
estimated_tokens: input.estimatedTokens,
|
|
44
|
+
...(input.syntheticDemo ? { synthetic_demo: true } : {}),
|
|
45
|
+
cost_source: costSource,
|
|
46
|
+
cost_confidence: costConfidenceFor(costSource, input.providerReportedTokens),
|
|
47
|
+
...(input.currency ? { currency: input.currency } : {}),
|
|
48
|
+
...(input.model ? { model: input.model } : {}),
|
|
49
|
+
...(input.provider ? { provider: input.provider } : {}),
|
|
50
|
+
...(pricingAssumption ? { pricing_assumption: pricingAssumption } : {}),
|
|
51
|
+
limitations: [...(input.limitations ?? [])]
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function missingUsageMetadata(input = {}) {
|
|
55
|
+
return createUsageMetadata({
|
|
56
|
+
providerReportedTokens: false,
|
|
57
|
+
estimatedTokens: false,
|
|
58
|
+
model: input.model,
|
|
59
|
+
provider: input.provider,
|
|
60
|
+
limitations: input.limitations ?? ["Token metadata is missing/unknown and was not invented.", "Cost metadata is missing/unknown."]
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export function localEstimateUsageMetadata(trace, limitations = []) {
|
|
64
|
+
const estimate = estimateTraceTokens(trace);
|
|
65
|
+
return createUsageMetadata({
|
|
66
|
+
inputTokens: estimate.inputTokens,
|
|
67
|
+
outputTokens: estimate.outputTokens,
|
|
68
|
+
totalTokens: estimate.totalTokens,
|
|
69
|
+
providerReportedTokens: false,
|
|
70
|
+
estimatedTokens: true,
|
|
71
|
+
model: trace.model,
|
|
72
|
+
pricingAssumption: `local token estimate priced with fallback table for ${trace.model}`,
|
|
73
|
+
limitations: ["Tokens are estimated locally from trace text; provider billing accuracy is not implied.", ...limitations]
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
export function priceTableEstimateForUsage(metadata) {
|
|
77
|
+
if (metadata.input_tokens === undefined && metadata.output_tokens === undefined)
|
|
78
|
+
return null;
|
|
79
|
+
if (!metadata.model)
|
|
80
|
+
return null;
|
|
81
|
+
const cost = calculateCost(metadata.model, {
|
|
82
|
+
inputTokens: metadata.input_tokens ?? 0,
|
|
83
|
+
outputTokens: metadata.output_tokens ?? 0,
|
|
84
|
+
totalTokens: metadata.total_tokens ?? (metadata.input_tokens ?? 0) + (metadata.output_tokens ?? 0)
|
|
85
|
+
}, {
|
|
86
|
+
cacheReadTokens: metadata.cache_read_input_tokens,
|
|
87
|
+
cacheCreationTokens: metadata.cache_creation_input_tokens
|
|
88
|
+
});
|
|
89
|
+
return Number(cost.totalCostUsd.toFixed(6));
|
|
90
|
+
}
|
|
91
|
+
export function describeTokenMetadata(metadata) {
|
|
92
|
+
if (metadata.provider_reported_tokens) {
|
|
93
|
+
const prefix = metadata.synthetic_demo ? "synthetic-demo provider-reported" : "provider-reported";
|
|
94
|
+
const label = metadata.synthetic_demo ? " (not production billing data)" : "";
|
|
95
|
+
return [
|
|
96
|
+
`${prefix} input tokens: ${metadata.input_tokens ?? "unknown"}${label}`,
|
|
97
|
+
`${prefix} output tokens: ${metadata.output_tokens ?? "unknown"}${label}`,
|
|
98
|
+
`${prefix} total tokens: ${metadata.total_tokens ?? "unknown"}${label}`
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
if (metadata.estimated_tokens) {
|
|
102
|
+
return [
|
|
103
|
+
`estimated input tokens: ${metadata.input_tokens ?? "unknown"}`,
|
|
104
|
+
`estimated output tokens: ${metadata.output_tokens ?? "unknown"}`,
|
|
105
|
+
`estimated total tokens: ${metadata.total_tokens ?? "unknown"}`
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
return ["Token metadata: missing / unknown"];
|
|
109
|
+
}
|
|
110
|
+
export function describeCostMetadata(metadata) {
|
|
111
|
+
const missingCostLine = metadata.cost_source === "missing" || metadata.cost_source === "unknown" ? "Cost metadata: missing / unknown" : null;
|
|
112
|
+
const syntheticLine = metadata.synthetic_demo ? "usage metadata label: synthetic-demo (not production billing data)" : null;
|
|
113
|
+
return [
|
|
114
|
+
...(syntheticLine ? [syntheticLine] : []),
|
|
115
|
+
...(missingCostLine ? [missingCostLine] : []),
|
|
116
|
+
`cost source: ${metadata.cost_source}`,
|
|
117
|
+
`cost confidence: ${metadata.cost_confidence}`,
|
|
118
|
+
...(metadata.currency ? [`currency: ${metadata.currency}`] : []),
|
|
119
|
+
...(metadata.pricing_assumption ? [`pricing assumption: ${metadata.pricing_assumption}`] : [])
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=usage-metadata.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AgentTrace, WasteFinding } from "./types.js";
|
|
2
|
+
export { detectSkillInjectionRepetition, buildSkillInjectionAdvisory, isSkillInjection, parseSkillName, type SkillInjectionFinding, type SkillInjectionAdvisory, type SkillInjectionSkillAttribution } from "./skill-injection-detector.js";
|
|
3
|
+
export interface WasteDetectionThresholds {
|
|
4
|
+
minDuplicateTokens?: number;
|
|
5
|
+
minDuplicateCharacters?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function detectWaste(trace: AgentTrace, thresholds?: WasteDetectionThresholds): WasteFinding[];
|
|
8
|
+
export declare function getCompactedMessageIds(findings: WasteFinding[]): Set<string>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { estimateTextTokens } from "./token-estimator.js";
|
|
2
|
+
// Report-only skill-injection repetition detector. Re-exported here so callers have a
|
|
3
|
+
// single waste-detection entry point, but it builds its OWN dedup map (separate from the
|
|
4
|
+
// tool-output map below) and its `repeated_skill_injection` findings are intentionally
|
|
5
|
+
// NOT compacted — see getCompactedMessageIds.
|
|
6
|
+
export { detectSkillInjectionRepetition, buildSkillInjectionAdvisory, isSkillInjection, parseSkillName } from "./skill-injection-detector.js";
|
|
7
|
+
function normalizeContent(content) {
|
|
8
|
+
return content.trim().replace(/\s+/g, " ");
|
|
9
|
+
}
|
|
10
|
+
function describeTool(message) {
|
|
11
|
+
return message.toolName ? `${message.toolName} tool output` : "tool output";
|
|
12
|
+
}
|
|
13
|
+
export function detectWaste(trace, thresholds = {}) {
|
|
14
|
+
const findings = [];
|
|
15
|
+
const firstToolOutputBySignature = new Map();
|
|
16
|
+
for (const message of trace.messages) {
|
|
17
|
+
if (message.role !== "tool") {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const signature = `${message.toolName ?? "tool"}:${normalizeContent(message.content)}`;
|
|
21
|
+
const originalMessage = firstToolOutputBySignature.get(signature);
|
|
22
|
+
if (originalMessage) {
|
|
23
|
+
const estimatedTokens = estimateTextTokens(message.content);
|
|
24
|
+
if (estimatedTokens < (thresholds.minDuplicateTokens ?? 0) ||
|
|
25
|
+
message.content.length < (thresholds.minDuplicateCharacters ?? 0)) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
findings.push({
|
|
29
|
+
category: "repeated_tool_output",
|
|
30
|
+
messageIds: [originalMessage.id, message.id],
|
|
31
|
+
summary: `Repeated ${describeTool(message)}. Keep ${originalMessage.id} and compact ${message.id}.`,
|
|
32
|
+
estimatedTokens
|
|
33
|
+
});
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
firstToolOutputBySignature.set(signature, message);
|
|
37
|
+
}
|
|
38
|
+
return findings;
|
|
39
|
+
}
|
|
40
|
+
export function getCompactedMessageIds(findings) {
|
|
41
|
+
const compactedIds = new Set();
|
|
42
|
+
for (const finding of findings) {
|
|
43
|
+
// ONLY "repeated_tool_output" findings are ever compacted. This is the report-only
|
|
44
|
+
// safety boundary for "repeated_skill_injection": such findings are detected and
|
|
45
|
+
// reported but, by this guard, never enter the compacted message-id set and never
|
|
46
|
+
// alter the compacted trace. policy-middleware also independently filters to
|
|
47
|
+
// "repeated_tool_output" (findRepeatCandidates), so skill injections are never applied.
|
|
48
|
+
if (finding.category !== "repeated_tool_output") {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const [, ...duplicateIds] = finding.messageIds;
|
|
52
|
+
for (const duplicateId of duplicateIds) {
|
|
53
|
+
compactedIds.add(duplicateId);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return compactedIds;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=waste-detector.js.map
|