@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.
Files changed (153) hide show
  1. package/README.md +142 -64
  2. package/dist/cli/commands/activity.d.ts +8 -0
  3. package/dist/cli/commands/activity.js +35 -0
  4. package/dist/cli/commands/apply-context.js +68 -0
  5. package/dist/cli/commands/billing-delta.js +12 -1
  6. package/dist/cli/commands/capture-claude-code.d.ts +44 -0
  7. package/dist/cli/commands/capture-claude-code.js +206 -0
  8. package/dist/cli/commands/capture.js +283 -1
  9. package/dist/cli/commands/compact.js +9 -5
  10. package/dist/cli/commands/dev.d.ts +17 -0
  11. package/dist/cli/commands/dev.js +131 -0
  12. package/dist/cli/commands/gateway.d.ts +31 -0
  13. package/dist/cli/commands/gateway.js +312 -0
  14. package/dist/cli/commands/hooks.d.ts +5 -0
  15. package/dist/cli/commands/hooks.js +181 -0
  16. package/dist/cli/commands/init.js +422 -17
  17. package/dist/cli/commands/input-compaction-ab.d.ts +2 -0
  18. package/dist/cli/commands/input-compaction-ab.js +125 -0
  19. package/dist/cli/commands/optimize-hosted.d.ts +54 -0
  20. package/dist/cli/commands/optimize-hosted.js +123 -0
  21. package/dist/cli/commands/optimize.js +8 -0
  22. package/dist/cli/commands/output-shaping-ab.d.ts +2 -0
  23. package/dist/cli/commands/output-shaping-ab.js +132 -0
  24. package/dist/cli/commands/output-shaping.d.ts +7 -0
  25. package/dist/cli/commands/output-shaping.js +43 -0
  26. package/dist/cli/commands/policies.d.ts +2 -0
  27. package/dist/cli/commands/policies.js +80 -0
  28. package/dist/cli/commands/precall.d.ts +19 -0
  29. package/dist/cli/commands/precall.js +436 -0
  30. package/dist/cli/commands/recommend.js +1 -1
  31. package/dist/cli/commands/run.js +297 -12
  32. package/dist/cli/commands/upgrade-status.d.ts +42 -0
  33. package/dist/cli/commands/upgrade-status.js +152 -0
  34. package/dist/cli/cursor-export-read.d.ts +13 -0
  35. package/dist/cli/cursor-export-read.js +53 -0
  36. package/dist/cli/cursor-live-preflight.d.ts +19 -0
  37. package/dist/cli/cursor-live-preflight.js +46 -0
  38. package/dist/cli/index.js +36 -2
  39. package/dist/cli/onboarding/GatewayTui.d.ts +33 -0
  40. package/dist/cli/onboarding/GatewayTui.js +113 -0
  41. package/dist/cli/onboarding/model.d.ts +32 -1
  42. package/dist/cli/onboarding/model.js +27 -2
  43. package/dist/cli/terminal-logo.d.ts +27 -0
  44. package/dist/cli/terminal-logo.js +49 -0
  45. package/dist/core/activity-event.d.ts +107 -0
  46. package/dist/core/activity-event.js +182 -0
  47. package/dist/core/activity-store.d.ts +63 -0
  48. package/dist/core/activity-store.js +254 -0
  49. package/dist/core/activity-view.d.ts +76 -0
  50. package/dist/core/activity-view.js +120 -0
  51. package/dist/core/api-client/index.d.ts +3 -1
  52. package/dist/core/api-client/index.js +4 -0
  53. package/dist/core/api-client/payload.d.ts +5 -1
  54. package/dist/core/api-client/payload.js +8 -6
  55. package/dist/core/api-client/persisted-config.d.ts +57 -0
  56. package/dist/core/api-client/persisted-config.js +156 -0
  57. package/dist/core/api-client/tool.d.ts +29 -0
  58. package/dist/core/api-client/tool.js +50 -0
  59. package/dist/core/api-client/types.d.ts +15 -4
  60. package/dist/core/auto-apply-ask.d.ts +58 -0
  61. package/dist/core/auto-apply-ask.js +105 -0
  62. package/dist/core/auto-apply-gates.d.ts +76 -0
  63. package/dist/core/auto-apply-gates.js +113 -0
  64. package/dist/core/before-call-activity.d.ts +35 -0
  65. package/dist/core/before-call-activity.js +103 -0
  66. package/dist/core/before-call-recovery.d.ts +19 -0
  67. package/dist/core/before-call-recovery.js +46 -0
  68. package/dist/core/before-call-stdin.d.ts +59 -0
  69. package/dist/core/before-call-stdin.js +78 -0
  70. package/dist/core/before-call.d.ts +149 -0
  71. package/dist/core/before-call.js +358 -0
  72. package/dist/core/billing-delta/billing-delta-record.d.ts +28 -0
  73. package/dist/core/billing-delta/billing-delta-record.js +26 -0
  74. package/dist/core/capture-record.d.ts +40 -0
  75. package/dist/core/capture-record.js +55 -0
  76. package/dist/core/claude-code-before-call.d.ts +37 -0
  77. package/dist/core/claude-code-before-call.js +120 -0
  78. package/dist/core/claude-code-connect.d.ts +31 -0
  79. package/dist/core/claude-code-connect.js +87 -0
  80. package/dist/core/claude-code-hook-record.d.ts +71 -0
  81. package/dist/core/claude-code-hook-record.js +0 -0
  82. package/dist/core/claude-code-hooks.d.ts +77 -0
  83. package/dist/core/claude-code-hooks.js +141 -0
  84. package/dist/core/codex-capture.d.ts +45 -0
  85. package/dist/core/codex-capture.js +204 -0
  86. package/dist/core/command-runner.js +5 -1
  87. package/dist/core/compaction-artifacts.d.ts +51 -0
  88. package/dist/core/compaction-artifacts.js +242 -0
  89. package/dist/core/compactor.d.ts +9 -0
  90. package/dist/core/compactor.js +95 -0
  91. package/dist/core/cross-surface-event.d.ts +306 -0
  92. package/dist/core/cross-surface-event.js +330 -0
  93. package/dist/core/cursor-capture.d.ts +54 -0
  94. package/dist/core/cursor-capture.js +215 -0
  95. package/dist/core/cursor-preflight-probe.d.ts +8 -0
  96. package/dist/core/cursor-preflight-probe.js +88 -0
  97. package/dist/core/cursor-preflight.d.ts +86 -0
  98. package/dist/core/cursor-preflight.js +126 -0
  99. package/dist/core/gateway/apply-activation.d.ts +39 -0
  100. package/dist/core/gateway/apply-activation.js +84 -0
  101. package/dist/core/gateway/apply-policy.d.ts +64 -0
  102. package/dist/core/gateway/apply-policy.js +221 -0
  103. package/dist/core/gateway/apply-receipt.d.ts +36 -0
  104. package/dist/core/gateway/apply-receipt.js +75 -0
  105. package/dist/core/gateway/cache-proof.d.ts +45 -0
  106. package/dist/core/gateway/cache-proof.js +65 -0
  107. package/dist/core/gateway/configure.d.ts +50 -0
  108. package/dist/core/gateway/configure.js +169 -0
  109. package/dist/core/gateway/openai-usage.d.ts +56 -0
  110. package/dist/core/gateway/openai-usage.js +128 -0
  111. package/dist/core/gateway/receipt.d.ts +138 -0
  112. package/dist/core/gateway/receipt.js +120 -0
  113. package/dist/core/gateway/recovery.d.ts +23 -0
  114. package/dist/core/gateway/recovery.js +68 -0
  115. package/dist/core/gateway/server.d.ts +51 -0
  116. package/dist/core/gateway/server.js +276 -0
  117. package/dist/core/gateway/status.d.ts +45 -0
  118. package/dist/core/gateway/status.js +109 -0
  119. package/dist/core/hook-usage-aggregate.d.ts +47 -0
  120. package/dist/core/hook-usage-aggregate.js +161 -0
  121. package/dist/core/input-compaction-ab.d.ts +111 -0
  122. package/dist/core/input-compaction-ab.js +158 -0
  123. package/dist/core/local-run-record.d.ts +109 -0
  124. package/dist/core/local-run-record.js +223 -0
  125. package/dist/core/output-shaping-ab.d.ts +140 -0
  126. package/dist/core/output-shaping-ab.js +146 -0
  127. package/dist/core/output-shaping-attach.d.ts +31 -0
  128. package/dist/core/output-shaping-attach.js +57 -0
  129. package/dist/core/output-shaping.d.ts +56 -0
  130. package/dist/core/output-shaping.js +89 -0
  131. package/dist/core/policy-middleware.d.ts +121 -0
  132. package/dist/core/policy-middleware.js +919 -0
  133. package/dist/core/policy-preferences.d.ts +99 -0
  134. package/dist/core/policy-preferences.js +232 -0
  135. package/dist/core/run-aggregator.d.ts +11 -1
  136. package/dist/core/run-aggregator.js +29 -2
  137. package/dist/core/run-flow-report.d.ts +82 -0
  138. package/dist/core/run-flow-report.js +71 -0
  139. package/dist/core/safety-report.js +8 -1
  140. package/dist/core/shim-capture-bridge.d.ts +32 -0
  141. package/dist/core/shim-capture-bridge.js +88 -0
  142. package/dist/core/skill-injection-policy.d.ts +72 -0
  143. package/dist/core/skill-injection-policy.js +183 -0
  144. package/dist/core/spend-attribution.js +1 -1
  145. package/dist/core/token-accounting.d.ts +1 -1
  146. package/dist/core/tool-shim.d.ts +129 -0
  147. package/dist/core/tool-shim.js +447 -0
  148. package/dist/core/trace-parser.d.ts +13 -13
  149. package/dist/core/trace-parser.js +6 -6
  150. package/dist/core/types.d.ts +12 -2
  151. package/dist/core/waste-detector.d.ts +20 -0
  152. package/dist/core/waste-detector.js +160 -6
  153. package/package.json +1 -1
package/README.md CHANGED
@@ -1,17 +1,53 @@
1
1
  # @compaction/cli
2
2
 
3
- **The free, local-first CLI for capturing and measuring AI agent traces.** It captures
4
- supported agent sessions on your own machine, shows input/output tokens and estimated spend
5
- with honest labels, and writes local reports and redacted share bundles. Everything runs
6
- locally and writes local files only **no account, no telemetry, no upload by default.**
3
+ **The free, local-first CLI for the Compaction Gateway a local, OpenAI-compatible proxy that
4
+ measures your AI traffic without changing it.** Point your OpenAI-compatible client's base URL at
5
+ the local gateway (or launch your workflow through it) and it forwards traffic to your provider and
6
+ records **content-free** token/cache receiptscounts and labels only, never prompt/response
7
+ content. Your provider key stays on the client. The CLI also captures and measures agent traces
8
+ locally. Everything runs on your machine — **no account, no telemetry, no upload to Compaction.**
9
+
10
+ > The free CLI runs the **local Gateway** (byte-safe **record** mode + content-free receipts, plus
11
+ > an **explicit, experimental deterministic apply/recovery**), captures and measures agent traces,
12
+ > and performs deterministic local compaction with the basic policies (`compact`) — writing honest
13
+ > before/after artifacts. The **learned context model (LCM), learned optimization engine, hosted /
14
+ > team control plane, verified-savings logic, and advanced proprietary evals are NOT in the npm /
15
+ > free CLI** — they are the **opt-in Compaction API**, which is **not a live hosted service yet**.
16
+
17
+ ## The Gateway (local, byte-safe)
18
+
19
+ The Compaction **Gateway** is a **local, OpenAI-compatible reverse proxy for OpenAI-compatible
20
+ clients**. Point your client's base URL at it, or launch your workflow through it, and it forwards
21
+ traffic to your provider and records **content-free** token/cache receipts (counts / structure /
22
+ labels only — never prompt/response content). Your provider key rides straight through to the
23
+ provider and is **never read, stored, or logged**. Set it up from onboarding (`compaction` →
24
+ **Gateway setup**), or directly:
7
25
 
8
- > **The free CLI captures, measures, inspects, reports, and redacts.** Optimization, advanced
9
- > verification, apply guidance, policies, and team reporting are **not** in the free CLI — they
10
- > are the **opt-in Compaction API**, which is **not a live hosted service yet**.
11
-
12
- ## What the CLI does
26
+ ```bash
27
+ compaction gateway start # start the local gateway (RECORD mode: byte-safe, no mutation)
28
+ compaction gateway run -- npm run dev # run a command through the gateway (no baseURL copy-paste)
29
+ compaction gateway status # requests observed + a content-free receipts rollup
30
+ compaction gateway activate # print env exports: eval "$(compaction gateway activate)"
31
+ ```
13
32
 
14
- Everything below runs on your machine and writes local files only:
33
+ - **Record mode is byte-safe** — it forwards your request and the provider's response
34
+ **byte-for-byte** and never changes either. Receipts are content-free and local-only (gitignored).
35
+ - **Explicit, experimental deterministic apply** (`--mode apply --policy deterministic-dedupe`) can
36
+ remove **exact-duplicate large blocks** from known-safe requests. It is **off by default**,
37
+ opt-in, and **retains the original locally** (`compaction gateway recover <id>`). Unknown/complex
38
+ shapes **fail closed** (forwarded unchanged); system/developer instructions and tool schemas are
39
+ never touched; the **response is never changed**.
40
+ - **No hosted/managed proxy or model routing.** The local Gateway is an OpenAI-compatible reverse
41
+ proxy for compatible clients — it runs on your machine.
42
+ - **Claim boundary.** The gateway reports **provider-reported** token/cache counts (or
43
+ `unavailable`) — it makes **no cost-savings, no output-token-savings, and no billing-confirmed
44
+ claim**. A model-visible **input** reduction is stated only when apply actually changed the
45
+ request (a **local-estimate** before/after; the original is retained) — never a cost or
46
+ output-token figure, and never a "no context lost" claim.
47
+
48
+ ## Trace / capture utilities (other local tools)
49
+
50
+ Beyond the Gateway, the free CLI captures and measures agent traces locally — local files only:
15
51
 
16
52
  - **Capture supported agent sessions.** Claude Code is the primary supported workflow
17
53
  (`capture claude-code`); OpenAI Agents SDK (`capture openai-agents`) and Codex / local-file
@@ -22,59 +58,64 @@ Everything below runs on your machine and writes local files only:
22
58
  run carries provider usage metadata; otherwise they are a **local estimate (chars/4)**. Cost is
23
59
  a **price-table estimate** labeled `local estimate` / `token-estimated cost` — **never
24
60
  billing-confirmed**.
61
+ - **Compact deterministically, locally.** `compact` applies the basic deterministic policies
62
+ (e.g. `stale_tool_output_to_state_capsule`) to a captured/imported trace and writes honest
63
+ before/after artifacts locally (report, safety/recoverability report, state capsules). The
64
+ original trace is retained, token figures stay **local estimates** unless provider-reported,
65
+ and **nothing is applied to your workflow automatically**.
25
66
  - **Inspect spend and roll up runs.** `spend`, `summary`, and `aggregate` summarize where context
26
67
  spend came from and roll up local runs — local files only, no hosted dashboard.
27
68
  - **Build a local context store and retrieve from it.** `context add` accumulates source-pointed
28
69
  items from a captured/imported trace into a local, size-capped store; `context get`
29
70
  deterministically retrieves and assembles the active context for a query under a token budget,
30
- printing it **with source pointers** plus local retrieval **diagnostics** (what was
31
- considered / included / dropped, the recoverable fraction, latency). Local-only and
32
- deterministic **no model, no embeddings, no network**. It reports **diagnostics only** (no
33
- quality or sufficiency verdict on your data) and makes **no savings claim** (see `spend` /
34
- `summary` for cost).
35
- - **Write local reports.** `analyze` writes local JSON + Markdown reports under an
36
- operator-chosen output directory. Local files only.
71
+ printing it **with source pointers** plus local retrieval **diagnostics**. Local-only and
72
+ deterministic **no model, no embeddings, no network**. It reports **diagnostics only** and
73
+ makes **no savings claim**.
74
+ - **Write local reports.** `analyze` writes local JSON + Markdown reports under an operator-chosen
75
+ output directory. Local files only.
37
76
  - **Create redacted, content-free share bundles.** `feedback --redact` writes a redacted,
38
77
  privacy-safe bundle as a **local file only — never uploaded**, and only with an explicit write
39
78
  gesture (`--yes`).
40
- - **No upload by default.** There is **no telemetry**. The **only** network call in the free CLI
41
- is the explicitly operator-invoked, read-only `capture provider-usage` (credential supplied via
42
- an environment variable only; aggregate usage/cost figures only, no prompt/completion content).
43
79
 
44
- The free CLI deliberately answers the first-value question — *"where is my context being wasted,
45
- and roughly what does it cost?"* — entirely locally, with honest labels.
80
+ ## Free (npm) vs. private (opt-in API)
46
81
 
47
- ## What requires the Compaction API (opt-in — not in the free CLI)
82
+ **In the free / local CLI (npm):**
48
83
 
49
- These are **not** part of the free local CLI:
84
+ - the **local Gateway** record mode, `status`, content-free receipts, `gateway run`,
85
+ - **explicit deterministic apply / dry-run / recover** (experimental, opt-in),
86
+ - **capture / import / analyze / spend / compact / context / feedback** (local trace utilities).
50
87
 
51
- - **Optimization** producing a compacted context from a captured trace.
52
- - **Advanced verification** — stronger checks on what a compacted context retains.
53
- - **Apply guidance** — a readiness verdict and a review/approval path before any context is applied.
54
- - **Policies & team reporting** — configurable behavior controls and cross-run / team reporting.
88
+ **NOT in the npm / free CLI** (these are the opt-in Compaction API — **not a live hosted service
89
+ yet**):
55
90
 
56
- > **The Compaction API is not broadly hosted yet.** There is **no live hosted service today**.
57
- > Until the API is available, these capabilities are **on the roadmap / opt-in**, not present in
58
- > the free CLI. The honesty rails carry over unchanged: cost stays an **estimate** unless
59
- > billing-confirmed (no figure carries that label today); verification is **deterministic
60
- > recoverability, NOT semantic/meaning preservation**; and there is **no auto-apply** every
61
- > application is an explicit operator gesture.
91
+ - the **LCM / learned context model**,
92
+ - the **learned optimization engine**,
93
+ - the **hosted / team control plane**,
94
+ - **verified-savings** logic,
95
+ - **advanced proprietary evals** (and recommendation / advanced verification / apply-guidance / advanced policies).
96
+
97
+ > **The Compaction API is not broadly hosted yet.** There is **no live hosted service today**. The
98
+ > honesty rails carry over unchanged: cost stays an **estimate** unless billing-confirmed (no figure
99
+ > carries that label today); verification is **deterministic recoverability, NOT semantic/meaning
100
+ > preservation**; and there is **no auto-apply** — every application is an explicit operator gesture.
62
101
 
63
102
  ## Install
64
103
 
65
- A local-first CLI. It makes no network calls by default; the only command that reaches the
66
- network is the explicitly operator-invoked `capture provider-usage`. There is no telemetry.
104
+ A local-first CLI. **No telemetry and no upload to Compaction.** Network happens only when you
105
+ explicitly call provider-usage (`capture provider-usage`) or route your own provider traffic through
106
+ the local gateway (which forwards it to your provider). Nothing else reaches the network.
67
107
 
68
108
  ### npm / npx
69
109
 
70
110
  ```bash
71
- npm install -g @compaction/cli # then: compaction init
111
+ npm install -g @compaction/cli # then: compaction
72
112
  npx @compaction/cli init # no global install
73
113
  npx @compaction/cli --help # no install at all
74
114
  ```
75
115
 
76
116
  The installed binary is `compaction`. After a global install, sanity-check with
77
- `compaction --version` (→ `0.2.0`) and `compaction --help`.
117
+ `compaction --version` (→ `0.3.1`) and `compaction --help`. Run `compaction` (no args) to open
118
+ onboarding, including **Gateway setup**.
78
119
 
79
120
  ### curl one-liner
80
121
 
@@ -94,23 +135,29 @@ curl -fsSL https://cli.compaction.dev/install | sh -s -- --dry-run
94
135
 
95
136
  ## Quick start
96
137
 
97
- Run on **your own** captured/imported workflow — not a demo. The recommended free local loop is
98
- **capture → analyze → (local report) → redact / share**:
138
+ Two local loops, both on **your own** workflow — not a demo.
139
+
140
+ **Route your traffic through the local Gateway** (byte-safe; content-free receipts):
99
141
 
100
142
  ```bash
101
- compaction init # one-time local scaffold (writes nothing until you capture)
102
- # 1. Capture a real workflow (Claude Code shown; --discover lists local sessions):
103
- compaction capture claude-code --discover
143
+ compaction gateway start # start the local gateway (RECORD mode)
144
+ compaction gateway run -- npm run dev # or point your OpenAI client's baseURL at the listen address + /v1
145
+ compaction gateway status # see requests observed + the content-free receipts rollup
146
+ ```
147
+
148
+ **Capture + measure an agent trace** (local files only):
149
+
150
+ ```bash
151
+ compaction init # one-time local scaffold (writes nothing until you capture)
152
+ compaction capture claude-code --discover # list local Claude Code sessions
104
153
  compaction capture claude-code --session <session.jsonl> --out ./my-trace
105
- # 2. Measure tokens + estimated spend, and see where it came from:
106
- compaction analyze ./my-trace/captured-trace.json
107
- compaction spend ./my-trace/captured-trace.json
108
- # 3. Share a redacted, content-free bundle (local file only, never uploaded):
109
- compaction feedback --redact --yes
154
+ compaction analyze ./my-trace/captured-trace.json # tokens + estimated spend, honestly labeled
155
+ compaction feedback --redact --yes # redacted, content-free bundle (local file only, never uploaded)
110
156
  ```
111
157
 
112
- Optimization, verification, and apply guidance are the opt-in Compaction API — not steps of the
113
- free local CLI.
158
+ Deterministic basic compaction (`compaction compact <trace> --out <dir>`) also runs locally.
159
+ Advanced verification, recommendation, and apply guidance are the opt-in Compaction API — not steps
160
+ of the free local CLI.
114
161
 
115
162
  ## Supported local workflows
116
163
 
@@ -126,14 +173,29 @@ free local CLI.
126
173
  `codex exec --json` export) and normalizes it. **Level 1.** Codex token counts on this path are
127
174
  **locally estimated (chars/4)**, not provider-reported. `compaction import --list-sources` lists
128
175
  supported sources.
176
+ - **OpenAI-compatible clients via the local Gateway** — any OpenAI-compatible client can route
177
+ through `compaction gateway` for content-free receipts (and, opt-in, explicit deterministic apply).
129
178
 
130
- There is **no** native/Level 3 SDK instrumentation, no production proxy, no model routing, and no
179
+ There is **no** native/Level 3 SDK instrumentation, **no hosted/managed proxy or model routing**
180
+ (the local Gateway is an OpenAI-compatible reverse proxy for compatible clients), and no
131
181
  consumer-app (ChatGPT/Claude desktop) support.
132
182
 
133
183
  ## Commands
134
184
 
135
185
  ```bash
186
+ compaction # onboarding (workflow chooser + Gateway setup)
136
187
  compaction init # one-time local scaffold
188
+
189
+ # Gateway (local, byte-safe):
190
+ compaction gateway start # start the local gateway (RECORD mode)
191
+ compaction gateway run -- <command> # run a command through the gateway
192
+ compaction gateway status # requests observed + content-free receipts rollup
193
+ compaction gateway activate # print env exports (eval-able); prints only, no mutation
194
+ compaction gateway configure # approval-gated: propose a project baseURL (diff, no write without --apply)
195
+ compaction gateway recover <recovery_id> # recover the original request retained by apply mode
196
+ compaction gateway stop # stop a running local gateway
197
+
198
+ # Trace / capture utilities:
137
199
  compaction capture claude-code --session <session.jsonl> [--include-subagents] --out <dir>
138
200
  compaction capture claude-code --discover [--projects-dir <path>]
139
201
  compaction capture openai-agents --out <dir> -- <command> [args...]
@@ -141,6 +203,7 @@ compaction capture provider-usage --endpoint <url> [--credential-env-var <NAME>]
141
203
  compaction import <trace-file> --source <source> --out <dir>
142
204
  compaction import --list-sources
143
205
  compaction analyze <trace-file>
206
+ compaction compact <trace-file> --out <dir> # deterministic local compaction (basic policies) + honest artifacts
144
207
  compaction context add <trace-file> # build a local context store from a captured/imported trace
145
208
  compaction context get "<query>" # retrieve + assemble the active context (with source pointers)
146
209
  compaction spend <trace-or-optimization-path> # context-spend attribution
@@ -149,15 +212,20 @@ compaction aggregate # per-session / cross-session l
149
212
  compaction feedback --redact [--yes] # redacted, local-only share bundle
150
213
  ```
151
214
 
152
- `compaction --help` lists the free local commands.
215
+ `compaction --help` lists the free local commands; `compaction gateway --help` lists the gateway commands.
153
216
 
154
217
  ## Privacy and security
155
218
 
156
- - **No telemetry; no upload by default.** The free CLI is local-first and file-based.
157
- - **One network call only.** `capture provider-usage` is explicitly operator-invoked and read-only;
158
- its credential comes from an **environment variable only** (never a flag value, never logged,
159
- never written into artifacts), and it captures **aggregate** usage/cost figures only — no
160
- prompt/completion content. Its absence is a clean refusal.
219
+ - **No telemetry; no upload to Compaction.** The free CLI is local-first and file-based.
220
+ - **Network only on explicit action.** Network happens only when you explicitly call
221
+ `capture provider-usage` (read-only; credential via an **environment variable only**, never a flag
222
+ value, never logged, never written into artifacts; **aggregate** usage/cost figures only — no
223
+ prompt/completion content) **or** route your own provider traffic through the local gateway (which
224
+ forwards it to your provider; your provider key stays on the client and is never read or stored).
225
+ - **Content-free gateway receipts.** Gateway receipts record token/cache counts and labels only —
226
+ never prompt/response content; they are local-only (gitignored). Apply mode retains the original
227
+ request **locally** (restrictive permissions) so it is recoverable; that original is referenced
228
+ from receipts by id only.
161
229
  - **Captured traces can contain local content.** Capturing a session may include local file
162
230
  content — review a trace before sharing it.
163
231
  - **Redacted sharing.** `feedback --redact` produces a redacted, content-free bundle as a local
@@ -171,17 +239,24 @@ Every figure this tool produces carries one of these labels — and nothing stro
171
239
  | Label | Meaning |
172
240
  |---|---|
173
241
  | **local estimate** | chars/4 token heuristics and price-table cost approximations computed locally |
174
- | **provider-reported token delta** | token counts read from the provider's own API response/usage metadata (operator-entered or captured); input tokens are deterministic for a fixed context+prompt |
242
+ | **provider-reported token delta** | token counts read from the provider's own API response/usage metadata (operator-entered, captured, or via the gateway); input tokens are deterministic for a fixed context+prompt |
175
243
  | **token-estimated cost** | a price-table estimate applied to provider-reported token counts (`price_table_estimate`) — an estimate, never a billed figure |
176
244
  | **operator-entered billing record** | billed costs the operator read from their own provider console; recorded, not provider-verified by this tool |
177
245
  | **billing-confirmed** | only with real provider billing/invoice evidence **AND** the accepted measurement criterion met on those billed runs (N ≥ 3 runs per arm AND the delta-of-means ±2·SE interval excludes zero) — the statistical criterion alone is a bar on operator-entered figures, not proof of billing, so it is never sufficient by itself — **no figure in this repository currently carries this label** |
178
246
 
179
- **What is NOT claimed:** no billing-confirmed savings claim; no invoice savings claim; no general savings claim (all results are workflow-scoped and caveated); no auto-apply (every application is an explicit operator gesture); no hosted service or control plane (the free CLI is local-first, file-based); no universal "no context is lost" claim; no semantic/meaning preservation claim (it stays `not_evaluated`); no native/production/Level 3 integration claim.
247
+ **What is NOT claimed:** no billing-confirmed savings claim; no invoice savings claim; no
248
+ output-token savings claim unless measured before/after **and** eval-confirmed; no general savings
249
+ claim (all results are workflow-scoped and caveated); no auto-apply (every application is an explicit
250
+ operator gesture); no hosted service or control plane, and **no hosted-live claim** (the free CLI +
251
+ Gateway are local-first, file-based); no universal "no context is lost" claim; no semantic/meaning
252
+ preservation claim (it stays `not_evaluated`); no native/production/Level 3 integration claim.
180
253
 
181
254
  ## Known limitations
182
255
 
183
- - **The free CLI captures and measures; it does not optimize or verify.** Optimization, advanced
184
- verification, apply guidance, policies, and team reporting are the **opt-in Compaction API**
256
+ - **The free CLI runs the local Gateway, captures/measures traces, and compacts deterministically;
257
+ it does not perform advanced verification.** The LCM / learned optimization engine, hosted/team
258
+ control plane, verified savings, advanced proprietary evals, recommendation, advanced verification
259
+ (incl. `compact --eval`), apply guidance, and advanced policies are the **opt-in Compaction API** —
185
260
  not the free local CLI. The API is **not broadly hosted yet**.
186
261
  - **Cost figures are estimates or operator-entered records, not billing-confirmed.** Locally
187
262
  computed cost figures are price-table estimates; provider-usage records carry operator-entered/
@@ -189,14 +264,17 @@ Every figure this tool produces carries one of these labels — and nothing stro
189
264
  - **Token counts are local estimates unless provider-reported usage is captured.** The default is a
190
265
  chars/4 estimate, labeled as such. Unknown stays unknown — output tokens, provider, or cost the
191
266
  data does not carry are never inferred.
267
+ - **Gateway apply is experimental, deterministic, and opt-in.** It removes only exact-duplicate large
268
+ blocks from known-safe request shapes, retains the original locally, and fails closed on anything
269
+ else; it makes no output-token or cost claim, and never claims meaning preservation or "no context
270
+ lost". Record mode is the byte-safe default.
192
271
  - **Integrations are Level 1/2, not native/production.** Claude Code and OpenAI Agents capture are
193
272
  Level 2 local command-wrappers; Codex/local-file import is Level 1. There is **no** native/Level 3
194
- SDK instrumentation, no production proxy, no model routing, and no consumer-app support.
273
+ SDK instrumentation, **no hosted/managed proxy or model routing**, and no consumer-app support.
195
274
  - **Verification (opt-in API) is deterministic recoverability, not semantic preservation.** Where
196
275
  the opt-in API verifies a compacted context, it checks deterministic recoverability for a
197
276
  supported workflow; it does **not** evaluate meaning or task equivalence and does **not** re-run
198
- your agent through a model. Semantic / meaning preservation remains **not evaluated**; true model
199
- replay is future work.
277
+ your agent through a model. Semantic / meaning preservation remains **not evaluated**.
200
278
  - **No universal "no context is lost" guarantee.** Any apply-guidance verdict from the opt-in API
201
279
  is **fail-closed and scoped** to supported developer-agent workflows — never a universal
202
280
  guarantee, and never a semantic/meaning claim.
@@ -0,0 +1,8 @@
1
+ import { Command } from "commander";
2
+ /**
3
+ * `compaction activity` — READ-ONLY, metrics-only view of recent runs from the local activity
4
+ * store (`.compaction/activity/activity.jsonl`). Content-free: it renders only the counts/labels
5
+ * the events already carry; it never reads or prints prompt/response content, and it writes
6
+ * NOTHING. Empty store → a friendly message, exit 0.
7
+ */
8
+ export declare function registerActivityCommand(program: Command): void;
@@ -0,0 +1,35 @@
1
+ import chalk from "chalk";
2
+ import { readActivityEvents } from "../../core/activity-store.js";
3
+ import { DEFAULT_ACTIVITY_LIMIT, buildActivityJson, buildActivityRows, formatActivityTable } from "../../core/activity-view.js";
4
+ /**
5
+ * `compaction activity` — READ-ONLY, metrics-only view of recent runs from the local activity
6
+ * store (`.compaction/activity/activity.jsonl`). Content-free: it renders only the counts/labels
7
+ * the events already carry; it never reads or prints prompt/response content, and it writes
8
+ * NOTHING. Empty store → a friendly message, exit 0.
9
+ */
10
+ export function registerActivityCommand(program) {
11
+ program
12
+ .command("activity")
13
+ .description("Show recent runs recorded in the local metrics-only activity store (read-only, content-free).")
14
+ .option("--limit <n>", `Max runs to show, most recent first (default ${DEFAULT_ACTIVITY_LIMIT})`)
15
+ .option("--surface <surface>", "Only show runs from this surface (e.g. cursor, codex, cli, claude_code)")
16
+ .option("--json", "Machine-readable JSON output")
17
+ .action(async (options) => {
18
+ const limit = options.limit !== undefined ? Number.parseInt(options.limit, 10) : DEFAULT_ACTIVITY_LIMIT;
19
+ if (Number.isNaN(limit) || limit < 0) {
20
+ console.error(`Invalid --limit "${options.limit}": expected a non-negative integer.`);
21
+ process.exitCode = 1;
22
+ return;
23
+ }
24
+ const { events, skipped } = await readActivityEvents();
25
+ const rows = buildActivityRows(events, { limit, surface: options.surface });
26
+ const meta = { totalEvents: events.length, limit, surface: options.surface, skippedCount: skipped.length };
27
+ if (options.json) {
28
+ console.log(JSON.stringify(buildActivityJson(rows, meta), null, 2));
29
+ return;
30
+ }
31
+ const table = formatActivityTable(rows, meta);
32
+ console.log(rows.length === 0 ? chalk.yellow(table) : chalk.cyan(table));
33
+ });
34
+ }
35
+ //# sourceMappingURL=activity.js.map
@@ -1,11 +1,27 @@
1
1
  import path from "node:path";
2
2
  import chalk from "chalk";
3
3
  import { Option } from "commander";
4
+ import { AUTO_APPLY_QUESTION_LINES, formatSavedPreferenceConfirmation, resolveAutoApplyOffer } from "../../core/auto-apply-ask.js";
5
+ import { savePolicyPreference } from "../../core/policy-preferences.js";
4
6
  import { parseTraceFile } from "../../core/trace-parser.js";
5
7
  import { runEngineCommand } from "../engine-degrade.js";
6
8
  function defaultOutputDir(artifactRoot, traceId) {
7
9
  return path.join(artifactRoot, traceId);
8
10
  }
11
+ /**
12
+ * Infer the SAFEST workflow tool for the auto-apply preference scope. Prefers the explicit
13
+ * `--workflow-tool` flag; otherwise maps an unambiguous trace source to its tool. Returns undefined
14
+ * when it cannot infer safely (then the offer fails closed and saves nothing — never guesses global).
15
+ */
16
+ function inferWorkflowTool(explicit, traceSource) {
17
+ if (explicit && explicit.trim() !== "")
18
+ return explicit.trim();
19
+ if (traceSource === "codex_import")
20
+ return "codex";
21
+ if (traceSource === "cursor_import")
22
+ return "cursor";
23
+ return undefined;
24
+ }
9
25
  /**
10
26
  * `compaction apply-context <trace-file> --out <dir>` — LEVEL-5 IN-WORKFLOW APPLY, Form A.
11
27
  *
@@ -26,6 +42,12 @@ export function registerApplyContextCommand(program) {
26
42
  .addOption(new Option("--approve-skill-injection-policy", "EXPLICITLY also authorize the approval-required repeated_skill_injection_to_state_capsule policy. Default off; no auto-apply."))
27
43
  .addOption(new Option("--attest-used", "OPTIONAL operator attestation: AFTER an approval, the operator records that they used the approved context in a live call. Stored as operator-ATTESTED provenance (the operator's own claim), never observed by compaction.dev."))
28
44
  .option("--attest-note <text>", "Optional free-text note recorded with the operator attestation")
45
+ .addOption(new Option("--offer-auto-apply", "OPT-IN. AFTER an explicit approval, offer the ONE binary question 'apply this automatically next time when safety gates pass?' (default no). Saving a preference records intent only — it NEVER applies anything (no auto-apply engine exists)."))
46
+ .addOption(new Option("--remember-auto-apply", "Non-interactive answer YES to the auto-apply offer (requires --offer-auto-apply and a prior approval). Saves a preference for the inferred safest scope; applies nothing now."))
47
+ .addOption(new Option("--no-remember", "Non-interactive answer NO to the auto-apply offer (the default): save nothing, keep asking each time."))
48
+ .option("--workflow-tool <tool>", "Tool/workflow to scope a saved auto-apply preference to (never global/cross-tool). Inferred from the trace source when omitted.")
49
+ .option("--workflow-repo <repo>", "Optional content-free repo identifier to narrow a saved auto-apply preference's scope.")
50
+ .option("--policy-type <type>", "Policy type a saved auto-apply preference concerns (defaults to the approved policy name).")
29
51
  .action(async (traceFile, options) => {
30
52
  await runEngineCommand(async () => {
31
53
  // Lazy-load the proprietary engine: present in-repo/API, excluded from the public package.
@@ -70,6 +92,52 @@ export function registerApplyContextCommand(program) {
70
92
  }
71
93
  }
72
94
  console.log(chalk.green(`Output directory: ${result.outputDirectory}`));
95
+ // OPT-IN post-approval auto-apply OFFER (§11a block 4/5). This offer is made ONLY after an
96
+ // explicit in-workflow approval already emitted an approved context. It APPLIES NOTHING: on
97
+ // yes it records a PREFERENCE (intent for the future gated slice) via the #598 store; on
98
+ // no/default it saves nothing. It never touches apply execution semantics.
99
+ if (options.offerAutoApply === true && result.approvedContextEmitted) {
100
+ console.log("");
101
+ for (const line of AUTO_APPLY_QUESTION_LINES)
102
+ console.log(chalk.cyan(line));
103
+ const answer = options.rememberAutoApply === true ? "yes" : "no";
104
+ const decision = resolveAutoApplyOffer({
105
+ approvedInWorkflow: true,
106
+ answer,
107
+ scope: {
108
+ tool: inferWorkflowTool(options.workflowTool, trace.source),
109
+ repo: options.workflowRepo,
110
+ policyType: options.policyType ?? result.review.policies_applied[0]
111
+ }
112
+ });
113
+ if (decision.action === "save") {
114
+ const saved = await savePolicyPreference({
115
+ scope: decision.scope,
116
+ preference: decision.preference,
117
+ gates_required: decision.gatesRequired
118
+ });
119
+ if (saved.saved) {
120
+ console.log("");
121
+ console.log(chalk.green(formatSavedPreferenceConfirmation(saved.preference)));
122
+ }
123
+ else {
124
+ console.log("");
125
+ console.log(chalk.yellow("Preference NOT saved (fail-closed) — nothing was applied:"));
126
+ for (const problem of saved.problems)
127
+ console.log(chalk.yellow(` - ${problem}`));
128
+ }
129
+ }
130
+ else if (decision.action === "blocked") {
131
+ console.log("");
132
+ console.log(chalk.yellow("Preference NOT saved — could not infer a safe (non-global, non-cross-tool) scope. Nothing applied:"));
133
+ for (const problem of decision.problems)
134
+ console.log(chalk.yellow(` - ${problem}`));
135
+ console.log(chalk.dim(" Pass --workflow-tool <tool> (and optionally --policy-type / --workflow-repo) to record a preference."));
136
+ }
137
+ else {
138
+ console.log(chalk.dim("No auto-apply preference saved — Compaction will keep asking each time (the default)."));
139
+ }
140
+ }
73
141
  // Exit non-zero when no approved context was emitted (refusal / review-only), mirroring apply/approve.
74
142
  if (!result.approvedContextEmitted) {
75
143
  process.exitCode = 1;
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import chalk from "chalk";
4
4
  import { writeJsonArtifact, writeTextArtifact } from "../../core/artifact-writer.js";
5
- import { addRun, computeReport, initMeasurementRecord, BILLING_DELTA_CRITERION, BILLING_DELTA_RECORD_ROOT } from "../../core/billing-delta/billing-delta-record.js";
5
+ import { addRun, computeReport, initMeasurementRecord, API_METERED_BILLING_CONFIRMED_CRITERIA, BILLING_DELTA_CRITERION, BILLING_DELTA_FIXED_PLAN_NOTE, BILLING_DELTA_RECORD_ROOT } from "../../core/billing-delta/billing-delta-record.js";
6
6
  const PRIVACY_NOTE = "Operator-run, local-first. compaction.dev makes NO provider call, reads NO credential, and has NO network access. " +
7
7
  "All billing figures are operator-entered (read from YOUR provider console/billing export) and are NOT provider-verified by this tool. " +
8
8
  "Aggregate cost only — no prompt/completion content is stored.";
@@ -81,6 +81,17 @@ function buildReportMarkdown(report) {
81
81
  "",
82
82
  `- ${report.criterion}`,
83
83
  "",
84
+ "## API-metered billing-confirmed criteria (cross-surface contract §5, D6 — checklist only)",
85
+ "",
86
+ "> A billing-confirmed figure is possible ONLY on api_metered workflows, ONLY through this",
87
+ "> billing-delta flow, and ONLY when ALL of the criteria below hold. This report does NOT verify",
88
+ "> them and NEVER carries billing_confirmed: true. Even when all criteria hold, the claim is",
89
+ "> workflow-scoped only — generalized billing-confirmed = future only.",
90
+ ">",
91
+ `> ${BILLING_DELTA_FIXED_PLAN_NOTE}`,
92
+ "",
93
+ ...API_METERED_BILLING_CONFIRMED_CRITERIA.map((c, i) => `${i + 1}. ${c}`),
94
+ "",
84
95
  "## Verdict notes",
85
96
  "",
86
97
  ...report.verdict_notes.map((n) => `- ${n}`),
@@ -1,3 +1,4 @@
1
+ import type { UsageMetadata } from "../../core/usage-metadata.js";
1
2
  interface DiscoverClaudeCodeOptions {
2
3
  projectsDir?: string;
3
4
  }
@@ -8,6 +9,49 @@ interface CaptureClaudeCodeOptions {
8
9
  includeSubagents?: boolean;
9
10
  }
10
11
  export declare function captureClaudeCodeCommand(options: CaptureClaudeCodeOptions): Promise<void>;
12
+ /** A normalized usage summary for the hook path — usage + a content-free fingerprint + message count. */
13
+ interface HookNormalizeResult {
14
+ usage: UsageMetadata;
15
+ messageCount: number;
16
+ fingerprint?: string;
17
+ }
18
+ export interface FromHookDeps {
19
+ readStdin?: () => Promise<string>;
20
+ normalize?: (transcriptPath: string) => Promise<HookNormalizeResult>;
21
+ cwd?: string;
22
+ now?: () => string;
23
+ hostedConfigured?: () => boolean;
24
+ }
25
+ export interface FromHookOptions {
26
+ dryRun?: boolean;
27
+ }
28
+ /**
29
+ * `compaction capture claude-code --from-hook`. Reads the Stop payload from stdin, resolves the session
30
+ * JSONL via `transcript_path`, and records CONTENT-FREE usage only — never message content, never
31
+ * `last_assistant_message`. Idempotent via a content-free dedup ledger. FAIL-OPEN by contract: any error is
32
+ * swallowed and the process exits 0 so the hook can never break Claude Code.
33
+ */
34
+ export declare function captureClaudeCodeFromHook(options?: FromHookOptions, deps?: FromHookDeps): Promise<void>;
35
+ export interface FromPromptHookDeps {
36
+ readStdin?: () => Promise<string>;
37
+ cwd?: string;
38
+ }
39
+ /**
40
+ * `compaction capture claude-code --from-prompt-hook` — the BEFORE-CALL recommendation path (charter
41
+ * 2026-07-04-run-13). The Claude Code **UserPromptSubmit** hook runs this BEFORE the model processes the
42
+ * prompt (a genuine pre-call event — NOT the post-session Stop hook). It reads the UserPromptSubmit
43
+ * payload from stdin, analyzes the pending PROMPT for avoidable duplicated context IN-PROCESS
44
+ * (content-free), and — when present — records ONE metrics-only `claude_code` before-call RECOMMENDATION
45
+ * activity event so it appears in `compaction activity`. Apply is a PROVEN BLOCKER on this surface (no
46
+ * hook can reduce the model's context; hooks are non-interactive) → recommendation-only; the original
47
+ * prompt ALWAYS runs UNCHANGED.
48
+ *
49
+ * CRITICAL: this path writes NOTHING to stdout — on UserPromptSubmit, a hook's stdout is ADDED to the
50
+ * model's context, so injecting anything would be both intrusive and self-defeating for a compaction
51
+ * tool. It is silent and transparent; the recommendation lives in `compaction activity`. FAIL-OPEN +
52
+ * CONTENT-FREE: any error is swallowed and the process exits 0; the prompt text is never persisted.
53
+ */
54
+ export declare function captureClaudeCodeFromPromptHook(deps?: FromPromptHookDeps): Promise<void>;
11
55
  /**
12
56
  * Discover local Claude Code sessions (metadata only) and print, for each, the
13
57
  * ready-to-run `capture claude-code --session <path>` command (guided capture).