@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,23 @@
1
+ /**
2
+ * Credential-shaped redaction for the provider-usage capture path.
3
+ *
4
+ * Per docs/design/provider-api-secrets-handling.md §4: a single redaction pass
5
+ * applies to EVERY output path (logs, errors, artifacts, reports, debug dumps).
6
+ *
7
+ * The PRIMARY control is that the credential is never placed into any structure
8
+ * that gets logged or serialized (see provider-usage-adapter.ts). This redaction
9
+ * pass is the defense-in-depth backstop: it masks the configured secret value (if
10
+ * present) AND anything matching known credential shapes, before any string is
11
+ * written to stdout/stderr/log/error/artifact.
12
+ *
13
+ * NO real secret ever appears here — tests exercise this with fake sentinels only.
14
+ */
15
+ export declare const REDACTION_MASK = "***REDACTED***";
16
+ /**
17
+ * Redact a string for safe output. If `configuredSecret` is provided and present
18
+ * in the text, every occurrence is masked first (exact match — provider-agnostic),
19
+ * then known credential shapes are masked as a backstop.
20
+ */
21
+ export declare function redactSecrets(text: string, configuredSecret?: string): string;
22
+ /** Redact then write to stderr. Use for ALL error/diagnostic output on this path. */
23
+ export declare function redactedError(message: string, configuredSecret?: string): string;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Credential-shaped redaction for the provider-usage capture path.
3
+ *
4
+ * Per docs/design/provider-api-secrets-handling.md §4: a single redaction pass
5
+ * applies to EVERY output path (logs, errors, artifacts, reports, debug dumps).
6
+ *
7
+ * The PRIMARY control is that the credential is never placed into any structure
8
+ * that gets logged or serialized (see provider-usage-adapter.ts). This redaction
9
+ * pass is the defense-in-depth backstop: it masks the configured secret value (if
10
+ * present) AND anything matching known credential shapes, before any string is
11
+ * written to stdout/stderr/log/error/artifact.
12
+ *
13
+ * NO real secret ever appears here — tests exercise this with fake sentinels only.
14
+ */
15
+ export const REDACTION_MASK = "***REDACTED***";
16
+ /**
17
+ * Known credential-shaped patterns (defense in depth). These are deliberately
18
+ * conservative shape matchers, NOT a DLP/ML classifier:
19
+ * - `Authorization: Bearer <token>` header values
20
+ * - `x-api-key: <token>` header values
21
+ * - common provider key prefixes followed by a long token body
22
+ * - long high-entropy bearer-style tokens
23
+ */
24
+ const KNOWN_CREDENTIAL_SHAPE_PATTERNS = [
25
+ // Authorization: Bearer <token> (mask the token, keep the header name)
26
+ /(authorization\s*:\s*bearer\s+)[A-Za-z0-9._\-+/=]{8,}/gi,
27
+ // x-api-key: <token> (mask the value, keep the header name)
28
+ /(x-api-key\s*:\s*)[A-Za-z0-9._\-+/=]{8,}/gi,
29
+ // Common provider key prefixes (sk-, sk-ant-, sk-proj-, anthropic-, etc.) + body
30
+ /\b(?:sk-(?:ant-|proj-)?|api[-_]?key[-_]|anthropic-|openai-)[A-Za-z0-9._\-]{12,}\b/gi,
31
+ // Long high-entropy token bodies (>= 32 chars of base64url-ish content)
32
+ /\b[A-Za-z0-9._\-+/=]{40,}\b/g
33
+ ];
34
+ /**
35
+ * Redact a string for safe output. If `configuredSecret` is provided and present
36
+ * in the text, every occurrence is masked first (exact match — provider-agnostic),
37
+ * then known credential shapes are masked as a backstop.
38
+ */
39
+ export function redactSecrets(text, configuredSecret) {
40
+ let redacted = text;
41
+ if (configuredSecret && configuredSecret.length > 0) {
42
+ // Exact-match mask of the known secret value (provider-agnostic, highest-precision).
43
+ redacted = redacted.split(configuredSecret).join(REDACTION_MASK);
44
+ }
45
+ for (const pattern of KNOWN_CREDENTIAL_SHAPE_PATTERNS) {
46
+ redacted = redacted.replace(pattern, (match, prefix) => {
47
+ // Header patterns capture a non-secret prefix we preserve; keep it, mask the value.
48
+ if (typeof prefix === "string" && prefix.length > 0) {
49
+ return `${prefix}${REDACTION_MASK}`;
50
+ }
51
+ return REDACTION_MASK;
52
+ });
53
+ }
54
+ return redacted;
55
+ }
56
+ /** Redact then write to stderr. Use for ALL error/diagnostic output on this path. */
57
+ export function redactedError(message, configuredSecret) {
58
+ return redactSecrets(message, configuredSecret);
59
+ }
60
+ //# sourceMappingURL=credential-redaction.js.map
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Provider-usage capture adapter (Option A, read-only, aggregate-only).
3
+ *
4
+ * Normalizes a read-only provider usage/cost API read into a CapturedRun carrying
5
+ * usage/cost UsageMetadata ONLY (source = "provider_usage"), with NO message
6
+ * content. See:
7
+ * - docs/design/provider-api-capture.md (Option A)
8
+ * - docs/design/provider-api-secrets-handling.md (credential: env-only, in-memory,
9
+ * never stored/logged/serialized)
10
+ * - docs/design/provider-api-privacy-redaction.md (aggregate-only, no content)
11
+ * - docs/design/provider-api-tracesource-classification.md (Option 2: provider_usage
12
+ * -> provider_reported_usage; cost/spend evidence, caps approval-readiness at
13
+ * "conditional", never "ready")
14
+ *
15
+ * HARD INVARIANTS enforced here:
16
+ * - The credential is read from an env var, held in a local variable, passed ONLY
17
+ * to the client's auth header, and NEVER copied into the trace/usage/provenance
18
+ * or any returned/serialized structure.
19
+ * - Missing/empty credential => clean refusal (clear message naming the env var),
20
+ * no artifact, no network read. Never a silent fallback.
21
+ * - The produced trace has NO messages (a usage-only read has nothing to compact)
22
+ * and NO content fields. UsageMetadata carries aggregate usage/cost only.
23
+ * - cost_source: "provider_reported" ONLY when the endpoint genuinely exposed cost;
24
+ * otherwise price-table estimate / missing.
25
+ */
26
+ import type { CapturedRun } from "../capture-adapter.js";
27
+ import { type ProviderUsageClient } from "./provider-usage-client.js";
28
+ export declare const PROVIDER_USAGE_ADAPTER_ID = "provider-usage-read-v1";
29
+ /** Default env var name the credential is read from. A flag may only NAME the var. */
30
+ export declare const DEFAULT_CREDENTIAL_ENV_VAR = "COMPACTION_PROVIDER_USAGE_TOKEN";
31
+ export declare const PROVIDER_USAGE_PRIVACY_NOTE: string;
32
+ /** Thrown when the credential env var is absent/empty. Carries NO secret value. */
33
+ export declare class MissingProviderCredentialError extends Error {
34
+ readonly envVar: string;
35
+ constructor(envVar: string);
36
+ }
37
+ export interface ProviderUsageCaptureInput {
38
+ /** Configurable provider usage/cost endpoint (non-secret descriptor). */
39
+ endpoint: string;
40
+ /** Env var name to read the credential from (NAME only — never the value). */
41
+ credentialEnvVar?: string;
42
+ /** Optional reporting window start (ISO 8601). */
43
+ windowStart?: string;
44
+ /** Optional reporting window end (ISO 8601). */
45
+ windowEnd?: string;
46
+ /** Human-readable label for the run/window, if any. */
47
+ label?: string;
48
+ /** Injectable client. Tests pass a mock; defaults to the real fetch client. */
49
+ client?: ProviderUsageClient;
50
+ /** Override env reader (tests). Defaults to process.env. */
51
+ env?: Record<string, string | undefined>;
52
+ /** Fixed timestamp for deterministic artifacts/tests. */
53
+ capturedAt?: string;
54
+ /** Fixed run id for deterministic artifacts/tests. */
55
+ runId?: string;
56
+ }
57
+ /**
58
+ * Capture aggregate provider usage/cost into a CapturedRun.
59
+ *
60
+ * Reads the credential from the named env var (refusing cleanly if absent/empty),
61
+ * performs the single read via the injectable client, and normalizes the result
62
+ * into a usage-only CapturedRun. The credential never leaves this function except
63
+ * as the client's auth header value.
64
+ */
65
+ export declare function captureProviderUsage(input: ProviderUsageCaptureInput): Promise<CapturedRun>;
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Provider-usage capture adapter (Option A, read-only, aggregate-only).
3
+ *
4
+ * Normalizes a read-only provider usage/cost API read into a CapturedRun carrying
5
+ * usage/cost UsageMetadata ONLY (source = "provider_usage"), with NO message
6
+ * content. See:
7
+ * - docs/design/provider-api-capture.md (Option A)
8
+ * - docs/design/provider-api-secrets-handling.md (credential: env-only, in-memory,
9
+ * never stored/logged/serialized)
10
+ * - docs/design/provider-api-privacy-redaction.md (aggregate-only, no content)
11
+ * - docs/design/provider-api-tracesource-classification.md (Option 2: provider_usage
12
+ * -> provider_reported_usage; cost/spend evidence, caps approval-readiness at
13
+ * "conditional", never "ready")
14
+ *
15
+ * HARD INVARIANTS enforced here:
16
+ * - The credential is read from an env var, held in a local variable, passed ONLY
17
+ * to the client's auth header, and NEVER copied into the trace/usage/provenance
18
+ * or any returned/serialized structure.
19
+ * - Missing/empty credential => clean refusal (clear message naming the env var),
20
+ * no artifact, no network read. Never a silent fallback.
21
+ * - The produced trace has NO messages (a usage-only read has nothing to compact)
22
+ * and NO content fields. UsageMetadata carries aggregate usage/cost only.
23
+ * - cost_source: "provider_reported" ONLY when the endpoint genuinely exposed cost;
24
+ * otherwise price-table estimate / missing.
25
+ */
26
+ import { createUsageMetadata } from "../usage-metadata.js";
27
+ import { FetchProviderUsageClient } from "./provider-usage-client.js";
28
+ export const PROVIDER_USAGE_ADAPTER_ID = "provider-usage-read-v1";
29
+ /** Default env var name the credential is read from. A flag may only NAME the var. */
30
+ export const DEFAULT_CREDENTIAL_ENV_VAR = "COMPACTION_PROVIDER_USAGE_TOKEN";
31
+ export const PROVIDER_USAGE_PRIVACY_NOTE = "NOTE: This is a read-only, aggregate-only provider usage/cost read. It captures token/cost\n" +
32
+ "aggregates ONLY — NO prompt/completion content is read or stored. The captured artifact is\n" +
33
+ "written locally to the output directory only. The credential is read from an environment\n" +
34
+ "variable, used only for the single read-only request, and is never logged or written to any\n" +
35
+ "artifact.";
36
+ /** Thrown when the credential env var is absent/empty. Carries NO secret value. */
37
+ export class MissingProviderCredentialError extends Error {
38
+ envVar;
39
+ constructor(envVar) {
40
+ super(`No provider usage credential found. Set the ${envVar} environment variable ` +
41
+ `(a read-only, usage/cost-scoped token) and re-run. Refusing; no fallback, no artifact written.`);
42
+ this.name = "MissingProviderCredentialError";
43
+ this.envVar = envVar;
44
+ }
45
+ }
46
+ /**
47
+ * Build the UsageMetadata for an aggregate read. cost_source is "provider_reported"
48
+ * only when the endpoint genuinely exposed a cost; otherwise the existing estimate
49
+ * path (price-table when a known model is present, else missing) is used.
50
+ */
51
+ function buildUsageMetadata(usage) {
52
+ const endpointExposedCost = typeof usage.cost === "number";
53
+ const limitations = [
54
+ "Aggregate usage/cost read from the provider usage/cost reporting API for account-level reporting; this is a usage/cost report, not a per-run invoice reconciliation.",
55
+ "Aggregate usage only — NO prompt/completion content was captured by this read.",
56
+ "Not billing-confirmed savings (Tier 3): this is the usage/cost substrate only, not a measured applied-optimization billing delta."
57
+ ];
58
+ if (!endpointExposedCost) {
59
+ limitations.push("Cost not exposed by the endpoint; cost remains a price-table estimate or missing.");
60
+ }
61
+ return createUsageMetadata({
62
+ inputTokens: usage.input_tokens,
63
+ outputTokens: usage.output_tokens,
64
+ totalTokens: usage.total_tokens,
65
+ providerReportedTokens: true,
66
+ estimatedTokens: false,
67
+ providerReportedCost: endpointExposedCost,
68
+ currency: usage.currency,
69
+ model: usage.model,
70
+ provider: usage.provider,
71
+ limitations
72
+ });
73
+ }
74
+ /** A usage-only read produces a trace with NO messages and NO content. */
75
+ function buildUsageOnlyTrace(usage, input, capturedAt, runId) {
76
+ return {
77
+ id: runId,
78
+ title: input.label ?? `Provider usage/cost read (${input.endpoint})`,
79
+ artifactVersion: "agent-trace-v1",
80
+ source: "provider_usage",
81
+ createdAt: capturedAt,
82
+ generatedAt: capturedAt,
83
+ model: usage.model ?? "unknown",
84
+ // Aggregate-only: NO messages. A usage-only trace has nothing to compact.
85
+ messages: []
86
+ };
87
+ }
88
+ /**
89
+ * Build a non-secret provenance descriptor of the read. Records the endpoint and
90
+ * window and the explicit "aggregate usage only, no content" note. NEVER records
91
+ * the credential or any credential-derived value.
92
+ */
93
+ function buildProvenance(input, usage, capturedAt) {
94
+ const windowStart = input.windowStart ?? usage.window_start ?? null;
95
+ const windowEnd = input.windowEnd ?? usage.window_end ?? null;
96
+ const windowDescriptor = windowStart || windowEnd ? `window ${windowStart ?? "unspecified"} .. ${windowEnd ?? "unspecified"}` : "window unspecified";
97
+ return {
98
+ captureAdapter: PROVIDER_USAGE_ADAPTER_ID,
99
+ // Non-secret descriptor ONLY: endpoint + window. Never the credential.
100
+ sourcePath: `${input.endpoint} (${windowDescriptor})`,
101
+ capturedAt,
102
+ sessionId: null,
103
+ warnings: [PROVIDER_USAGE_PRIVACY_NOTE],
104
+ limitations: [
105
+ "Read-only, aggregate-only provider usage/cost read (Option A).",
106
+ "NO prompt/completion content was captured by this read.",
107
+ "Credential was read from an environment variable, used only for the single read-only request, and is not recorded here.",
108
+ `Provider-reported usage; cost is ${typeof usage.cost === "number" ? "provider-reported" : "price-table estimated or missing"}.`,
109
+ "Billing-confirmed savings (Tier 3) are NOT claimed."
110
+ ]
111
+ };
112
+ }
113
+ /**
114
+ * Capture aggregate provider usage/cost into a CapturedRun.
115
+ *
116
+ * Reads the credential from the named env var (refusing cleanly if absent/empty),
117
+ * performs the single read via the injectable client, and normalizes the result
118
+ * into a usage-only CapturedRun. The credential never leaves this function except
119
+ * as the client's auth header value.
120
+ */
121
+ export async function captureProviderUsage(input) {
122
+ const envVar = input.credentialEnvVar ?? DEFAULT_CREDENTIAL_ENV_VAR;
123
+ const env = input.env ?? process.env;
124
+ // Credential acquisition: env var ONLY. Clean refusal on absence — never a fallback.
125
+ const credential = env[envVar];
126
+ if (typeof credential !== "string" || credential.length === 0) {
127
+ throw new MissingProviderCredentialError(envVar);
128
+ }
129
+ const capturedAt = input.capturedAt ?? new Date().toISOString();
130
+ const runId = input.runId ?? `provider-usage-${Date.parse(capturedAt) || Date.now()}`;
131
+ const client = input.client ?? new FetchProviderUsageClient();
132
+ // The single read-only request. The credential is passed in-memory only and is
133
+ // NOT retained after this call (no copy into any returned structure below).
134
+ const usage = await client.fetchUsage({
135
+ endpoint: input.endpoint,
136
+ credential,
137
+ windowStart: input.windowStart,
138
+ windowEnd: input.windowEnd
139
+ });
140
+ const trace = buildUsageOnlyTrace(usage, input, capturedAt, runId);
141
+ const usageMetadata = buildUsageMetadata(usage);
142
+ const provenance = buildProvenance(input, usage, capturedAt);
143
+ // The returned CapturedRun carries usage/cost + non-secret descriptors ONLY.
144
+ // The credential is never part of trace, usage, or provenance.
145
+ return { trace, usage: usageMetadata, provenance };
146
+ }
147
+ //# sourceMappingURL=provider-usage-adapter.js.map
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Injectable provider-usage read client (Option A, read-only, aggregate-only).
3
+ *
4
+ * Per docs/design/provider-api-capture.md and the secrets/privacy designs:
5
+ * - The provider HTTP read is abstracted behind this small interface so tests
6
+ * inject a MOCK returning a fixture aggregate-usage response (NO real network,
7
+ * NO real secret).
8
+ * - The real implementation uses Node's global `fetch` (built-in — NO new npm
9
+ * dependency) to a configurable endpoint with the credential in the auth header.
10
+ * - The credential is passed in-memory only and is NEVER stored, logged, or
11
+ * placed into any serialized structure (see provider-usage-adapter.ts).
12
+ *
13
+ * Aggregate-only invariant: the response carries usage/cost AGGREGATES ONLY.
14
+ * There is NO field for prompt/completion/message content anywhere in this contract.
15
+ */
16
+ /**
17
+ * Expected aggregate-usage input contract — the JSON shape the read expects from a
18
+ * provider usage/cost reporting endpoint. Parsed defensively (parseAggregateUsage).
19
+ *
20
+ * NOTE: the exact per-provider response schema mapping is only validated when an
21
+ * operator runs this against a real provider; here it is the documented contract
22
+ * the read normalizes into, exercised with mock fixtures only. There is NO content
23
+ * field in this contract by construction.
24
+ */
25
+ export interface ProviderAggregateUsage {
26
+ /** Model identifier the aggregate is reported for (e.g. "claude-3-5-sonnet"). */
27
+ model?: string;
28
+ /** Provider identifier (e.g. "anthropic", "openai"). */
29
+ provider?: string;
30
+ /** Aggregate input token count for the window. */
31
+ input_tokens?: number;
32
+ /** Aggregate output token count for the window. */
33
+ output_tokens?: number;
34
+ /** Aggregate total token count for the window (derived if absent). */
35
+ total_tokens?: number;
36
+ /** Aggregate request count for the window, if the endpoint reports it. */
37
+ request_count?: number;
38
+ /**
39
+ * Aggregate cost for the window, ONLY when the endpoint genuinely exposes cost.
40
+ * When present, cost is labeled provider-reported; when absent, cost stays a
41
+ * price-table estimate. Never guessed.
42
+ */
43
+ cost?: number;
44
+ /** Currency code for `cost` (e.g. "USD"), when cost is exposed. */
45
+ currency?: string;
46
+ /** Start of the reporting window (ISO 8601), if reported. */
47
+ window_start?: string;
48
+ /** End of the reporting window (ISO 8601), if reported. */
49
+ window_end?: string;
50
+ }
51
+ export interface FetchUsageInput {
52
+ /** Configurable provider usage/cost endpoint (non-secret). */
53
+ endpoint: string;
54
+ /**
55
+ * The read-only credential, held in memory only for the single request.
56
+ * Supplied as the auth header value; NEVER logged, stored, or serialized.
57
+ */
58
+ credential: string;
59
+ /** Optional reporting window start (ISO 8601). */
60
+ windowStart?: string;
61
+ /** Optional reporting window end (ISO 8601). */
62
+ windowEnd?: string;
63
+ }
64
+ export interface ProviderUsageClient {
65
+ /**
66
+ * Read aggregate usage/cost from the provider's own usage/cost reporting API.
67
+ * Returns aggregate usage/cost ONLY — never prompt/completion content.
68
+ */
69
+ fetchUsage(input: FetchUsageInput): Promise<ProviderAggregateUsage>;
70
+ }
71
+ /**
72
+ * Defensive parser for the aggregate-usage contract. Accepts an unknown JSON value
73
+ * (e.g. a provider response body) and extracts ONLY the aggregate usage/cost fields.
74
+ * Any content-shaped or unexpected fields are dropped — they never reach the trace.
75
+ */
76
+ export declare function parseAggregateUsage(raw: unknown): ProviderAggregateUsage;
77
+ /**
78
+ * Real provider-usage client using Node's built-in global `fetch` (NO new npm
79
+ * dependency). Performs exactly ONE read-only GET to the configured endpoint with
80
+ * the credential in the auth header. The credential is used only for this request
81
+ * and is never logged or returned. The response is parsed defensively to aggregate
82
+ * usage/cost only.
83
+ *
84
+ * This live path is unit-tested with a MOCK client; it is NOT dogfooded against a
85
+ * real provider in this environment (no credential/endpoint available).
86
+ */
87
+ export declare class FetchProviderUsageClient implements ProviderUsageClient {
88
+ fetchUsage(input: FetchUsageInput): Promise<ProviderAggregateUsage>;
89
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Injectable provider-usage read client (Option A, read-only, aggregate-only).
3
+ *
4
+ * Per docs/design/provider-api-capture.md and the secrets/privacy designs:
5
+ * - The provider HTTP read is abstracted behind this small interface so tests
6
+ * inject a MOCK returning a fixture aggregate-usage response (NO real network,
7
+ * NO real secret).
8
+ * - The real implementation uses Node's global `fetch` (built-in — NO new npm
9
+ * dependency) to a configurable endpoint with the credential in the auth header.
10
+ * - The credential is passed in-memory only and is NEVER stored, logged, or
11
+ * placed into any serialized structure (see provider-usage-adapter.ts).
12
+ *
13
+ * Aggregate-only invariant: the response carries usage/cost AGGREGATES ONLY.
14
+ * There is NO field for prompt/completion/message content anywhere in this contract.
15
+ */
16
+ function coerceNonNegativeNumber(value) {
17
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
18
+ }
19
+ function coerceString(value) {
20
+ return typeof value === "string" && value.length > 0 ? value : undefined;
21
+ }
22
+ /**
23
+ * Defensive parser for the aggregate-usage contract. Accepts an unknown JSON value
24
+ * (e.g. a provider response body) and extracts ONLY the aggregate usage/cost fields.
25
+ * Any content-shaped or unexpected fields are dropped — they never reach the trace.
26
+ */
27
+ export function parseAggregateUsage(raw) {
28
+ const obj = (raw && typeof raw === "object" ? raw : {});
29
+ const result = {};
30
+ const model = coerceString(obj.model);
31
+ if (model !== undefined)
32
+ result.model = model;
33
+ const provider = coerceString(obj.provider);
34
+ if (provider !== undefined)
35
+ result.provider = provider;
36
+ const inputTokens = coerceNonNegativeNumber(obj.input_tokens);
37
+ if (inputTokens !== undefined)
38
+ result.input_tokens = inputTokens;
39
+ const outputTokens = coerceNonNegativeNumber(obj.output_tokens);
40
+ if (outputTokens !== undefined)
41
+ result.output_tokens = outputTokens;
42
+ const totalTokens = coerceNonNegativeNumber(obj.total_tokens);
43
+ if (totalTokens !== undefined)
44
+ result.total_tokens = totalTokens;
45
+ const requestCount = coerceNonNegativeNumber(obj.request_count);
46
+ if (requestCount !== undefined)
47
+ result.request_count = requestCount;
48
+ const cost = coerceNonNegativeNumber(obj.cost);
49
+ if (cost !== undefined)
50
+ result.cost = cost;
51
+ const currency = coerceString(obj.currency);
52
+ if (currency !== undefined)
53
+ result.currency = currency;
54
+ const windowStart = coerceString(obj.window_start);
55
+ if (windowStart !== undefined)
56
+ result.window_start = windowStart;
57
+ const windowEnd = coerceString(obj.window_end);
58
+ if (windowEnd !== undefined)
59
+ result.window_end = windowEnd;
60
+ return result;
61
+ }
62
+ /**
63
+ * Real provider-usage client using Node's built-in global `fetch` (NO new npm
64
+ * dependency). Performs exactly ONE read-only GET to the configured endpoint with
65
+ * the credential in the auth header. The credential is used only for this request
66
+ * and is never logged or returned. The response is parsed defensively to aggregate
67
+ * usage/cost only.
68
+ *
69
+ * This live path is unit-tested with a MOCK client; it is NOT dogfooded against a
70
+ * real provider in this environment (no credential/endpoint available).
71
+ */
72
+ export class FetchProviderUsageClient {
73
+ async fetchUsage(input) {
74
+ const url = new URL(input.endpoint);
75
+ if (input.windowStart)
76
+ url.searchParams.set("window_start", input.windowStart);
77
+ if (input.windowEnd)
78
+ url.searchParams.set("window_end", input.windowEnd);
79
+ const response = await fetch(url, {
80
+ method: "GET",
81
+ headers: {
82
+ // Auth header carries the credential for this single read only.
83
+ Authorization: `Bearer ${input.credential}`,
84
+ Accept: "application/json"
85
+ }
86
+ });
87
+ if (!response.ok) {
88
+ // Surface status WITHOUT echoing the request auth header or the credential.
89
+ // The caller redacts before printing as a backstop.
90
+ throw new Error(`Provider usage endpoint returned HTTP ${response.status} ${response.statusText}.`);
91
+ }
92
+ const body = await response.json();
93
+ return parseAggregateUsage(body);
94
+ }
95
+ }
96
+ //# sourceMappingURL=provider-usage-client.js.map
@@ -0,0 +1,7 @@
1
+ import type { SkillInjectionAdvisory } from "./skill-injection-detector.js";
2
+ import type { AgentTrace, CompactionPolicy, CompactionReport, CostEstimate, TokenEstimate, WasteFinding } from "./types.js";
3
+ import type { UsageMetadata } from "./usage-metadata.js";
4
+ export declare function formatAnalyzeReport(trace: AgentTrace, tokens: TokenEstimate, cost: CostEstimate, findings?: WasteFinding[], usage?: UsageMetadata, skillInjectionAdvisory?: SkillInjectionAdvisory): string;
5
+ export declare function formatCompactionReport(report: CompactionReport): string;
6
+ export declare function formatCompactionMarkdownReport(report: CompactionReport, policy: CompactionPolicy): string;
7
+ export declare function formatPrCommentReport(report: CompactionReport, policy: CompactionPolicy, artifactNames: string[]): string;