@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.
Files changed (143) hide show
  1. package/LICENSE +663 -0
  2. package/README.md +319 -0
  3. package/dist/cli/commands/adapter.d.ts +2 -0
  4. package/dist/cli/commands/adapter.js +49 -0
  5. package/dist/cli/commands/aggregate.d.ts +2 -0
  6. package/dist/cli/commands/aggregate.js +60 -0
  7. package/dist/cli/commands/analyze.d.ts +2 -0
  8. package/dist/cli/commands/analyze.js +55 -0
  9. package/dist/cli/commands/apply-context.d.ts +12 -0
  10. package/dist/cli/commands/apply-context.js +80 -0
  11. package/dist/cli/commands/apply.d.ts +2 -0
  12. package/dist/cli/commands/apply.js +65 -0
  13. package/dist/cli/commands/approve.d.ts +2 -0
  14. package/dist/cli/commands/approve.js +55 -0
  15. package/dist/cli/commands/audit.d.ts +2 -0
  16. package/dist/cli/commands/audit.js +63 -0
  17. package/dist/cli/commands/billing-delta.d.ts +2 -0
  18. package/dist/cli/commands/billing-delta.js +246 -0
  19. package/dist/cli/commands/capture-claude-code.d.ts +19 -0
  20. package/dist/cli/commands/capture-claude-code.js +198 -0
  21. package/dist/cli/commands/capture-provider-usage.d.ts +35 -0
  22. package/dist/cli/commands/capture-provider-usage.js +113 -0
  23. package/dist/cli/commands/capture.d.ts +2 -0
  24. package/dist/cli/commands/capture.js +76 -0
  25. package/dist/cli/commands/compact.d.ts +2 -0
  26. package/dist/cli/commands/compact.js +206 -0
  27. package/dist/cli/commands/eval.d.ts +2 -0
  28. package/dist/cli/commands/eval.js +66 -0
  29. package/dist/cli/commands/export-session-seed.d.ts +11 -0
  30. package/dist/cli/commands/export-session-seed.js +42 -0
  31. package/dist/cli/commands/feedback.d.ts +2 -0
  32. package/dist/cli/commands/feedback.js +233 -0
  33. package/dist/cli/commands/import.d.ts +2 -0
  34. package/dist/cli/commands/import.js +60 -0
  35. package/dist/cli/commands/init.d.ts +2 -0
  36. package/dist/cli/commands/init.js +216 -0
  37. package/dist/cli/commands/integrations.d.ts +2 -0
  38. package/dist/cli/commands/integrations.js +29 -0
  39. package/dist/cli/commands/launch-treatment-session.d.ts +14 -0
  40. package/dist/cli/commands/launch-treatment-session.js +50 -0
  41. package/dist/cli/commands/optimize.d.ts +2 -0
  42. package/dist/cli/commands/optimize.js +33 -0
  43. package/dist/cli/commands/recommend.d.ts +4 -0
  44. package/dist/cli/commands/recommend.js +66 -0
  45. package/dist/cli/commands/review.d.ts +2 -0
  46. package/dist/cli/commands/review.js +60 -0
  47. package/dist/cli/commands/run.d.ts +2 -0
  48. package/dist/cli/commands/run.js +49 -0
  49. package/dist/cli/commands/spend.d.ts +2 -0
  50. package/dist/cli/commands/spend.js +24 -0
  51. package/dist/cli/commands/summary.d.ts +2 -0
  52. package/dist/cli/commands/summary.js +19 -0
  53. package/dist/cli/engine-degrade.d.ts +40 -0
  54. package/dist/cli/engine-degrade.js +59 -0
  55. package/dist/cli/index.d.ts +2 -0
  56. package/dist/cli/index.js +59 -0
  57. package/dist/core/adapter-validation.d.ts +32 -0
  58. package/dist/core/adapter-validation.js +185 -0
  59. package/dist/core/adapters/claude-code-adapter.d.ts +51 -0
  60. package/dist/core/adapters/claude-code-adapter.js +539 -0
  61. package/dist/core/adapters/claude-code-discovery.d.ts +40 -0
  62. package/dist/core/adapters/claude-code-discovery.js +100 -0
  63. package/dist/core/aggregate-format.d.ts +62 -0
  64. package/dist/core/aggregate-format.js +349 -0
  65. package/dist/core/api-client/client.d.ts +43 -0
  66. package/dist/core/api-client/client.js +143 -0
  67. package/dist/core/api-client/config.d.ts +36 -0
  68. package/dist/core/api-client/config.js +44 -0
  69. package/dist/core/api-client/index.d.ts +13 -0
  70. package/dist/core/api-client/index.js +14 -0
  71. package/dist/core/api-client/payload.d.ts +93 -0
  72. package/dist/core/api-client/payload.js +180 -0
  73. package/dist/core/api-client/types.d.ts +95 -0
  74. package/dist/core/api-client/types.js +21 -0
  75. package/dist/core/artifact-writer.d.ts +2 -0
  76. package/dist/core/artifact-writer.js +14 -0
  77. package/dist/core/audit-report.d.ts +91 -0
  78. package/dist/core/audit-report.js +422 -0
  79. package/dist/core/billing-delta/billing-delta-record.d.ts +290 -0
  80. package/dist/core/billing-delta/billing-delta-record.js +394 -0
  81. package/dist/core/capture-adapter.d.ts +74 -0
  82. package/dist/core/capture-adapter.js +2 -0
  83. package/dist/core/command-runner.d.ts +29 -0
  84. package/dist/core/command-runner.js +96 -0
  85. package/dist/core/content-hash.d.ts +1 -0
  86. package/dist/core/content-hash.js +5 -0
  87. package/dist/core/cost-calculator.d.ts +6 -0
  88. package/dist/core/cost-calculator.js +20 -0
  89. package/dist/core/feedback-bundle.d.ts +159 -0
  90. package/dist/core/feedback-bundle.js +406 -0
  91. package/dist/core/openai-agents-capture.d.ts +66 -0
  92. package/dist/core/openai-agents-capture.js +475 -0
  93. package/dist/core/policy-types.d.ts +85 -0
  94. package/dist/core/policy-types.js +29 -0
  95. package/dist/core/pricing.d.ts +23 -0
  96. package/dist/core/pricing.js +55 -0
  97. package/dist/core/provider-usage/credential-redaction.d.ts +23 -0
  98. package/dist/core/provider-usage/credential-redaction.js +60 -0
  99. package/dist/core/provider-usage/provider-usage-adapter.d.ts +65 -0
  100. package/dist/core/provider-usage/provider-usage-adapter.js +147 -0
  101. package/dist/core/provider-usage/provider-usage-client.d.ts +89 -0
  102. package/dist/core/provider-usage/provider-usage-client.js +96 -0
  103. package/dist/core/report-generator.d.ts +7 -0
  104. package/dist/core/report-generator.js +275 -0
  105. package/dist/core/run-aggregator.d.ts +50 -0
  106. package/dist/core/run-aggregator.js +251 -0
  107. package/dist/core/run-labels.d.ts +52 -0
  108. package/dist/core/run-labels.js +80 -0
  109. package/dist/core/run-trace-converter.d.ts +3 -0
  110. package/dist/core/run-trace-converter.js +94 -0
  111. package/dist/core/safety-report.d.ts +52 -0
  112. package/dist/core/safety-report.js +440 -0
  113. package/dist/core/savings.d.ts +22 -0
  114. package/dist/core/savings.js +36 -0
  115. package/dist/core/session-aggregate.d.ts +211 -0
  116. package/dist/core/session-aggregate.js +490 -0
  117. package/dist/core/skill-injection-detector.d.ts +51 -0
  118. package/dist/core/skill-injection-detector.js +146 -0
  119. package/dist/core/spend-attribution.d.ts +169 -0
  120. package/dist/core/spend-attribution.js +611 -0
  121. package/dist/core/standalone-capsule-provenance.d.ts +4 -0
  122. package/dist/core/standalone-capsule-provenance.js +62 -0
  123. package/dist/core/state-capsule.d.ts +3 -0
  124. package/dist/core/state-capsule.js +147 -0
  125. package/dist/core/token-accounting.d.ts +62 -0
  126. package/dist/core/token-accounting.js +81 -0
  127. package/dist/core/token-estimator.d.ts +3 -0
  128. package/dist/core/token-estimator.js +18 -0
  129. package/dist/core/trace-adapters.d.ts +53 -0
  130. package/dist/core/trace-adapters.js +714 -0
  131. package/dist/core/trace-fingerprint.d.ts +79 -0
  132. package/dist/core/trace-fingerprint.js +102 -0
  133. package/dist/core/trace-intake.d.ts +41 -0
  134. package/dist/core/trace-intake.js +164 -0
  135. package/dist/core/trace-parser.d.ts +203 -0
  136. package/dist/core/trace-parser.js +143 -0
  137. package/dist/core/types.d.ts +238 -0
  138. package/dist/core/types.js +2 -0
  139. package/dist/core/usage-metadata.d.ts +51 -0
  140. package/dist/core/usage-metadata.js +122 -0
  141. package/dist/core/waste-detector.d.ts +8 -0
  142. package/dist/core/waste-detector.js +58 -0
  143. package/package.json +90 -0
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Compaction API client — configuration (PUBLIC CLI/SDK code).
3
+ *
4
+ * Reads env ONLY. Persists NOTHING. The default URL is the LOCAL-DEV server
5
+ * (`127.0.0.1`); there is no remote URL baked into the public package. The API key
6
+ * (when set) is attached as a bearer token at call time and is NEVER persisted, logged,
7
+ * or printed. See `docs/ops/cli-api-client-design.md` §1.
8
+ */
9
+ /** Local-dev default. Deliberately NOT a remote URL — a fresh checkout talks to a LOCAL server. */
10
+ export const DEFAULT_API_URL = "http://127.0.0.1:8787";
11
+ /** Default per-request timeout. Override with `COMPACTION_API_TIMEOUT_MS`. */
12
+ export const DEFAULT_TIMEOUT_MS = 30_000;
13
+ function stripTrailingSlash(url) {
14
+ return url.endsWith("/") ? url.slice(0, -1) : url;
15
+ }
16
+ /**
17
+ * Resolve API config from the environment. Pure: same env in → same config out. Never reads
18
+ * or writes any file and never mutates the env.
19
+ *
20
+ * - `COMPACTION_API_URL` → base URL (default LOCAL `127.0.0.1:8787`).
21
+ * - `COMPACTION_API_KEY` → optional bearer token (unset by default).
22
+ * - `COMPACTION_API_TIMEOUT_MS` → optional positive integer timeout.
23
+ */
24
+ export function resolveApiConfig(env = process.env) {
25
+ const rawUrl = (env.COMPACTION_API_URL ?? "").trim();
26
+ const url = stripTrailingSlash(rawUrl === "" ? DEFAULT_API_URL : rawUrl);
27
+ const rawKey = (env.COMPACTION_API_KEY ?? "").trim();
28
+ const apiKey = rawKey === "" ? undefined : rawKey;
29
+ const rawTimeout = (env.COMPACTION_API_TIMEOUT_MS ?? "").trim();
30
+ const parsedTimeout = Number.parseInt(rawTimeout, 10);
31
+ const timeoutMs = Number.isFinite(parsedTimeout) && parsedTimeout > 0 ? parsedTimeout : DEFAULT_TIMEOUT_MS;
32
+ return { url, apiKey, timeoutMs };
33
+ }
34
+ /**
35
+ * Build request headers. The API key (when present) is attached as a bearer token. Returns a
36
+ * fresh object each call; the key is never stored elsewhere.
37
+ */
38
+ export function buildHeaders(config) {
39
+ const headers = { "content-type": "application/json" };
40
+ if (config.apiKey)
41
+ headers.authorization = `Bearer ${config.apiKey}`;
42
+ return headers;
43
+ }
44
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Compaction API client — public surface (PUBLIC CLI/SDK code).
3
+ *
4
+ * Reference implementation of `docs/ops/cli-api-client-design.md` (Track E). It is the typed
5
+ * boundary the public CLI uses to talk to the private Compaction API over the documented HTTP
6
+ * contract ONLY. It imports NOTHING from `src/engine`, makes NO call at import time, and never
7
+ * auto-uploads. It is NOT wired into any free command; an API call happens only when a caller
8
+ * explicitly invokes `apiStatus` / `sendRequest`.
9
+ */
10
+ export { DEFAULT_API_URL, DEFAULT_TIMEOUT_MS, resolveApiConfig, buildHeaders, type ApiConfig, type EnvLike } from "./config.js";
11
+ export { isContentBearing, ConsentError, buildOptimizeRequest, buildEvaluateRequest, buildReportsRequest, previewPayload, type OptimizeBuildOptions, type EvaluateBuildOptions, type ReportsBuildOptions, type PayloadPreview } from "./payload.js";
12
+ export { apiStatus, sendRequest, ApiNotConfirmedError, ApiTransportError, type ApiResponse, type SendOptions } from "./client.js";
13
+ export { CONTENT_BEARING_PAYLOAD_CLASSES, type PayloadClass, type TokenSource, type Consent, type Trace, type TokenUsage, type OptimizeRequest, type EvaluateRequest, type ReportsRequest, type ApiRequestBody, type StatusResponse, type ApiErrorBody } from "./types.js";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Compaction API client — public surface (PUBLIC CLI/SDK code).
3
+ *
4
+ * Reference implementation of `docs/ops/cli-api-client-design.md` (Track E). It is the typed
5
+ * boundary the public CLI uses to talk to the private Compaction API over the documented HTTP
6
+ * contract ONLY. It imports NOTHING from `src/engine`, makes NO call at import time, and never
7
+ * auto-uploads. It is NOT wired into any free command; an API call happens only when a caller
8
+ * explicitly invokes `apiStatus` / `sendRequest`.
9
+ */
10
+ export { DEFAULT_API_URL, DEFAULT_TIMEOUT_MS, resolveApiConfig, buildHeaders } from "./config.js";
11
+ export { isContentBearing, ConsentError, buildOptimizeRequest, buildEvaluateRequest, buildReportsRequest, previewPayload } from "./payload.js";
12
+ export { apiStatus, sendRequest, ApiNotConfirmedError, ApiTransportError } from "./client.js";
13
+ export { CONTENT_BEARING_PAYLOAD_CLASSES } from "./types.js";
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Compaction API client — payload builders + preview (PUBLIC CLI/SDK code).
3
+ *
4
+ * Pure functions: artifact + options → a typed request body. NO network, NO engine import.
5
+ * Builders fail CLOSED: a content-bearing payload class requires explicit upload consent, and
6
+ * inline raw (non-redacted) content requires consent too — mirroring the server's
7
+ * `enforceConsent`. The preview shows EXACTLY what would be sent (API key redacted), so the
8
+ * user never sends blind. See `docs/ops/cli-api-client-design.md` §3–§5.
9
+ */
10
+ import { type EvaluateRequest, type OptimizeRequest, type PayloadClass, type ReportsRequest, type Trace } from "./types.js";
11
+ /** True when a class carries raw-or-sanitized message CONTENT (requires explicit consent). */
12
+ export declare function isContentBearing(payloadClass: PayloadClass | undefined): boolean;
13
+ /** Thrown by a builder when consent rules are violated. The client must fail closed. */
14
+ export declare class ConsentError extends Error {
15
+ constructor(message: string);
16
+ }
17
+ /**
18
+ * Inspect a built/hand-built request body and report whether it ACTUALLY carries inline raw
19
+ * (non-redacted) content in ANY content-bearing field — independent of the declared
20
+ * `payload_class`. This is the source of truth the client uses to fail closed: the consent gate
21
+ * must be driven by what the body CONTAINS, not by its label. Content is considered "raw" (and
22
+ * therefore consent-requiring) unless it is explicitly marked redacted on its own trace.
23
+ *
24
+ * Content-bearing fields across the v0 contract:
25
+ * - `trace.content` (optimize)
26
+ * - `original_trace.content` (evaluate)
27
+ * - `compacted_context.content`(evaluate — trace-derived)
28
+ */
29
+ export declare function bodyContainsInlineContent(body: Record<string, unknown>): boolean;
30
+ export interface OptimizeBuildOptions {
31
+ trace: Trace;
32
+ uploadPermitted?: boolean;
33
+ payloadClass?: PayloadClass;
34
+ tokenSource?: OptimizeRequest["token_usage"]["source"];
35
+ inputTokens?: number;
36
+ outputTokens?: number;
37
+ provider?: string;
38
+ runtime?: string;
39
+ labels?: {
40
+ project?: string;
41
+ session?: string;
42
+ };
43
+ optimizationMode?: OptimizeRequest["optimization_mode"];
44
+ }
45
+ /** Build a `POST /v0/optimize` body. Fails closed on a content-bearing class without consent. */
46
+ export declare function buildOptimizeRequest(opts: OptimizeBuildOptions): OptimizeRequest;
47
+ export interface EvaluateBuildOptions {
48
+ originalTrace: Trace;
49
+ compactedContext: {
50
+ content?: Record<string, unknown>;
51
+ reference?: string;
52
+ };
53
+ uploadPermitted?: boolean;
54
+ payloadClass?: PayloadClass;
55
+ evidenceRequirements?: EvaluateRequest["evidence_requirements"];
56
+ }
57
+ /** Build a `POST /v0/evaluate` body. Fails closed on a content-bearing class without consent. */
58
+ export declare function buildEvaluateRequest(opts: EvaluateBuildOptions): EvaluateRequest;
59
+ export interface ReportsBuildOptions {
60
+ reportBundle: ReportsRequest["report_bundle"];
61
+ uploadPermitted?: boolean;
62
+ payloadClass?: PayloadClass;
63
+ labels?: {
64
+ team?: string;
65
+ project?: string;
66
+ };
67
+ }
68
+ /** Build a `POST /v0/reports` body. A report bundle is metrics/structure; consent still applies if a content-bearing class is declared. */
69
+ export declare function buildReportsRequest(opts: ReportsBuildOptions): ReportsRequest;
70
+ export interface PayloadPreview {
71
+ /** The HTTP method + path that would be hit. */
72
+ endpoint: string;
73
+ /** The resolved base URL the request would go to. */
74
+ url: string;
75
+ /** The declared payload class. */
76
+ payloadClass: PayloadClass;
77
+ /** Whether the body carries message content. */
78
+ contentIncluded: boolean;
79
+ /** A short human-readable description of what is / is NOT included. */
80
+ summary: string;
81
+ /** The EXACT JSON body that would be sent, with any API key redacted. */
82
+ body: Record<string, unknown>;
83
+ /** On-wire byte size of the serialized body. */
84
+ byteSize: number;
85
+ }
86
+ /**
87
+ * Produce a payload preview: exactly what would be sent (the API key is NOT part of the body
88
+ * and never appears here), plus a plain-language summary and byte size. This is the contract
89
+ * the user sees before any send and is identical to the real body.
90
+ */
91
+ export declare function previewPayload(method: string, path: string, url: string, body: {
92
+ payload_class?: PayloadClass;
93
+ } & Record<string, unknown>): PayloadPreview;
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Compaction API client — payload builders + preview (PUBLIC CLI/SDK code).
3
+ *
4
+ * Pure functions: artifact + options → a typed request body. NO network, NO engine import.
5
+ * Builders fail CLOSED: a content-bearing payload class requires explicit upload consent, and
6
+ * inline raw (non-redacted) content requires consent too — mirroring the server's
7
+ * `enforceConsent`. The preview shows EXACTLY what would be sent (API key redacted), so the
8
+ * user never sends blind. See `docs/ops/cli-api-client-design.md` §3–§5.
9
+ */
10
+ import { CONTENT_BEARING_PAYLOAD_CLASSES } from "./types.js";
11
+ /** True when a class carries raw-or-sanitized message CONTENT (requires explicit consent). */
12
+ export function isContentBearing(payloadClass) {
13
+ return payloadClass !== undefined && CONTENT_BEARING_PAYLOAD_CLASSES.has(payloadClass);
14
+ }
15
+ /** Thrown by a builder when consent rules are violated. The client must fail closed. */
16
+ export class ConsentError extends Error {
17
+ constructor(message) {
18
+ super(message);
19
+ this.name = "ConsentError";
20
+ }
21
+ }
22
+ /** True when an inline-content field is present and NON-empty (i.e. actually carries content). */
23
+ function hasInlineContent(content) {
24
+ return (typeof content === "object" &&
25
+ content !== null &&
26
+ !Array.isArray(content) &&
27
+ Object.keys(content).length > 0);
28
+ }
29
+ /**
30
+ * Inspect a built/hand-built request body and report whether it ACTUALLY carries inline raw
31
+ * (non-redacted) content in ANY content-bearing field — independent of the declared
32
+ * `payload_class`. This is the source of truth the client uses to fail closed: the consent gate
33
+ * must be driven by what the body CONTAINS, not by its label. Content is considered "raw" (and
34
+ * therefore consent-requiring) unless it is explicitly marked redacted on its own trace.
35
+ *
36
+ * Content-bearing fields across the v0 contract:
37
+ * - `trace.content` (optimize)
38
+ * - `original_trace.content` (evaluate)
39
+ * - `compacted_context.content`(evaluate — trace-derived)
40
+ */
41
+ export function bodyContainsInlineContent(body) {
42
+ const trace = body.trace;
43
+ if (trace && hasInlineContent(trace.content) && trace.redacted !== true)
44
+ return true;
45
+ const originalTrace = body.original_trace;
46
+ if (originalTrace && hasInlineContent(originalTrace.content) && originalTrace.redacted !== true) {
47
+ return true;
48
+ }
49
+ // Compacted context is trace-derived; it carries no independent `redacted` flag, so any inline
50
+ // content here counts as raw trace-derived content for consent purposes.
51
+ const compacted = body.compacted_context;
52
+ if (compacted && hasInlineContent(compacted.content))
53
+ return true;
54
+ return false;
55
+ }
56
+ /**
57
+ * Shared consent enforcement (client-side mirror of `apps/api` `enforceConsent`). Fail closed:
58
+ * - a content-bearing `payload_class` requires `consent.upload_permitted === true`;
59
+ * - inline raw (non-redacted) `trace.content` requires consent too.
60
+ * Content-free classes / redacted inline content / reference-only payloads need no consent.
61
+ */
62
+ function assertConsent(consent, payloadClass, trace) {
63
+ const uploadPermitted = consent.upload_permitted === true;
64
+ if (isContentBearing(payloadClass) && !uploadPermitted) {
65
+ throw new ConsentError(`consent_required: payload_class "${payloadClass}" carries content and may not be ` +
66
+ "sent unless upload is explicitly permitted. Use a metrics_only/redacted_structure " +
67
+ "payload, or pass the upload-permitted flag.");
68
+ }
69
+ if (trace && trace.content !== undefined && trace.redacted !== true && !uploadPermitted) {
70
+ throw new ConsentError("consent_required: inline raw trace.content may not be sent unless upload is explicitly " +
71
+ "permitted (or the trace is redacted). Send a redacted trace or a reference instead.");
72
+ }
73
+ }
74
+ /** Build a `POST /v0/optimize` body. Fails closed on a content-bearing class without consent. */
75
+ export function buildOptimizeRequest(opts) {
76
+ const payloadClass = opts.payloadClass ?? "metrics_only";
77
+ const consent = {
78
+ upload_permitted: opts.uploadPermitted === true,
79
+ redacted: opts.trace.redacted === true
80
+ };
81
+ // For content-free classes, NEVER attach inline content even if the caller passed it.
82
+ const trace = isContentBearing(payloadClass)
83
+ ? opts.trace
84
+ : { redacted: opts.trace.redacted, reference: opts.trace.reference };
85
+ assertConsent(consent, payloadClass, trace);
86
+ const req = {
87
+ trace,
88
+ consent,
89
+ payload_class: payloadClass,
90
+ token_usage: {
91
+ source: opts.tokenSource ?? "local-estimate",
92
+ ...(opts.inputTokens !== undefined ? { input_tokens: opts.inputTokens } : {}),
93
+ ...(opts.outputTokens !== undefined ? { output_tokens: opts.outputTokens } : {})
94
+ },
95
+ optimization_mode: opts.optimizationMode ?? "recommend"
96
+ };
97
+ if (opts.provider || opts.runtime) {
98
+ req.provider_metadata = {
99
+ ...(opts.provider ? { provider: opts.provider } : {}),
100
+ ...(opts.runtime ? { runtime: opts.runtime } : {})
101
+ };
102
+ }
103
+ if (opts.labels)
104
+ req.labels = opts.labels;
105
+ return req;
106
+ }
107
+ /** Build a `POST /v0/evaluate` body. Fails closed on a content-bearing class without consent. */
108
+ export function buildEvaluateRequest(opts) {
109
+ const payloadClass = opts.payloadClass ?? "metrics_only";
110
+ const consent = {
111
+ upload_permitted: opts.uploadPermitted === true,
112
+ redacted: opts.originalTrace.redacted === true
113
+ };
114
+ const contentBearing = isContentBearing(payloadClass);
115
+ // For content-free classes, NEVER attach inline content from ANY source — not the original
116
+ // trace AND not the compacted context (which is trace-derived). A content-free class must
117
+ // carry zero inline content regardless of which field the caller populated. Strip everything
118
+ // except references, so the declared class is TRUE to the actual body.
119
+ const originalTrace = contentBearing
120
+ ? opts.originalTrace
121
+ : { redacted: opts.originalTrace.redacted, reference: opts.originalTrace.reference };
122
+ const compactedContext = contentBearing
123
+ ? opts.compactedContext
124
+ : { reference: opts.compactedContext.reference };
125
+ assertConsent(consent, payloadClass, originalTrace);
126
+ const req = {
127
+ original_trace: originalTrace,
128
+ compacted_context: compactedContext,
129
+ consent,
130
+ payload_class: payloadClass
131
+ };
132
+ if (opts.evidenceRequirements)
133
+ req.evidence_requirements = opts.evidenceRequirements;
134
+ return req;
135
+ }
136
+ /** Build a `POST /v0/reports` body. A report bundle is metrics/structure; consent still applies if a content-bearing class is declared. */
137
+ export function buildReportsRequest(opts) {
138
+ const payloadClass = opts.payloadClass ?? "metrics_only";
139
+ const consent = {
140
+ upload_permitted: opts.uploadPermitted === true,
141
+ redacted: opts.reportBundle.redacted === true
142
+ };
143
+ assertConsent(consent, payloadClass, undefined);
144
+ const req = {
145
+ report_bundle: opts.reportBundle,
146
+ consent,
147
+ payload_class: payloadClass
148
+ };
149
+ if (opts.labels)
150
+ req.labels = opts.labels;
151
+ return req;
152
+ }
153
+ const CONTENT_SUMMARY = {
154
+ metrics_only: "metrics only — NO message content",
155
+ redacted_structure: "structure/roles/ids — message content REMOVED",
156
+ sanitized_snippets: "sanitized snippets — partial message CONTENT included",
157
+ full_trace: "FULL message content included"
158
+ };
159
+ /**
160
+ * Produce a payload preview: exactly what would be sent (the API key is NOT part of the body
161
+ * and never appears here), plus a plain-language summary and byte size. This is the contract
162
+ * the user sees before any send and is identical to the real body.
163
+ */
164
+ export function previewPayload(method, path, url, body) {
165
+ const payloadClass = (body.payload_class ?? "metrics_only");
166
+ const serialized = JSON.stringify(body);
167
+ return {
168
+ endpoint: `${method} ${path}`,
169
+ url,
170
+ payloadClass,
171
+ // Report what the body ACTUALLY carries, not just its declared class — a
172
+ // mislabeled content-free body that still contains inline content must preview
173
+ // as content-included (and will be rejected by sendRequest before any fetch).
174
+ contentIncluded: isContentBearing(payloadClass) || bodyContainsInlineContent(body),
175
+ summary: CONTENT_SUMMARY[payloadClass],
176
+ body,
177
+ byteSize: Buffer.byteLength(serialized, "utf8")
178
+ };
179
+ }
180
+ //# sourceMappingURL=payload.js.map
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Compaction API client — wire types (PUBLIC CLI/SDK code).
3
+ *
4
+ * OPEN-CORE BOUNDARY: this module is part of the PUBLIC `@compaction/cli` package and
5
+ * ships in the published tarball. It MUST NOT import anything from `src/engine` and MUST
6
+ * NOT import the private `apps/api` package. It mirrors — by hand, with no cross-package
7
+ * import — the request/response SHAPES defined in `apps/api/src/schemas.ts` and the contract
8
+ * in `docs/api/compaction-api-v0.md`. The client speaks only the documented HTTP contract.
9
+ *
10
+ * Keeping these types LOCAL (rather than importing the `apps/api` zod schemas) is deliberate:
11
+ * it guarantees the public package has zero build/runtime coupling to the server package or
12
+ * the engine. The local-stub tests assert the shapes still line up with the live contract.
13
+ *
14
+ * See `docs/ops/cli-api-client-design.md` (Track E) and `docs/ops/api-command-ux.md` (Track F).
15
+ */
16
+ /** Payload classes (mirror of `apps/api` `PayloadClassSchema`). */
17
+ export type PayloadClass = "metrics_only" | "redacted_structure" | "sanitized_snippets" | "full_trace";
18
+ /** Classes that carry raw-or-sanitized CONTENT and therefore require explicit upload consent. */
19
+ export declare const CONTENT_BEARING_PAYLOAD_CLASSES: ReadonlySet<PayloadClass>;
20
+ /** Honest token-source labels (mirror of `apps/api` `TokenSourceSchema`). Never upgraded. */
21
+ export type TokenSource = "provider-reported" | "local-estimate" | "unknown";
22
+ export interface Consent {
23
+ upload_permitted: boolean;
24
+ redacted: boolean;
25
+ }
26
+ export interface Trace {
27
+ redacted: boolean;
28
+ content?: Record<string, unknown>;
29
+ reference?: string;
30
+ }
31
+ export interface TokenUsage {
32
+ input_tokens?: number;
33
+ output_tokens?: number;
34
+ source: TokenSource;
35
+ }
36
+ export interface OptimizeRequest {
37
+ trace: Trace;
38
+ consent: Consent;
39
+ payload_class?: PayloadClass;
40
+ provider_metadata?: {
41
+ provider?: string;
42
+ runtime?: string;
43
+ };
44
+ token_usage: TokenUsage;
45
+ evidence_labels?: Record<string, unknown>;
46
+ labels?: {
47
+ project?: string;
48
+ session?: string;
49
+ };
50
+ optimization_mode: "recommend" | "compact" | "apply-ready";
51
+ }
52
+ export interface EvaluateRequest {
53
+ original_trace: Trace;
54
+ compacted_context: {
55
+ content?: Record<string, unknown>;
56
+ reference?: string;
57
+ };
58
+ consent: Consent;
59
+ payload_class?: PayloadClass;
60
+ evidence_requirements?: {
61
+ require_recoverability?: boolean;
62
+ require_commitment_preservation?: boolean;
63
+ require_task_replay?: boolean;
64
+ };
65
+ }
66
+ export interface ReportsRequest {
67
+ report_bundle: {
68
+ runs?: Array<Record<string, unknown>>;
69
+ summary?: Record<string, unknown>;
70
+ redacted: boolean;
71
+ };
72
+ labels?: {
73
+ team?: string;
74
+ project?: string;
75
+ };
76
+ consent: Consent;
77
+ payload_class?: PayloadClass;
78
+ }
79
+ /** Any payload-bearing request body (carries `consent` + optional `payload_class`). */
80
+ export type ApiRequestBody = OptimizeRequest | EvaluateRequest | ReportsRequest;
81
+ /** `GET /v0/status` response (honest local-engine-dev shape). */
82
+ export interface StatusResponse {
83
+ status: "ok";
84
+ service: "compaction-api";
85
+ version: "v0";
86
+ mode: string;
87
+ engine: string;
88
+ hosted: boolean;
89
+ uptime_seconds: number;
90
+ }
91
+ /** Coded error body shape returned by every endpoint on failure. */
92
+ export interface ApiErrorBody {
93
+ error: string;
94
+ details?: unknown;
95
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Compaction API client — wire types (PUBLIC CLI/SDK code).
3
+ *
4
+ * OPEN-CORE BOUNDARY: this module is part of the PUBLIC `@compaction/cli` package and
5
+ * ships in the published tarball. It MUST NOT import anything from `src/engine` and MUST
6
+ * NOT import the private `apps/api` package. It mirrors — by hand, with no cross-package
7
+ * import — the request/response SHAPES defined in `apps/api/src/schemas.ts` and the contract
8
+ * in `docs/api/compaction-api-v0.md`. The client speaks only the documented HTTP contract.
9
+ *
10
+ * Keeping these types LOCAL (rather than importing the `apps/api` zod schemas) is deliberate:
11
+ * it guarantees the public package has zero build/runtime coupling to the server package or
12
+ * the engine. The local-stub tests assert the shapes still line up with the live contract.
13
+ *
14
+ * See `docs/ops/cli-api-client-design.md` (Track E) and `docs/ops/api-command-ux.md` (Track F).
15
+ */
16
+ /** Classes that carry raw-or-sanitized CONTENT and therefore require explicit upload consent. */
17
+ export const CONTENT_BEARING_PAYLOAD_CLASSES = new Set([
18
+ "sanitized_snippets",
19
+ "full_trace"
20
+ ]);
21
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,2 @@
1
+ export declare function writeJsonArtifact(outputDirectory: string, fileName: string, value: unknown): Promise<string>;
2
+ export declare function writeTextArtifact(outputDirectory: string, fileName: string, value: string): Promise<string>;
@@ -0,0 +1,14 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ async function writeArtifact(outputDirectory, fileName, value) {
3
+ await mkdir(outputDirectory, { recursive: true });
4
+ const artifactPath = `${outputDirectory}/${fileName}`;
5
+ await writeFile(artifactPath, value, "utf8");
6
+ return artifactPath;
7
+ }
8
+ export async function writeJsonArtifact(outputDirectory, fileName, value) {
9
+ return writeArtifact(outputDirectory, fileName, `${JSON.stringify(value, null, 2)}\n`);
10
+ }
11
+ export async function writeTextArtifact(outputDirectory, fileName, value) {
12
+ return writeArtifact(outputDirectory, fileName, value.endsWith("\n") ? value : `${value}\n`);
13
+ }
14
+ //# sourceMappingURL=artifact-writer.js.map
@@ -0,0 +1,91 @@
1
+ import type { RunSummaryBucket, SkippedReportFile, TopSavingsRun } from "./run-aggregator.js";
2
+ import type { RecommendationMode } from "../engine/recommendation.js";
3
+ import type { SafetyRiskLevel, SafetyStatus } from "./safety-report.js";
4
+ import type { ApplySafetyStatus } from "../engine/apply-mode.js";
5
+ export declare const AUDIT_ARTIFACT_DIRECTORY = ".compaction/audits";
6
+ export declare const AUDIT_ARTIFACT_NAMES: readonly ["audit-report.json", "audit-report.md"];
7
+ export interface AuditInputDirectories {
8
+ runs: string;
9
+ recommendations: string;
10
+ apply: string;
11
+ audits: string;
12
+ }
13
+ export interface AuditSourceSummary {
14
+ source_directory: string;
15
+ artifact_name: string;
16
+ total_artifacts: number;
17
+ skipped_files: SkippedReportFile[];
18
+ missing: boolean;
19
+ note: string | null;
20
+ }
21
+ export interface PolicyAuditSummary extends RunSummaryBucket {
22
+ policy_name: string;
23
+ }
24
+ export interface WastePatternAuditSummary extends RunSummaryBucket {
25
+ waste_pattern: string;
26
+ }
27
+ export interface SafetyAuditSummary {
28
+ source: AuditSourceSummary;
29
+ total_reports: number;
30
+ status_counts: Record<SafetyStatus, number>;
31
+ risk_level_counts: Record<SafetyRiskLevel, number>;
32
+ total_tokens_saved_with_safety_reports: number;
33
+ warnings: string[];
34
+ failures: string[];
35
+ note: string | null;
36
+ }
37
+ export interface RecommendationAuditSummary {
38
+ source: AuditSourceSummary;
39
+ total_recommendations: number;
40
+ mode_counts: Record<RecommendationMode, number>;
41
+ total_tokens_saved: number;
42
+ recommended_next_steps: string[];
43
+ note: string | null;
44
+ }
45
+ export interface ApplyAuditSummary {
46
+ source: AuditSourceSummary;
47
+ total_apply_reports: number;
48
+ applied_count: number;
49
+ refused_count: number;
50
+ safety_status_counts: Record<ApplySafetyStatus, number>;
51
+ total_tokens_saved_from_applied_reports: number;
52
+ total_saving_per_run_from_applied_reports: number;
53
+ refusal_reasons: string[];
54
+ note: string | null;
55
+ }
56
+ export interface AuditReport {
57
+ audit_id: string;
58
+ generated_at: string;
59
+ total_runs: number;
60
+ total_original_input_tokens: number;
61
+ total_compacted_input_tokens: number;
62
+ total_tokens_saved: number;
63
+ average_percent_reduction: number;
64
+ total_cost_before_per_run: number;
65
+ total_cost_after_per_run: number;
66
+ total_saving_per_run: number;
67
+ policies_detected: PolicyAuditSummary[];
68
+ top_waste_patterns: WastePatternAuditSummary[];
69
+ safety_summary: SafetyAuditSummary;
70
+ recommendation_summary: RecommendationAuditSummary;
71
+ apply_summary?: ApplyAuditSummary;
72
+ top_savings_runs: TopSavingsRun[];
73
+ suggested_next_steps: string[];
74
+ limitations: string[];
75
+ sources: {
76
+ runs: AuditSourceSummary;
77
+ recommendations: AuditSourceSummary;
78
+ apply: AuditSourceSummary;
79
+ };
80
+ }
81
+ export interface AuditArtifactResult {
82
+ audit: AuditReport;
83
+ markdown: string;
84
+ paths: {
85
+ auditJsonPath: string;
86
+ auditMarkdownPath: string;
87
+ };
88
+ }
89
+ export declare function createAuditReport(directories?: Partial<AuditInputDirectories>, generatedAt?: string): Promise<AuditReport>;
90
+ export declare function formatAuditMarkdown(report: AuditReport): string;
91
+ export declare function writeAuditArtifacts(directories?: Partial<AuditInputDirectories>, generatedAt?: string): Promise<AuditArtifactResult>;