@compaction/cli 0.2.0 → 0.3.1
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 +142 -64
- 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 +9 -5
- 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/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 +297 -12
- 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 +36 -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/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/run-aggregator.d.ts +11 -1
- package/dist/core/run-aggregator.js +29 -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-parser.d.ts +13 -13
- package/dist/core/trace-parser.js +6 -6
- package/dist/core/types.d.ts +12 -2
- package/dist/core/waste-detector.d.ts +20 -0
- package/dist/core/waste-detector.js +160 -6
- package/package.json +1 -1
|
@@ -35,17 +35,171 @@ export function detectWaste(trace, thresholds = {}) {
|
|
|
35
35
|
}
|
|
36
36
|
firstToolOutputBySignature.set(signature, message);
|
|
37
37
|
}
|
|
38
|
+
// ADDITIVE: input-compaction category 1 (superseded same-source reads). The existing
|
|
39
|
+
// repeated_tool_output logic above is UNCHANGED; category-1 findings are appended so the existing
|
|
40
|
+
// pipeline (getCompactedMessageIds, policy-middleware) can pick them up. Category 1 only emits when
|
|
41
|
+
// an earlier same-source read GENUINELY DIFFERS from the latest, so it never overlaps an
|
|
42
|
+
// identical-up-to-whitespace repeat (which stays a repeated_tool_output finding).
|
|
43
|
+
findings.push(...detectSupersededSameSourceReads(trace, thresholds));
|
|
44
|
+
return findings;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Source identity of a tool OUTPUT for input-compaction category 1 (`superseded_same_source_read`).
|
|
48
|
+
*
|
|
49
|
+
* Identity = `toolName + "::" + normalizedArgs`, where `normalizedArgs` is derived from the
|
|
50
|
+
* originating tool CALL: the nearest PRECEDING assistant message (since the last tool output) with
|
|
51
|
+
* the SAME `toolName` whose `content` parses as the call args. Parsing is deterministic and
|
|
52
|
+
* CONSERVATIVE — two accepted shapes ONLY:
|
|
53
|
+
* 1. a JSON object -> JSON.stringify with sorted keys (so key order does not change identity);
|
|
54
|
+
* 2. a `"Tool call: <cmd>"` string -> the `<cmd>`, whitespace-normalized.
|
|
55
|
+
* Anything else (no preceding same-tool call, unparseable content) yields `null` => the output is
|
|
56
|
+
* NOT eligible for category 1 (conservative: if source identity cannot be PROVEN, do not compact).
|
|
57
|
+
*
|
|
58
|
+
* This is pure deterministic equality of provable source identity. There is NO scoring, threshold
|
|
59
|
+
* (beyond the existing min-tokens/chars in detectWaste), weight, relevance, or learned logic here.
|
|
60
|
+
*/
|
|
61
|
+
function normalizeCallArgs(callContent) {
|
|
62
|
+
const trimmed = callContent.trim();
|
|
63
|
+
// Shape 2: a "Tool call: <cmd>" string (the command form, whitespace-normalized).
|
|
64
|
+
const toolCallMatch = /^Tool call:\s*(.+)$/s.exec(trimmed);
|
|
65
|
+
if (toolCallMatch) {
|
|
66
|
+
const command = toolCallMatch[1].trim().replace(/\s+/g, " ");
|
|
67
|
+
return command.length > 0 ? `cmd:${command}` : null;
|
|
68
|
+
}
|
|
69
|
+
// Shape 1: a JSON object -> stable stringify with sorted keys.
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(trimmed);
|
|
72
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
73
|
+
const sortedKeys = Object.keys(parsed).sort();
|
|
74
|
+
const stable = JSON.stringify(parsed, sortedKeys);
|
|
75
|
+
return `json:${stable}`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Not JSON — fall through to "not provable".
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Resolve the PROVABLE source identity of a tool output at `outputIndex`. Walks BACKWARD from the
|
|
85
|
+
* output to the nearest preceding assistant message that has the SAME toolName and whose content
|
|
86
|
+
* parses as call args (the originating CALL). Stops at the previous tool output boundary ("since the
|
|
87
|
+
* last tool output") so a call belonging to an EARLIER output is never mis-attributed. Returns the
|
|
88
|
+
* identity `toolName + "::" + normalizedArgs`, or `null` when no such parseable same-tool call exists
|
|
89
|
+
* (source identity unknown/ambiguous => not eligible for category 1).
|
|
90
|
+
*/
|
|
91
|
+
function resolveSourceIdentity(messages, outputIndex) {
|
|
92
|
+
const output = messages[outputIndex];
|
|
93
|
+
const toolName = output.toolName;
|
|
94
|
+
if (!toolName) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
for (let i = outputIndex - 1; i >= 0; i -= 1) {
|
|
98
|
+
const candidate = messages[i];
|
|
99
|
+
// Boundary: do not look past the previous tool output (a call before it originated THAT output).
|
|
100
|
+
if (candidate.role === "tool") {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
if (candidate.role !== "assistant" || candidate.toolName !== toolName) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const normalizedArgs = normalizeCallArgs(candidate.content);
|
|
107
|
+
if (normalizedArgs === null) {
|
|
108
|
+
// A same-tool assistant message that is NOT a parseable call: ambiguous. Conservative -> skip.
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return `${toolName}::${normalizedArgs}`;
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Input-compaction category 1: detect SUPERSEDED same-source reads.
|
|
117
|
+
*
|
|
118
|
+
* When the SAME source (provable source identity) is read ≥2 times, the LATEST read is authoritative
|
|
119
|
+
* (preserved verbatim). Each EARLIER read whose whitespace-normalized content GENUINELY DIFFERS from
|
|
120
|
+
* the latest's is a superseded candidate that can be replaced by a recoverable pointer to the
|
|
121
|
+
* retained original. Conservative & additive:
|
|
122
|
+
* - Outputs with NO provable source identity are skipped (not eligible).
|
|
123
|
+
* - An earlier output that is identical-up-to-whitespace to the latest is LEFT to the existing
|
|
124
|
+
* `repeated_tool_output` policy (NOT category-1-compacted) — no overlap, no double-compaction.
|
|
125
|
+
* - Each compacted (earlier) output must meet the SAME thresholds detectWaste uses
|
|
126
|
+
* (minDuplicateTokens / minDuplicateCharacters).
|
|
127
|
+
*
|
|
128
|
+
* Finding convention: `messageIds[0]` = KEPT latest authoritative read; `messageIds[1..]` = earlier
|
|
129
|
+
* superseded reads to compact (estimatedTokens = sum of the compacted earlier reads' estimates).
|
|
130
|
+
*
|
|
131
|
+
* NO scoring formula, weight, relevance, or learned logic — only deterministic source-identity
|
|
132
|
+
* equality + whitespace-normalized content difference + the existing min-tokens/chars thresholds.
|
|
133
|
+
*/
|
|
134
|
+
export function detectSupersededSameSourceReads(trace, thresholds = {}) {
|
|
135
|
+
const minTokens = thresholds.minDuplicateTokens ?? 0;
|
|
136
|
+
const minChars = thresholds.minDuplicateCharacters ?? 0;
|
|
137
|
+
// Group eligible tool outputs by provable source identity, preserving order.
|
|
138
|
+
const groups = new Map();
|
|
139
|
+
for (let index = 0; index < trace.messages.length; index += 1) {
|
|
140
|
+
const message = trace.messages[index];
|
|
141
|
+
if (message.role !== "tool") {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const identity = resolveSourceIdentity(trace.messages, index);
|
|
145
|
+
if (identity === null) {
|
|
146
|
+
continue; // source identity unknown/ambiguous -> not eligible (conservative)
|
|
147
|
+
}
|
|
148
|
+
const existing = groups.get(identity);
|
|
149
|
+
if (existing) {
|
|
150
|
+
existing.push(message);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
groups.set(identity, [message]);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const findings = [];
|
|
157
|
+
for (const outputs of groups.values()) {
|
|
158
|
+
if (outputs.length < 2) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const latest = outputs[outputs.length - 1];
|
|
162
|
+
const latestNormalized = normalizeContent(latest.content);
|
|
163
|
+
const supersededIds = [];
|
|
164
|
+
let estimatedTokens = 0;
|
|
165
|
+
for (let i = 0; i < outputs.length - 1; i += 1) {
|
|
166
|
+
const earlier = outputs[i];
|
|
167
|
+
// GENUINE supersession ONLY: content changed (normalized-different from the latest). An
|
|
168
|
+
// identical-up-to-whitespace earlier copy is the existing repeated_tool_output case -> leave it.
|
|
169
|
+
if (normalizeContent(earlier.content) === latestNormalized) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
// Same thresholds the existing detector uses, applied per compacted (earlier) output.
|
|
173
|
+
const earlierTokens = estimateTextTokens(earlier.content);
|
|
174
|
+
if (earlierTokens < minTokens || earlier.content.length < minChars) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
supersededIds.push(earlier.id);
|
|
178
|
+
estimatedTokens += earlierTokens;
|
|
179
|
+
}
|
|
180
|
+
if (supersededIds.length === 0) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
findings.push({
|
|
184
|
+
category: "superseded_same_source_read",
|
|
185
|
+
messageIds: [latest.id, ...supersededIds],
|
|
186
|
+
summary: `Superseded ${describeTool(latest)} from the same source. ` +
|
|
187
|
+
`Keep latest ${latest.id} (verbatim) and compact superseded ${supersededIds.join(", ")}.`,
|
|
188
|
+
estimatedTokens
|
|
189
|
+
});
|
|
190
|
+
}
|
|
38
191
|
return findings;
|
|
39
192
|
}
|
|
40
193
|
export function getCompactedMessageIds(findings) {
|
|
41
194
|
const compactedIds = new Set();
|
|
42
195
|
for (const finding of findings) {
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// "
|
|
48
|
-
|
|
196
|
+
// Categories whose later message(s) are compacted (messageIds[0] is the KEPT message):
|
|
197
|
+
// - "repeated_tool_output": keep the first copy, compact the later identical duplicate(s).
|
|
198
|
+
// - "superseded_same_source_read" (category 1): keep the LATEST authoritative read, compact the
|
|
199
|
+
// earlier superseded copies (messageIds[1..]).
|
|
200
|
+
// "repeated_skill_injection" is report-only and is NEVER compacted by this guard (its findings are
|
|
201
|
+
// detected/reported but never enter the compacted set; policy-middleware also independently filters).
|
|
202
|
+
if (finding.category !== "repeated_tool_output" && finding.category !== "superseded_same_source_read") {
|
|
49
203
|
continue;
|
|
50
204
|
}
|
|
51
205
|
const [, ...duplicateIds] = finding.messageIds;
|