@compaction/cli 0.1.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -12
- package/dist/cli/commands/activity.d.ts +8 -0
- package/dist/cli/commands/activity.js +35 -0
- package/dist/cli/commands/apply-context.js +68 -0
- package/dist/cli/commands/billing-delta.js +12 -1
- package/dist/cli/commands/capture-claude-code.d.ts +44 -0
- package/dist/cli/commands/capture-claude-code.js +206 -0
- package/dist/cli/commands/capture.js +283 -1
- package/dist/cli/commands/compact.js +24 -11
- package/dist/cli/commands/context.d.ts +2 -0
- package/dist/cli/commands/context.js +130 -0
- package/dist/cli/commands/dev.d.ts +17 -0
- package/dist/cli/commands/dev.js +131 -0
- package/dist/cli/commands/gateway.d.ts +31 -0
- package/dist/cli/commands/gateway.js +312 -0
- package/dist/cli/commands/hooks.d.ts +5 -0
- package/dist/cli/commands/hooks.js +181 -0
- package/dist/cli/commands/import.js +3 -1
- package/dist/cli/commands/init.js +422 -17
- package/dist/cli/commands/input-compaction-ab.d.ts +2 -0
- package/dist/cli/commands/input-compaction-ab.js +125 -0
- package/dist/cli/commands/optimize-hosted.d.ts +54 -0
- package/dist/cli/commands/optimize-hosted.js +123 -0
- package/dist/cli/commands/optimize.js +8 -0
- package/dist/cli/commands/output-shaping-ab.d.ts +2 -0
- package/dist/cli/commands/output-shaping-ab.js +132 -0
- package/dist/cli/commands/output-shaping.d.ts +7 -0
- package/dist/cli/commands/output-shaping.js +43 -0
- package/dist/cli/commands/policies.d.ts +2 -0
- package/dist/cli/commands/policies.js +80 -0
- package/dist/cli/commands/precall.d.ts +19 -0
- package/dist/cli/commands/precall.js +436 -0
- package/dist/cli/commands/recommend.js +1 -1
- package/dist/cli/commands/run.js +310 -18
- package/dist/cli/commands/upgrade-status.d.ts +42 -0
- package/dist/cli/commands/upgrade-status.js +152 -0
- package/dist/cli/cursor-export-read.d.ts +13 -0
- package/dist/cli/cursor-export-read.js +53 -0
- package/dist/cli/cursor-live-preflight.d.ts +19 -0
- package/dist/cli/cursor-live-preflight.js +46 -0
- package/dist/cli/index.js +39 -2
- package/dist/cli/onboarding/GatewayTui.d.ts +33 -0
- package/dist/cli/onboarding/GatewayTui.js +113 -0
- package/dist/cli/onboarding/model.d.ts +32 -1
- package/dist/cli/onboarding/model.js +27 -2
- package/dist/cli/terminal-logo.d.ts +27 -0
- package/dist/cli/terminal-logo.js +49 -0
- package/dist/core/activity-event.d.ts +107 -0
- package/dist/core/activity-event.js +182 -0
- package/dist/core/activity-store.d.ts +63 -0
- package/dist/core/activity-store.js +254 -0
- package/dist/core/activity-view.d.ts +76 -0
- package/dist/core/activity-view.js +120 -0
- package/dist/core/api-client/index.d.ts +3 -1
- package/dist/core/api-client/index.js +4 -0
- package/dist/core/api-client/payload.d.ts +5 -1
- package/dist/core/api-client/payload.js +8 -6
- package/dist/core/api-client/persisted-config.d.ts +57 -0
- package/dist/core/api-client/persisted-config.js +156 -0
- package/dist/core/api-client/tool.d.ts +29 -0
- package/dist/core/api-client/tool.js +50 -0
- package/dist/core/api-client/types.d.ts +15 -4
- package/dist/core/auto-apply-ask.d.ts +58 -0
- package/dist/core/auto-apply-ask.js +105 -0
- package/dist/core/auto-apply-gates.d.ts +76 -0
- package/dist/core/auto-apply-gates.js +113 -0
- package/dist/core/before-call-activity.d.ts +35 -0
- package/dist/core/before-call-activity.js +103 -0
- package/dist/core/before-call-recovery.d.ts +19 -0
- package/dist/core/before-call-recovery.js +46 -0
- package/dist/core/before-call-stdin.d.ts +59 -0
- package/dist/core/before-call-stdin.js +78 -0
- package/dist/core/before-call.d.ts +149 -0
- package/dist/core/before-call.js +358 -0
- package/dist/core/billing-delta/billing-delta-record.d.ts +28 -0
- package/dist/core/billing-delta/billing-delta-record.js +26 -0
- package/dist/core/capture-record.d.ts +40 -0
- package/dist/core/capture-record.js +55 -0
- package/dist/core/claude-code-before-call.d.ts +37 -0
- package/dist/core/claude-code-before-call.js +120 -0
- package/dist/core/claude-code-connect.d.ts +31 -0
- package/dist/core/claude-code-connect.js +87 -0
- package/dist/core/claude-code-hook-record.d.ts +71 -0
- package/dist/core/claude-code-hook-record.js +0 -0
- package/dist/core/claude-code-hooks.d.ts +77 -0
- package/dist/core/claude-code-hooks.js +141 -0
- package/dist/core/codex-capture.d.ts +45 -0
- package/dist/core/codex-capture.js +204 -0
- package/dist/core/command-runner.js +5 -1
- package/dist/core/compaction-artifacts.d.ts +51 -0
- package/dist/core/compaction-artifacts.js +242 -0
- package/dist/core/compactor.d.ts +9 -0
- package/dist/core/compactor.js +95 -0
- package/dist/core/context-store-eval-cases.d.ts +6 -0
- package/dist/core/context-store-eval-cases.js +331 -0
- package/dist/core/context-store-eval.d.ts +140 -0
- package/dist/core/context-store-eval.js +138 -0
- package/dist/core/context-store-fs.d.ts +73 -0
- package/dist/core/context-store-fs.js +157 -0
- package/dist/core/context-store-sufficiency.d.ts +98 -0
- package/dist/core/context-store-sufficiency.js +135 -0
- package/dist/core/context-store.d.ts +155 -0
- package/dist/core/context-store.js +228 -0
- package/dist/core/cross-surface-event.d.ts +306 -0
- package/dist/core/cross-surface-event.js +330 -0
- package/dist/core/cursor-capture.d.ts +54 -0
- package/dist/core/cursor-capture.js +215 -0
- package/dist/core/cursor-preflight-probe.d.ts +8 -0
- package/dist/core/cursor-preflight-probe.js +88 -0
- package/dist/core/cursor-preflight.d.ts +86 -0
- package/dist/core/cursor-preflight.js +126 -0
- package/dist/core/gateway/apply-activation.d.ts +39 -0
- package/dist/core/gateway/apply-activation.js +84 -0
- package/dist/core/gateway/apply-policy.d.ts +64 -0
- package/dist/core/gateway/apply-policy.js +221 -0
- package/dist/core/gateway/apply-receipt.d.ts +36 -0
- package/dist/core/gateway/apply-receipt.js +75 -0
- package/dist/core/gateway/cache-proof.d.ts +45 -0
- package/dist/core/gateway/cache-proof.js +65 -0
- package/dist/core/gateway/configure.d.ts +50 -0
- package/dist/core/gateway/configure.js +169 -0
- package/dist/core/gateway/openai-usage.d.ts +56 -0
- package/dist/core/gateway/openai-usage.js +128 -0
- package/dist/core/gateway/receipt.d.ts +138 -0
- package/dist/core/gateway/receipt.js +120 -0
- package/dist/core/gateway/recovery.d.ts +23 -0
- package/dist/core/gateway/recovery.js +68 -0
- package/dist/core/gateway/server.d.ts +51 -0
- package/dist/core/gateway/server.js +276 -0
- package/dist/core/gateway/status.d.ts +45 -0
- package/dist/core/gateway/status.js +109 -0
- package/dist/core/hook-usage-aggregate.d.ts +47 -0
- package/dist/core/hook-usage-aggregate.js +161 -0
- package/dist/core/input-compaction-ab.d.ts +111 -0
- package/dist/core/input-compaction-ab.js +158 -0
- package/dist/core/local-run-record.d.ts +109 -0
- package/dist/core/local-run-record.js +223 -0
- package/dist/core/output-shaping-ab.d.ts +140 -0
- package/dist/core/output-shaping-ab.js +146 -0
- package/dist/core/output-shaping-attach.d.ts +31 -0
- package/dist/core/output-shaping-attach.js +57 -0
- package/dist/core/output-shaping.d.ts +56 -0
- package/dist/core/output-shaping.js +89 -0
- package/dist/core/policy-middleware.d.ts +121 -0
- package/dist/core/policy-middleware.js +919 -0
- package/dist/core/policy-preferences.d.ts +99 -0
- package/dist/core/policy-preferences.js +232 -0
- package/dist/core/report-generator.d.ts +19 -1
- package/dist/core/report-generator.js +51 -0
- package/dist/core/run-aggregator.d.ts +56 -1
- package/dist/core/run-aggregator.js +93 -2
- package/dist/core/run-flow-report.d.ts +82 -0
- package/dist/core/run-flow-report.js +71 -0
- package/dist/core/safety-report.js +8 -1
- package/dist/core/shim-capture-bridge.d.ts +32 -0
- package/dist/core/shim-capture-bridge.js +88 -0
- package/dist/core/skill-injection-policy.d.ts +72 -0
- package/dist/core/skill-injection-policy.js +183 -0
- package/dist/core/spend-attribution.js +1 -1
- package/dist/core/token-accounting.d.ts +1 -1
- package/dist/core/tool-shim.d.ts +129 -0
- package/dist/core/tool-shim.js +447 -0
- package/dist/core/trace-adapters.d.ts +8 -0
- package/dist/core/trace-adapters.js +4 -0
- package/dist/core/trace-parser.d.ts +13 -13
- package/dist/core/trace-parser.js +6 -6
- package/dist/core/types.d.ts +45 -2
- package/dist/core/waste-detector.d.ts +20 -0
- package/dist/core/waste-detector.js +160 -6
- package/package.json +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export const DEFAULT_ACTIVITY_LIMIT = 20;
|
|
2
|
+
export const NO_ACTIVITY_MESSAGE = "No activity recorded yet — run a supported workflow (e.g. `compaction run <tool> -- …`) and it will appear here.";
|
|
3
|
+
function axisSource(event, axis) {
|
|
4
|
+
const axisData = event.token_source?.[axis];
|
|
5
|
+
if (!axisData)
|
|
6
|
+
return { source: "unknown" };
|
|
7
|
+
return { source: axisData.source, reason: axisData.unavailable_reason };
|
|
8
|
+
}
|
|
9
|
+
function autoApplyStatus(event) {
|
|
10
|
+
const auto = event.auto_apply;
|
|
11
|
+
if (!auto)
|
|
12
|
+
return "n/a";
|
|
13
|
+
if (auto.applied_automatically === true)
|
|
14
|
+
return "auto-applied (policy)";
|
|
15
|
+
if (auto.preference === "auto-when-gates-pass")
|
|
16
|
+
return "preference: auto-when-gates-pass (application pending)";
|
|
17
|
+
return "ask each time";
|
|
18
|
+
}
|
|
19
|
+
/** Build the rendered rows: newest first, optional surface filter, then limit. */
|
|
20
|
+
export function buildActivityRows(events, options = {}) {
|
|
21
|
+
const limit = options.limit ?? DEFAULT_ACTIVITY_LIMIT;
|
|
22
|
+
// The store appends chronologically; most recent is the LAST line. Newest-first for display.
|
|
23
|
+
const ordered = [...events].reverse();
|
|
24
|
+
const filtered = options.surface ? ordered.filter((event) => event.surface === options.surface) : ordered;
|
|
25
|
+
const shown = filtered.slice(0, Math.max(0, limit));
|
|
26
|
+
return shown.map((event, index) => {
|
|
27
|
+
const input = axisSource(event, "input");
|
|
28
|
+
const output = axisSource(event, "output");
|
|
29
|
+
const outputUnavailable = output.source === "unavailable" || output.source === "unknown";
|
|
30
|
+
const outputCount = typeof event.output_after === "number"
|
|
31
|
+
? event.output_after
|
|
32
|
+
: typeof event.output_before === "number"
|
|
33
|
+
? event.output_before
|
|
34
|
+
: typeof event.output_estimate === "number"
|
|
35
|
+
? event.output_estimate
|
|
36
|
+
: null;
|
|
37
|
+
return {
|
|
38
|
+
position: index + 1,
|
|
39
|
+
activity_event_id: event.activity_event_id ?? "unknown",
|
|
40
|
+
surface: event.surface,
|
|
41
|
+
provider: event.provider ?? "unknown",
|
|
42
|
+
model_label: event.model_label ?? "unknown",
|
|
43
|
+
input_before: typeof event.input_before === "number" ? event.input_before : null,
|
|
44
|
+
input_after: typeof event.input_after === "number" ? event.input_after : null,
|
|
45
|
+
input_source: input.source,
|
|
46
|
+
...(input.reason ? { input_unavailable_reason: input.reason } : {}),
|
|
47
|
+
output_tokens: outputUnavailable ? null : outputCount,
|
|
48
|
+
output_source: output.source,
|
|
49
|
+
...(output.reason ? { output_unavailable_reason: output.reason } : {}),
|
|
50
|
+
policy_used: event.policy_used ?? "none",
|
|
51
|
+
approval_status: event.approval_status ?? "not-recorded",
|
|
52
|
+
auto_apply_status: autoApplyStatus(event),
|
|
53
|
+
sync_status: event.sync_status ?? "local-only"
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function renderInputCell(row) {
|
|
58
|
+
if (row.input_source === "unavailable")
|
|
59
|
+
return `unavailable (${row.input_source})`;
|
|
60
|
+
if (row.input_before === null)
|
|
61
|
+
return `— (${row.input_source})`;
|
|
62
|
+
// Only show before→after when a real reshaping happened (both present); else a measured count.
|
|
63
|
+
if (row.input_after === null)
|
|
64
|
+
return `${row.input_before} measured (${row.input_source})`;
|
|
65
|
+
return `${row.input_before}→${row.input_after} (${row.input_source})`;
|
|
66
|
+
}
|
|
67
|
+
function renderOutputCell(row) {
|
|
68
|
+
if (row.output_tokens === null)
|
|
69
|
+
return `unavailable (${row.output_source})`;
|
|
70
|
+
return `${row.output_tokens} measured (${row.output_source})`;
|
|
71
|
+
}
|
|
72
|
+
export function buildActivityJson(rows, meta) {
|
|
73
|
+
return {
|
|
74
|
+
activity: rows,
|
|
75
|
+
meta: {
|
|
76
|
+
total_events: meta.totalEvents,
|
|
77
|
+
shown: rows.length,
|
|
78
|
+
limit: meta.limit,
|
|
79
|
+
surface_filter: meta.surface ?? null,
|
|
80
|
+
ordering: "append-recency (events carry no wall-clock time); position 1 = most recent",
|
|
81
|
+
labels: "token counts are shown at each event's own axis tier (local-estimate | provider-reported | unavailable); output is a measured token count, never a savings figure; no cost or billing figure is shown"
|
|
82
|
+
},
|
|
83
|
+
...(meta.skippedCount && meta.skippedCount > 0
|
|
84
|
+
? { skipped_note: `${meta.skippedCount} malformed line(s) were skipped (not shown)` }
|
|
85
|
+
: {})
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** Human table. Content-free; honest labels; a header note that ordering is append-recency. */
|
|
89
|
+
export function formatActivityTable(rows, meta) {
|
|
90
|
+
if (rows.length === 0) {
|
|
91
|
+
if (meta.surface) {
|
|
92
|
+
return `No activity recorded for surface "${meta.surface}" yet. ${NO_ACTIVITY_MESSAGE}`;
|
|
93
|
+
}
|
|
94
|
+
return NO_ACTIVITY_MESSAGE;
|
|
95
|
+
}
|
|
96
|
+
const header = ["#", "surface", "provider/model", "input (before→after)", "output tokens", "policy", "approval", "auto-apply", "sync"];
|
|
97
|
+
const table = rows.map((row) => [
|
|
98
|
+
`${row.position}`,
|
|
99
|
+
row.surface,
|
|
100
|
+
`${row.provider}/${row.model_label}`,
|
|
101
|
+
renderInputCell(row),
|
|
102
|
+
renderOutputCell(row),
|
|
103
|
+
row.policy_used,
|
|
104
|
+
row.approval_status,
|
|
105
|
+
row.auto_apply_status,
|
|
106
|
+
row.sync_status
|
|
107
|
+
]);
|
|
108
|
+
const widths = header.map((cell, col) => Math.max(cell.length, ...table.map((line) => line[col].length)));
|
|
109
|
+
const pad = (cells) => cells.map((cell, col) => cell.padEnd(widths[col])).join(" ");
|
|
110
|
+
const surfaceNote = meta.surface ? ` (surface "${meta.surface}")` : "";
|
|
111
|
+
const preamble = [
|
|
112
|
+
`Recent activity${surfaceNote}: showing ${rows.length} of ${meta.totalEvents} recorded run(s).`,
|
|
113
|
+
`Ordered by append recency (activity events carry no wall-clock time); #1 = most recent.`,
|
|
114
|
+
`Token counts are local estimates unless labeled provider-reported; output is a measured count, not a savings figure.`
|
|
115
|
+
];
|
|
116
|
+
const lines = [pad(header), pad(header.map((_, col) => "-".repeat(widths[col]))), ...table.map(pad)];
|
|
117
|
+
const trailer = meta.skippedCount && meta.skippedCount > 0 ? [``, `(${meta.skippedCount} malformed line(s) skipped.)`] : [];
|
|
118
|
+
return [...preamble, ``, ...lines, ...trailer].join("\n");
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=activity-view.js.map
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* explicitly invokes `apiStatus` / `sendRequest`.
|
|
9
9
|
*/
|
|
10
10
|
export { DEFAULT_API_URL, DEFAULT_TIMEOUT_MS, resolveApiConfig, buildHeaders, type ApiConfig, type EnvLike } from "./config.js";
|
|
11
|
+
export { configDir, configPath, readPersistedConfig, writePersistedConfig, maskKey, isLocalDefaultUrl, urlHost, resolveTarget, healthCheckConfig, type PersistedConfig, type ResolvedTarget, type Source } from "./persisted-config.js";
|
|
11
12
|
export { isContentBearing, ConsentError, buildOptimizeRequest, buildEvaluateRequest, buildReportsRequest, previewPayload, type OptimizeBuildOptions, type EvaluateBuildOptions, type ReportsBuildOptions, type PayloadPreview } from "./payload.js";
|
|
12
13
|
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";
|
|
14
|
+
export { CONTENT_BEARING_PAYLOAD_CLASSES, type PayloadClass, type TokenSource, type Consent, type Trace, type TokenUsage, type ProviderMetadata, type OptimizeRequest, type EvaluateRequest, type ReportsRequest, type ApiRequestBody, type StatusResponse, type ApiErrorBody } from "./types.js";
|
|
15
|
+
export { TOOL_NAMES, isToolName, resolveToolName, type ToolName } from "./tool.js";
|
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
* explicitly invokes `apiStatus` / `sendRequest`.
|
|
9
9
|
*/
|
|
10
10
|
export { DEFAULT_API_URL, DEFAULT_TIMEOUT_MS, resolveApiConfig, buildHeaders } from "./config.js";
|
|
11
|
+
// ADDITIVE opt-in persisted-config layer (upgrade/status). Does NOT change the env-only
|
|
12
|
+
// `resolveApiConfig` contract above.
|
|
13
|
+
export { configDir, configPath, readPersistedConfig, writePersistedConfig, maskKey, isLocalDefaultUrl, urlHost, resolveTarget, healthCheckConfig } from "./persisted-config.js";
|
|
11
14
|
export { isContentBearing, ConsentError, buildOptimizeRequest, buildEvaluateRequest, buildReportsRequest, previewPayload } from "./payload.js";
|
|
12
15
|
export { apiStatus, sendRequest, ApiNotConfirmedError, ApiTransportError } from "./client.js";
|
|
13
16
|
export { CONTENT_BEARING_PAYLOAD_CLASSES } from "./types.js";
|
|
17
|
+
export { TOOL_NAMES, isToolName, resolveToolName } from "./tool.js";
|
|
14
18
|
//# sourceMappingURL=index.js.map
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* `enforceConsent`. The preview shows EXACTLY what would be sent (API key redacted), so the
|
|
8
8
|
* user never sends blind. See `docs/ops/cli-api-client-design.md` §3–§5.
|
|
9
9
|
*/
|
|
10
|
-
import { type EvaluateRequest, type OptimizeRequest, type PayloadClass, type ReportsRequest, type Trace } from "./types.js";
|
|
10
|
+
import { type EvaluateRequest, type OptimizeRequest, type PayloadClass, type ReportsRequest, type ToolName, type Trace } from "./types.js";
|
|
11
11
|
/** True when a class carries raw-or-sanitized message CONTENT (requires explicit consent). */
|
|
12
12
|
export declare function isContentBearing(payloadClass: PayloadClass | undefined): boolean;
|
|
13
13
|
/** Thrown by a builder when consent rules are violated. The client must fail closed. */
|
|
@@ -36,6 +36,10 @@ export interface OptimizeBuildOptions {
|
|
|
36
36
|
outputTokens?: number;
|
|
37
37
|
provider?: string;
|
|
38
38
|
runtime?: string;
|
|
39
|
+
/** Content-free coding-tool identity (which tool produced the captured session). */
|
|
40
|
+
tool?: ToolName;
|
|
41
|
+
/** Content-free model identifier (e.g. "claude-sonnet-4-6"); never request content. */
|
|
42
|
+
model?: string;
|
|
39
43
|
labels?: {
|
|
40
44
|
project?: string;
|
|
41
45
|
session?: string;
|
|
@@ -94,12 +94,14 @@ export function buildOptimizeRequest(opts) {
|
|
|
94
94
|
},
|
|
95
95
|
optimization_mode: opts.optimizationMode ?? "recommend"
|
|
96
96
|
};
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
}
|
|
97
|
+
const providerMetadata = {
|
|
98
|
+
...(opts.provider ? { provider: opts.provider } : {}),
|
|
99
|
+
...(opts.runtime ? { runtime: opts.runtime } : {}),
|
|
100
|
+
...(opts.tool ? { tool: opts.tool } : {}),
|
|
101
|
+
...(opts.model ? { model: opts.model } : {})
|
|
102
|
+
};
|
|
103
|
+
if (Object.keys(providerMetadata).length > 0)
|
|
104
|
+
req.provider_metadata = providerMetadata;
|
|
103
105
|
if (opts.labels)
|
|
104
106
|
req.labels = opts.labels;
|
|
105
107
|
return req;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type ApiConfig, type EnvLike } from "./config.js";
|
|
2
|
+
/** On-disk shape. Only these two fields are ever written; nothing else is persisted. */
|
|
3
|
+
export interface PersistedConfig {
|
|
4
|
+
api_url: string;
|
|
5
|
+
api_key: string;
|
|
6
|
+
}
|
|
7
|
+
/** Where a value came from in the resolution precedence (for honest status reporting). */
|
|
8
|
+
export type Source = "flag" | "env" | "file" | "default" | "none";
|
|
9
|
+
export interface ResolvedTarget {
|
|
10
|
+
/** Resolved base URL (no trailing slash). */
|
|
11
|
+
url: string;
|
|
12
|
+
/** Resolved bearer token, or `undefined` when none is configured anywhere. */
|
|
13
|
+
apiKey?: string;
|
|
14
|
+
urlSource: Source;
|
|
15
|
+
keySource: Source;
|
|
16
|
+
}
|
|
17
|
+
/** Resolve the config directory. `COMPACTION_CONFIG_DIR` overrides `~/.compaction` (tests use this). */
|
|
18
|
+
export declare function configDir(env?: EnvLike): string;
|
|
19
|
+
/** Absolute path to the persisted config file. */
|
|
20
|
+
export declare function configPath(env?: EnvLike): string;
|
|
21
|
+
/**
|
|
22
|
+
* Read the persisted config, or `undefined` if absent/empty/malformed. Never throws: a corrupt or
|
|
23
|
+
* partial file resolves to `undefined` (fail-closed to "not configured") rather than crashing a
|
|
24
|
+
* command. Only returns a value when BOTH `api_url` and `api_key` are non-empty strings.
|
|
25
|
+
*/
|
|
26
|
+
export declare function readPersistedConfig(env?: EnvLike): PersistedConfig | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Persist `{api_url, api_key}` at `~/.compaction/config.json` with mode 0600 (dir 0700). The key
|
|
29
|
+
* touches disk ONLY here. Returns the absolute path written. `chmod` is applied explicitly AFTER the
|
|
30
|
+
* write because `writeFileSync`'s `mode` only takes effect on file CREATION (a pre-existing file
|
|
31
|
+
* keeps its old mode otherwise).
|
|
32
|
+
*/
|
|
33
|
+
export declare function writePersistedConfig(config: PersistedConfig, env?: EnvLike): string;
|
|
34
|
+
/**
|
|
35
|
+
* Mask an API key for display. Shows a short prefix + the last 4 characters
|
|
36
|
+
* (e.g. `ck_live_…a1b2`). A key too short to mask safely (< 16 chars, where a prefix+suffix would
|
|
37
|
+
* reveal most of it) is FULLY masked. Empty → `(none)`. The full key is NEVER returned.
|
|
38
|
+
*/
|
|
39
|
+
export declare function maskKey(key: string | undefined): string;
|
|
40
|
+
/** True iff the resolved URL is the LOCAL-dev default — i.e. NO private/hosted endpoint is set. */
|
|
41
|
+
export declare function isLocalDefaultUrl(url: string): boolean;
|
|
42
|
+
/** Extract the HOST (host:port, no scheme/path) for display. Falls back to the raw string. */
|
|
43
|
+
export declare function urlHost(url: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the effective target for the upgrade/status commands.
|
|
46
|
+
*
|
|
47
|
+
* Precedence (independently for url and key): explicit flag > env
|
|
48
|
+
* (`COMPACTION_API_URL` / `COMPACTION_API_KEY`) > persisted file > `DEFAULT_API_URL` (url) / none
|
|
49
|
+
* (key). Pure: it reads the given env + the persisted file and returns a plain object.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveTarget(opts: {
|
|
52
|
+
flagUrl?: string;
|
|
53
|
+
flagKey?: string;
|
|
54
|
+
env?: EnvLike;
|
|
55
|
+
}): ResolvedTarget;
|
|
56
|
+
/** Build a bounded-timeout `ApiConfig` for a health check from a resolved target. */
|
|
57
|
+
export declare function healthCheckConfig(target: ResolvedTarget, timeoutMs?: number): ApiConfig;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compaction API client — PERSISTED configuration (PUBLIC CLI/SDK code).
|
|
3
|
+
*
|
|
4
|
+
* ADDITIVE layer over the env-only `config.ts`: `config.ts` stays pure/env-only and its
|
|
5
|
+
* `resolveApiConfig` contract is UNCHANGED. This module adds an OPT-IN on-disk store used ONLY by
|
|
6
|
+
* `compaction upgrade` / `compaction status` so a private-beta / self-hosted user can save an
|
|
7
|
+
* endpoint + key once instead of re-exporting env vars every shell.
|
|
8
|
+
*
|
|
9
|
+
* HARD RAILS:
|
|
10
|
+
* - The file lives at `~/.compaction/config.json`, written with mode 0600 (dir 0700). An env
|
|
11
|
+
* override `COMPACTION_CONFIG_DIR` redirects it (tests point it at a tmpdir; the real
|
|
12
|
+
* `~/.compaction` is NEVER touched by tests).
|
|
13
|
+
* - There is NO baked-in remote URL — the default stays the LOCAL-dev `DEFAULT_API_URL`.
|
|
14
|
+
* - The API key is written ONLY to this 0600 file. `maskKey` is the ONLY form that may ever be
|
|
15
|
+
* printed, logged, or placed in an artifact/error; the full key is never surfaced anywhere else.
|
|
16
|
+
*/
|
|
17
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { DEFAULT_API_URL, DEFAULT_TIMEOUT_MS } from "./config.js";
|
|
21
|
+
function stripTrailingSlash(url) {
|
|
22
|
+
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
23
|
+
}
|
|
24
|
+
/** Resolve the config directory. `COMPACTION_CONFIG_DIR` overrides `~/.compaction` (tests use this). */
|
|
25
|
+
export function configDir(env = process.env) {
|
|
26
|
+
const override = (env.COMPACTION_CONFIG_DIR ?? "").trim();
|
|
27
|
+
return override !== "" ? override : join(homedir(), ".compaction");
|
|
28
|
+
}
|
|
29
|
+
/** Absolute path to the persisted config file. */
|
|
30
|
+
export function configPath(env = process.env) {
|
|
31
|
+
return join(configDir(env), "config.json");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Read the persisted config, or `undefined` if absent/empty/malformed. Never throws: a corrupt or
|
|
35
|
+
* partial file resolves to `undefined` (fail-closed to "not configured") rather than crashing a
|
|
36
|
+
* command. Only returns a value when BOTH `api_url` and `api_key` are non-empty strings.
|
|
37
|
+
*/
|
|
38
|
+
export function readPersistedConfig(env = process.env) {
|
|
39
|
+
const path = configPath(env);
|
|
40
|
+
if (!existsSync(path))
|
|
41
|
+
return undefined;
|
|
42
|
+
try {
|
|
43
|
+
const raw = JSON.parse(readFileSync(path, "utf8"));
|
|
44
|
+
const url = typeof raw.api_url === "string" ? raw.api_url.trim() : "";
|
|
45
|
+
const key = typeof raw.api_key === "string" ? raw.api_key.trim() : "";
|
|
46
|
+
if (url === "" || key === "")
|
|
47
|
+
return undefined;
|
|
48
|
+
return { api_url: stripTrailingSlash(url), api_key: key };
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Persist `{api_url, api_key}` at `~/.compaction/config.json` with mode 0600 (dir 0700). The key
|
|
56
|
+
* touches disk ONLY here. Returns the absolute path written. `chmod` is applied explicitly AFTER the
|
|
57
|
+
* write because `writeFileSync`'s `mode` only takes effect on file CREATION (a pre-existing file
|
|
58
|
+
* keeps its old mode otherwise).
|
|
59
|
+
*/
|
|
60
|
+
export function writePersistedConfig(config, env = process.env) {
|
|
61
|
+
const dir = configDir(env);
|
|
62
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
63
|
+
const path = join(dir, "config.json");
|
|
64
|
+
const body = `${JSON.stringify({ api_url: stripTrailingSlash(config.api_url.trim()), api_key: config.api_key.trim() }, null, 2)}\n`;
|
|
65
|
+
writeFileSync(path, body, { mode: 0o600 });
|
|
66
|
+
chmodSync(path, 0o600);
|
|
67
|
+
return path;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Mask an API key for display. Shows a short prefix + the last 4 characters
|
|
71
|
+
* (e.g. `ck_live_…a1b2`). A key too short to mask safely (< 16 chars, where a prefix+suffix would
|
|
72
|
+
* reveal most of it) is FULLY masked. Empty → `(none)`. The full key is NEVER returned.
|
|
73
|
+
*/
|
|
74
|
+
export function maskKey(key) {
|
|
75
|
+
const k = (key ?? "").trim();
|
|
76
|
+
if (k === "")
|
|
77
|
+
return "(none)";
|
|
78
|
+
// Too short to reveal a prefix AND a suffix without exposing most of the key → fully mask.
|
|
79
|
+
if (k.length < 16)
|
|
80
|
+
return "…(hidden)";
|
|
81
|
+
return `${k.slice(0, 8)}…${k.slice(-4)}`;
|
|
82
|
+
}
|
|
83
|
+
/** True iff the resolved URL is the LOCAL-dev default — i.e. NO private/hosted endpoint is set. */
|
|
84
|
+
export function isLocalDefaultUrl(url) {
|
|
85
|
+
return stripTrailingSlash(url.trim()) === DEFAULT_API_URL;
|
|
86
|
+
}
|
|
87
|
+
/** Extract the HOST (host:port, no scheme/path) for display. Falls back to the raw string. */
|
|
88
|
+
export function urlHost(url) {
|
|
89
|
+
try {
|
|
90
|
+
return new URL(url).host;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return url;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Resolve the effective target for the upgrade/status commands.
|
|
98
|
+
*
|
|
99
|
+
* Precedence (independently for url and key): explicit flag > env
|
|
100
|
+
* (`COMPACTION_API_URL` / `COMPACTION_API_KEY`) > persisted file > `DEFAULT_API_URL` (url) / none
|
|
101
|
+
* (key). Pure: it reads the given env + the persisted file and returns a plain object.
|
|
102
|
+
*/
|
|
103
|
+
export function resolveTarget(opts) {
|
|
104
|
+
const env = opts.env ?? process.env;
|
|
105
|
+
const file = readPersistedConfig(env);
|
|
106
|
+
const flagUrl = (opts.flagUrl ?? "").trim();
|
|
107
|
+
const envUrl = (env.COMPACTION_API_URL ?? "").trim();
|
|
108
|
+
let url;
|
|
109
|
+
let urlSource;
|
|
110
|
+
if (flagUrl !== "") {
|
|
111
|
+
url = flagUrl;
|
|
112
|
+
urlSource = "flag";
|
|
113
|
+
}
|
|
114
|
+
else if (envUrl !== "") {
|
|
115
|
+
url = envUrl;
|
|
116
|
+
urlSource = "env";
|
|
117
|
+
}
|
|
118
|
+
else if (file) {
|
|
119
|
+
url = file.api_url;
|
|
120
|
+
urlSource = "file";
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
url = DEFAULT_API_URL;
|
|
124
|
+
urlSource = "default";
|
|
125
|
+
}
|
|
126
|
+
const flagKey = (opts.flagKey ?? "").trim();
|
|
127
|
+
const envKey = (env.COMPACTION_API_KEY ?? "").trim();
|
|
128
|
+
let apiKey;
|
|
129
|
+
let keySource;
|
|
130
|
+
if (flagKey !== "") {
|
|
131
|
+
apiKey = flagKey;
|
|
132
|
+
keySource = "flag";
|
|
133
|
+
}
|
|
134
|
+
else if (envKey !== "") {
|
|
135
|
+
apiKey = envKey;
|
|
136
|
+
keySource = "env";
|
|
137
|
+
}
|
|
138
|
+
else if (file) {
|
|
139
|
+
apiKey = file.api_key;
|
|
140
|
+
keySource = "file";
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
apiKey = undefined;
|
|
144
|
+
keySource = "none";
|
|
145
|
+
}
|
|
146
|
+
return { url: stripTrailingSlash(url), apiKey, urlSource, keySource };
|
|
147
|
+
}
|
|
148
|
+
/** Build a bounded-timeout `ApiConfig` for a health check from a resolved target. */
|
|
149
|
+
export function healthCheckConfig(target, timeoutMs = 5000) {
|
|
150
|
+
return {
|
|
151
|
+
url: target.url,
|
|
152
|
+
apiKey: target.apiKey,
|
|
153
|
+
timeoutMs: timeoutMs > 0 ? timeoutMs : DEFAULT_TIMEOUT_MS
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=persisted-config.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical coding-tool identity (PUBLIC CLI/SDK code).
|
|
3
|
+
*
|
|
4
|
+
* The single source of truth for the `tool` attribution value the CLI tags onto a recorded run.
|
|
5
|
+
* It is CONTENT-FREE metadata — a short, fixed identifier of WHICH coding tool produced a captured
|
|
6
|
+
* session (e.g. Claude Code vs Codex vs Cursor), never any prompt/completion/trace content.
|
|
7
|
+
*
|
|
8
|
+
* This union is hand-mirrored — with no cross-package import — by the `tool` enum in
|
|
9
|
+
* `apps/api/src/schemas.ts` and the `tool` CHECK constraint in `apps/control-plane` migration
|
|
10
|
+
* 0007 / 0008. Keep the five values in lock-step across all three.
|
|
11
|
+
*
|
|
12
|
+
* `cursor` is a first-class recognized value so a Cursor run records correctly the moment a Cursor
|
|
13
|
+
* capture adapter lands. No live Cursor (or Codex) CAPTURE adapter ships today — Codex is import
|
|
14
|
+
* only and Cursor's session format is not yet verified — so a real CLI capture currently resolves
|
|
15
|
+
* to `claude-code` / `openai-agents`, or `other` when the source is unknown. The wire never carries
|
|
16
|
+
* a tool the producing adapter did not actually set.
|
|
17
|
+
*/
|
|
18
|
+
export type ToolName = "claude-code" | "codex" | "cursor" | "openai-agents" | "other";
|
|
19
|
+
/** The five canonical tool values, in declaration order. */
|
|
20
|
+
export declare const TOOL_NAMES: readonly ToolName[];
|
|
21
|
+
/** Narrowing guard: true iff `value` is one of the canonical tool names. */
|
|
22
|
+
export declare function isToolName(value: unknown): value is ToolName;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a CONTENT-FREE tool identifier from a capture-adapter id or a trace-source string
|
|
25
|
+
* (e.g. `provenance.captureAdapter` or `trace.source`). Honest by construction: an unknown or
|
|
26
|
+
* absent hint resolves to `other` rather than guessing a specific tool. Never returns a value the
|
|
27
|
+
* caller did not actually produce.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveToolName(hint: string | null | undefined): ToolName;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical coding-tool identity (PUBLIC CLI/SDK code).
|
|
3
|
+
*
|
|
4
|
+
* The single source of truth for the `tool` attribution value the CLI tags onto a recorded run.
|
|
5
|
+
* It is CONTENT-FREE metadata — a short, fixed identifier of WHICH coding tool produced a captured
|
|
6
|
+
* session (e.g. Claude Code vs Codex vs Cursor), never any prompt/completion/trace content.
|
|
7
|
+
*
|
|
8
|
+
* This union is hand-mirrored — with no cross-package import — by the `tool` enum in
|
|
9
|
+
* `apps/api/src/schemas.ts` and the `tool` CHECK constraint in `apps/control-plane` migration
|
|
10
|
+
* 0007 / 0008. Keep the five values in lock-step across all three.
|
|
11
|
+
*
|
|
12
|
+
* `cursor` is a first-class recognized value so a Cursor run records correctly the moment a Cursor
|
|
13
|
+
* capture adapter lands. No live Cursor (or Codex) CAPTURE adapter ships today — Codex is import
|
|
14
|
+
* only and Cursor's session format is not yet verified — so a real CLI capture currently resolves
|
|
15
|
+
* to `claude-code` / `openai-agents`, or `other` when the source is unknown. The wire never carries
|
|
16
|
+
* a tool the producing adapter did not actually set.
|
|
17
|
+
*/
|
|
18
|
+
/** The five canonical tool values, in declaration order. */
|
|
19
|
+
export const TOOL_NAMES = [
|
|
20
|
+
"claude-code",
|
|
21
|
+
"codex",
|
|
22
|
+
"cursor",
|
|
23
|
+
"openai-agents",
|
|
24
|
+
"other"
|
|
25
|
+
];
|
|
26
|
+
/** Narrowing guard: true iff `value` is one of the canonical tool names. */
|
|
27
|
+
export function isToolName(value) {
|
|
28
|
+
return typeof value === "string" && TOOL_NAMES.includes(value);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a CONTENT-FREE tool identifier from a capture-adapter id or a trace-source string
|
|
32
|
+
* (e.g. `provenance.captureAdapter` or `trace.source`). Honest by construction: an unknown or
|
|
33
|
+
* absent hint resolves to `other` rather than guessing a specific tool. Never returns a value the
|
|
34
|
+
* caller did not actually produce.
|
|
35
|
+
*/
|
|
36
|
+
export function resolveToolName(hint) {
|
|
37
|
+
if (!hint)
|
|
38
|
+
return "other";
|
|
39
|
+
const h = hint.trim().toLowerCase();
|
|
40
|
+
if (h === "claude-code" || h === "claude_code")
|
|
41
|
+
return "claude-code";
|
|
42
|
+
if (h === "openai-agents" || h === "openai_agents")
|
|
43
|
+
return "openai-agents";
|
|
44
|
+
if (h.startsWith("codex"))
|
|
45
|
+
return "codex"; // codex, codex_import, codex-exec
|
|
46
|
+
if (h.startsWith("cursor"))
|
|
47
|
+
return "cursor"; // cursor, cursor_import (adapter pending)
|
|
48
|
+
return "other";
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=tool.js.map
|
|
@@ -19,6 +19,20 @@ export type PayloadClass = "metrics_only" | "redacted_structure" | "sanitized_sn
|
|
|
19
19
|
export declare const CONTENT_BEARING_PAYLOAD_CLASSES: ReadonlySet<PayloadClass>;
|
|
20
20
|
/** Honest token-source labels (mirror of `apps/api` `TokenSourceSchema`). Never upgraded. */
|
|
21
21
|
export type TokenSource = "provider-reported" | "local-estimate" | "unknown";
|
|
22
|
+
/** Canonical coding-tool identity (re-exported from the source of truth in `./tool`). */
|
|
23
|
+
export type { ToolName } from "./tool.js";
|
|
24
|
+
import type { ToolName } from "./tool.js";
|
|
25
|
+
/**
|
|
26
|
+
* Content-free provider/tool attribution sent alongside a request. All fields are short
|
|
27
|
+
* identifiers (provider, tool, model name) — NEVER prompt/completion/trace content. The server
|
|
28
|
+
* records them on the content-free usage event so a workspace can be grouped by tool/provider/model.
|
|
29
|
+
*/
|
|
30
|
+
export interface ProviderMetadata {
|
|
31
|
+
provider?: string;
|
|
32
|
+
runtime?: string;
|
|
33
|
+
tool?: ToolName;
|
|
34
|
+
model?: string;
|
|
35
|
+
}
|
|
22
36
|
export interface Consent {
|
|
23
37
|
upload_permitted: boolean;
|
|
24
38
|
redacted: boolean;
|
|
@@ -37,10 +51,7 @@ export interface OptimizeRequest {
|
|
|
37
51
|
trace: Trace;
|
|
38
52
|
consent: Consent;
|
|
39
53
|
payload_class?: PayloadClass;
|
|
40
|
-
provider_metadata?:
|
|
41
|
-
provider?: string;
|
|
42
|
-
runtime?: string;
|
|
43
|
-
};
|
|
54
|
+
provider_metadata?: ProviderMetadata;
|
|
44
55
|
token_usage: TokenUsage;
|
|
45
56
|
evidence_labels?: Record<string, unknown>;
|
|
46
57
|
labels?: {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type PolicyPreference, type PolicyPreferenceScope } from "./policy-preferences.js";
|
|
2
|
+
/** The exact §11a block-4 binary question. No scope menu — the scope is inferred, never asked. */
|
|
3
|
+
export declare const AUTO_APPLY_QUESTION_LINES: readonly string[];
|
|
4
|
+
/** A stored preference records these gate names (the founder's gate list from `auto-apply-gates`). */
|
|
5
|
+
export declare const AUTO_APPLY_PREFERENCE_GATES_REQUIRED: readonly string[];
|
|
6
|
+
export type AutoApplyAnswer = "yes" | "no";
|
|
7
|
+
/**
|
|
8
|
+
* Interpret a raw answer. DEFAULT IS NO: only an explicit `y` / `yes` (case-insensitive, trimmed) is
|
|
9
|
+
* `yes`; empty, `n`, `no`, undefined, or anything unrecognized is `no`. Never throws.
|
|
10
|
+
*/
|
|
11
|
+
export declare function interpretAutoApplyAnswer(raw: string | null | undefined): AutoApplyAnswer;
|
|
12
|
+
/** Inputs from which the SAFEST scope is inferred — never a scope menu; the caller supplies facts. */
|
|
13
|
+
export interface AutoApplyScopeInputs {
|
|
14
|
+
/** REQUIRED. The workflow/tool this run belongs to (never global/cross-tool). */
|
|
15
|
+
tool?: string;
|
|
16
|
+
/** OPTIONAL. The repo, when detectable (content-free identifier). */
|
|
17
|
+
repo?: string;
|
|
18
|
+
/** REQUIRED. The policy type that was approved (content-free identifier). */
|
|
19
|
+
policyType?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface AutoApplyOfferInput {
|
|
22
|
+
/** TRUE only when an explicit in-workflow approval already emitted an approved context. */
|
|
23
|
+
approvedInWorkflow: boolean;
|
|
24
|
+
/** The interpreted answer to the binary question (default no). */
|
|
25
|
+
answer: AutoApplyAnswer;
|
|
26
|
+
/** The facts from which the safest scope is inferred. */
|
|
27
|
+
scope: AutoApplyScopeInputs;
|
|
28
|
+
}
|
|
29
|
+
export type AutoApplyOfferDecision =
|
|
30
|
+
/** No approval happened, or the answer was no/default — save nothing (the common, safe path). */
|
|
31
|
+
{
|
|
32
|
+
action: "skip";
|
|
33
|
+
reason: string;
|
|
34
|
+
}
|
|
35
|
+
/** Approval + explicit yes, but the scope could not be inferred safely — fail-closed, save nothing. */
|
|
36
|
+
| {
|
|
37
|
+
action: "blocked";
|
|
38
|
+
problems: string[];
|
|
39
|
+
}
|
|
40
|
+
/** Approval + explicit yes + a safe scope — the CLI should upsert this preference (applies nothing). */
|
|
41
|
+
| {
|
|
42
|
+
action: "save";
|
|
43
|
+
scope: PolicyPreferenceScope;
|
|
44
|
+
preference: "auto-when-gates-pass";
|
|
45
|
+
gatesRequired: string[];
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the binary offer into a DECISION. PURE — no I/O, no side effects. Enforces every rail:
|
|
49
|
+
* offer only after approval; default no; safest inferred scope; never global/cross-tool. Saving is
|
|
50
|
+
* left to the CLI boundary (`savePolicyPreference`); this never applies anything.
|
|
51
|
+
*/
|
|
52
|
+
export declare function resolveAutoApplyOffer(input: AutoApplyOfferInput): AutoApplyOfferDecision;
|
|
53
|
+
/**
|
|
54
|
+
* The §11a block-5 saved-preference confirmation (scope · gates · undo hint). Honest: states the
|
|
55
|
+
* preference is stored and that every FUTURE application would be logged, evidence-labeled, undoable —
|
|
56
|
+
* it does NOT claim anything was applied. No wall-clock, no content.
|
|
57
|
+
*/
|
|
58
|
+
export declare function formatSavedPreferenceConfirmation(preference: PolicyPreference): string;
|