@dsh-cc/compaction-cost-gate 0.8.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js ADDED
@@ -0,0 +1,368 @@
1
+ /**
2
+ * Cost-gated plan-step compaction service (design
3
+ * docs/plans/2026-09-20-cost-gated-plan-step-compaction.md §3.4–§3.6).
4
+ *
5
+ * Two seams: a mid-turn `tools/post-execute` listener ARMS a boundary when a
6
+ * `todo_write` transitions a todo to completed (observe-only, decision
7
+ * untouched); an `agent/status` idle listener runs the §3.3 cost gate for the
8
+ * latched root session only and, on a pass in `mode: 'on'`, calls
9
+ * `ctx.compaction.compactNow` with a preservation hint parked immediately
10
+ * before the call and a fresh abort signal (the turn-scoped signal is dead by
11
+ * idle). Subagent agents never arm, count, or trigger.
12
+ *
13
+ * The compaction engine is deliberately NOT injected (cordis strict-read
14
+ * would kill the service where compaction is absent); it is read through the
15
+ * guarded {@link optionalCompaction} accessor, and absence inactivates the
16
+ * gate with one log line and one ledger row.
17
+ *
18
+ * @module @dsh-cc/compaction-cost-gate
19
+ */
20
+ import { Service } from '@deepseek-ai/cordis';
21
+ import { CommandId } from '@deepseek-ai/dsh-commands';
22
+ import { ManualCompactionError } from '@deepseek-ai/dsh-compaction';
23
+ import { deriveEventMessage } from '@deepseek-ai/dsh-session';
24
+ import { setCompactHint, takeCompactHint } from '@dsh-cc/compaction-basic';
25
+ import { resolvePrice } from '@dsh-cc/command-cost';
26
+ import { createUserMessage } from '@deepseek-ai/dsh-llm';
27
+ import { evaluateGate } from "./gate.js";
28
+ import { CostGateLedger, projectKeyOf } from "./ledger.js";
29
+ import { registerCostGateSettings } from "./settings.js";
30
+ import { diffTodos } from "./todo-diff.js";
31
+ import { FAILURE_FUSE, } from "./types.js";
32
+ export { CostGateLedger, projectKeyOf } from "./ledger.js";
33
+ export { evaluateGate } from "./gate.js";
34
+ export { foldCounters } from "./fold-counters.js";
35
+ export { diffTodos } from "./todo-diff.js";
36
+ export { SETTINGS_NAMESPACE, registerCostGateSettings } from "./settings.js";
37
+ export * from "./types.js";
38
+ function dshHomeFn(ctx) {
39
+ try {
40
+ return ctx.dshHomePath;
41
+ }
42
+ catch {
43
+ return undefined;
44
+ }
45
+ }
46
+ /**
47
+ * The compaction service is optional at mount: reading an uninjected cordis
48
+ * context property throws, so the read is guarded (never `inject`ed).
49
+ */
50
+ function optionalCompaction(ctx) {
51
+ try {
52
+ return ctx.compaction;
53
+ }
54
+ catch {
55
+ return undefined;
56
+ }
57
+ }
58
+ /**
59
+ * Cost-gated plan-step compaction service. One instance per mounted plugin;
60
+ * state is keyed by the latched root session id and lives in memory only.
61
+ */
62
+ export class CompactionCostGate extends Service {
63
+ /** The token meter prices surface nodes for the gate arithmetic. */
64
+ static inject = ['tokenMeter'];
65
+ readSettings;
66
+ ledger;
67
+ now;
68
+ /** Content of the todo whose completion armed the current boundary. */
69
+ rootLatch;
70
+ stats = new Map();
71
+ constructor(ctx, deps = {}) {
72
+ super(ctx, 'compactionCostGate');
73
+ this.ctx = ctx;
74
+ this.readSettings = deps.readSettings ?? registerCostGateSettings(ctx);
75
+ this.now = deps.now ?? Date.now;
76
+ const home = dshHomeFn(ctx);
77
+ this.ledger = deps.ledger
78
+ ?? (home === undefined
79
+ ? undefined
80
+ : new CostGateLedger(home('compaction-cost-gate'), (error) => {
81
+ ctx.logger.warn(`compaction-cost-gate: ledger write failed: ${error instanceof Error ? error.message : String(error)}`);
82
+ }));
83
+ this.registerListeners();
84
+ }
85
+ /** Register the observe-only listeners (two-seam split, §3.4). */
86
+ registerListeners() {
87
+ this.ctx.on('llm/stream', (options, next) => {
88
+ this.observeStream(options);
89
+ return next();
90
+ }, { global: true, prepend: true });
91
+ this.ctx.on('tools/post-execute', async (exec, _result, next) => {
92
+ this.observePostExecute(exec);
93
+ // Observe-only: the downstream decision passes through unchanged.
94
+ return next();
95
+ });
96
+ this.ctx.on('agent/status', ({ agent, status }) => {
97
+ if (status === 'idle')
98
+ void this.evaluateIdle(agent);
99
+ });
100
+ this.ctx.on('session/disposed', (session) => {
101
+ const id = String(session.header.id);
102
+ if (id === this.rootLatch) {
103
+ this.rootLatch = undefined;
104
+ this.stats.delete(id);
105
+ }
106
+ });
107
+ }
108
+ /** Fresh per-root-session state (§3.2). */
109
+ freshStats() {
110
+ return {
111
+ boundaryArmed: false,
112
+ streamRequestCount: 0,
113
+ completedSteps: 0,
114
+ lastTodoSnapshot: new Map(),
115
+ rewriteDebt: [],
116
+ cooldownUntil: 0,
117
+ consecutiveFailures: 0,
118
+ paused: false,
119
+ };
120
+ }
121
+ /**
122
+ * Latch the root session on the first main-loop observation and count
123
+ * main-loop requests for it (auxiliary purposes never latch or count);
124
+ * amortize rewrite debt by the requests it has since observed.
125
+ */
126
+ observeStream(options) {
127
+ if (options.purpose !== undefined || options.sessionId === undefined)
128
+ return;
129
+ const id = String(options.sessionId);
130
+ if (this.rootLatch === undefined) {
131
+ this.rootLatch = id;
132
+ this.stats.set(id, this.freshStats());
133
+ }
134
+ const state = this.stats.get(id);
135
+ if (state === undefined)
136
+ return;
137
+ state.streamRequestCount += 1;
138
+ state.lastProvider = options.provider;
139
+ state.lastModel = options.model;
140
+ for (const entry of state.rewriteDebt)
141
+ entry.requestsSince += 1;
142
+ }
143
+ /**
144
+ * Detection seam: filter `todo_write`, diff the snapshot, and arm the
145
+ * boundary on any transition to completed — for the latched root session
146
+ * only. Never mutates the tool decision.
147
+ */
148
+ observePostExecute(exec) {
149
+ if (exec.name !== 'todo_write')
150
+ return;
151
+ if (this.rootLatch === undefined)
152
+ return;
153
+ const agentSession = exec.agent?.session;
154
+ if (agentSession === undefined || String(agentSession.header.id) !== this.rootLatch)
155
+ return;
156
+ const state = this.stats.get(this.rootLatch);
157
+ if (state === undefined)
158
+ return;
159
+ const todos = this.parseTodos(exec.arguments);
160
+ if (todos === undefined)
161
+ return;
162
+ const result = diffTodos(state.lastTodoSnapshot, todos);
163
+ if (result.armed) {
164
+ state.boundaryArmed = true;
165
+ state.lastCompletedTitle = result.newlyCompleted[result.newlyCompleted.length - 1] ?? '';
166
+ }
167
+ state.lastTodoSnapshot = new Map(result.snapshot);
168
+ state.completedSteps = result.completedSteps;
169
+ }
170
+ /** Action seam: evaluate the gate at idle for the latched root session only. */
171
+ async evaluateIdle(agent) {
172
+ const settings = this.readSettings();
173
+ if (!settings.enabled)
174
+ return; // ships dark
175
+ if (this.rootLatch === undefined)
176
+ return;
177
+ if (String(agent.session.header.id) !== this.rootLatch)
178
+ return;
179
+ const state = this.stats.get(this.rootLatch);
180
+ if (state === undefined)
181
+ return;
182
+ // Measure the last compaction's reduction at the next idle evaluation.
183
+ if (state.preCompactContextTokens !== undefined && state.preCompactContextTokens > 0) {
184
+ const current = this.contextTokens(agent.session);
185
+ const measured = Math.max(0, (state.preCompactContextTokens - current) / state.preCompactContextTokens);
186
+ state.lastShrink = measured;
187
+ delete state.preCompactContextTokens;
188
+ }
189
+ if (!state.boundaryArmed)
190
+ return;
191
+ state.boundaryArmed = false; // disarm unconditionally: one evaluation per boundary
192
+ if (state.paused)
193
+ return;
194
+ const now = this.now();
195
+ if (now < state.cooldownUntil) {
196
+ this.write('skipped:cooldown', { mode: settings.mode, reason: 'cooldown active' });
197
+ return;
198
+ }
199
+ const engine = optionalCompaction(this.ctx);
200
+ if (engine === undefined) {
201
+ this.ctx.logger.warn('compaction-cost-gate: no compaction service on the host context; gate inactive');
202
+ this.write('compaction-unavailable', { mode: settings.mode, reason: 'compaction-unavailable' });
203
+ return;
204
+ }
205
+ const contextTokens = this.contextTokens(agent.session);
206
+ const pendingSteps = this.pendingSteps(state);
207
+ const price = this.resolvePriceFor(settings, state);
208
+ const outcome = evaluateGate({
209
+ contextTokens,
210
+ streamRequestCount: state.streamRequestCount,
211
+ completedSteps: state.completedSteps,
212
+ pendingSteps,
213
+ ...(state.lastShrink !== undefined ? { lastShrink: state.lastShrink } : {}),
214
+ rewriteDebt: state.rewriteDebt,
215
+ margin: settings.margin,
216
+ ...(price !== undefined ? { price } : {}),
217
+ ...(settings.windowPressureTokens !== undefined
218
+ ? { windowPressureTokens: settings.windowPressureTokens }
219
+ : {}),
220
+ });
221
+ const row = {
222
+ mode: settings.mode,
223
+ contextTokens,
224
+ projectedSavedInput: outcome.projectedSavedInput,
225
+ rewriteCost: outcome.rewriteCost,
226
+ debtTokens: outcome.debtTokens,
227
+ pendingSteps: outcome.pendingSteps,
228
+ requestsPerStep: outcome.requestsPerStep,
229
+ shrink: outcome.shrink,
230
+ margin: settings.margin,
231
+ pass: outcome.pass,
232
+ windowPressureOverride: outcome.windowPressureOverride,
233
+ provider: state.lastProvider,
234
+ model: state.lastModel,
235
+ };
236
+ this.write('gate', row);
237
+ if (!outcome.pass)
238
+ return;
239
+ if (settings.mode === 'dry-run')
240
+ return;
241
+ await this.compactNow(engine, agent, contextTokens, settings, outcome.rewriteCost);
242
+ }
243
+ /**
244
+ * Hint-then-call: the hint must survive only the instant between set and
245
+ * consume; it is cleared on every exit path. A fresh AbortController (the
246
+ * turn-scoped signal is dead by idle and must never be reused).
247
+ */
248
+ async compactNow(engine, agent, contextTokens, settings, rewriteCost) {
249
+ const state = this.stats.get(this.rootLatch ?? '');
250
+ setCompactHint(agent, `plan-step-complete:${state?.lastCompletedTitle ?? 'todo'}`);
251
+ try {
252
+ const result = await engine.compactNow(agent, new AbortController().signal, CommandId('compaction-cost-gate'));
253
+ // Success resets the fuse and starts cooldown + debt amortization.
254
+ if (state !== undefined) {
255
+ state.consecutiveFailures = 0;
256
+ state.cooldownUntil = this.now() + settings.cooldownMs;
257
+ state.rewriteDebt.push({ tokens: rewriteCost, requestsSince: 0 });
258
+ state.preCompactContextTokens = contextTokens;
259
+ }
260
+ this.write('compacted', {
261
+ mode: settings.mode,
262
+ contextTokens,
263
+ rewriteCost,
264
+ reason: result === null ? 'no-compactable-history' : 'compacted',
265
+ });
266
+ this.ctx.logger.info(`compaction-cost-gate: compacted at plan-step boundary `
267
+ + `(context ~${contextTokens} tokens, debt ${rewriteCost})`);
268
+ }
269
+ catch (error) {
270
+ if (error instanceof ManualCompactionError && (error.code === 'busy' || error.code === 'cancelled')) {
271
+ // Expected classes: count toward nothing, reset nothing.
272
+ this.write(`skipped:${error.code}`, { mode: settings.mode, reason: error.message });
273
+ return;
274
+ }
275
+ const code = error instanceof ManualCompactionError ? error.code : 'unexpected';
276
+ this.write(`failed:${code}`, { mode: settings.mode, reason: error instanceof Error ? error.message : String(error) });
277
+ if (state !== undefined) {
278
+ state.consecutiveFailures += 1;
279
+ if (state.consecutiveFailures >= FAILURE_FUSE) {
280
+ state.paused = true;
281
+ this.notifyPaused(agent, state.consecutiveFailures);
282
+ }
283
+ }
284
+ }
285
+ finally {
286
+ // Cleared on ANY failure path (and after success) so a stale hint can
287
+ // never ride a later turn.
288
+ takeCompactHint(agent);
289
+ }
290
+ }
291
+ /** One durable model-visible pause notice pointing at manual `/compact`. */
292
+ notifyPaused(agent, failures) {
293
+ const text = `compaction-cost-gate: failed ${failures} consecutive time(s); `
294
+ + 'auto compaction paused for this session — run /compact manually';
295
+ try {
296
+ agent.inject?.(createUserMessage({
297
+ content: [{ type: 'text', text }],
298
+ source: { kind: 'plugin', plugin: 'compaction-cost-gate' },
299
+ }));
300
+ }
301
+ catch (error) {
302
+ this.ctx.logger.warn(`compaction-cost-gate: failed to inject pause notice: ${error instanceof Error ? error.message : String(error)}`);
303
+ }
304
+ }
305
+ /** Resolve the optional pricing layer from the last observed provider/model. */
306
+ resolvePriceFor(settings, state) {
307
+ if (settings.modelTable === undefined || state.lastProvider === undefined || state.lastModel === undefined) {
308
+ return undefined;
309
+ }
310
+ const price = resolvePrice(settings.modelTable, state.lastProvider, state.lastModel);
311
+ if (price === undefined)
312
+ return undefined;
313
+ return { cacheReadPerMTok: price.cacheReadPerMTok, cacheWritePerMTok: price.cacheWritePerMTok };
314
+ }
315
+ /**
316
+ * Σ estimateMessage over message-carrying surface nodes — the shadow-aware
317
+ * accessor (`session.surface.nodes` + `eventAt`), never raw events, so
318
+ * spans shadowed by prior compactions are not double-counted.
319
+ */
320
+ contextTokens(session) {
321
+ let total = 0;
322
+ for (const seq of [...session.surface.nodes]) {
323
+ const event = session.eventAt(seq);
324
+ const message = event === undefined ? null : deriveEventMessage(event);
325
+ if (message !== undefined && message !== null) {
326
+ total += this.ctx.tokenMeter.estimateMessage(message);
327
+ }
328
+ }
329
+ return total;
330
+ }
331
+ /** Non-completed todos in the latest snapshot. */
332
+ pendingSteps(state) {
333
+ let pending = 0;
334
+ for (const status of state.lastTodoSnapshot.values()) {
335
+ if (status !== 'completed')
336
+ pending += 1;
337
+ }
338
+ return pending;
339
+ }
340
+ /** Parse the todos array out of the `todo_write` tool arguments. */
341
+ parseTodos(arguments_) {
342
+ const todos = arguments_?.todos;
343
+ if (!Array.isArray(todos))
344
+ return undefined;
345
+ const items = [];
346
+ for (const item of todos) {
347
+ const candidate = item;
348
+ if (typeof candidate.content !== 'string' || typeof candidate.status !== 'string')
349
+ return undefined;
350
+ if (candidate.status !== 'pending' && candidate.status !== 'in_progress' && candidate.status !== 'completed') {
351
+ return undefined;
352
+ }
353
+ items.push({ content: candidate.content, status: candidate.status });
354
+ }
355
+ return todos;
356
+ }
357
+ /** Fire-and-forget ledger append; errors are swallowed by the ledger. */
358
+ write(kind, extra) {
359
+ this.ledger?.append(projectKeyOf(process.cwd()), {
360
+ ts: new Date().toISOString(),
361
+ sessionId: this.rootLatch ?? '',
362
+ kind,
363
+ ...extra,
364
+ });
365
+ }
366
+ }
367
+ export default CompactionCostGate;
368
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@dsh-cc/compaction-cost-gate`.
3
+ * @module @dsh-cc/compaction-cost-gate/invariant
4
+ */
5
+ import type { Context } from '@deepseek-ai/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "compaction-cost-gate-invariant";
8
+ /** Services required before the companion can register. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
16
+ //# sourceMappingURL=invariant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariant.d.ts","sourceRoot":"","sources":["../src/invariant.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,oCAAoC;AACpC,eAAO,MAAM,IAAI,mCAAmC,CAAA;AACpD,2DAA2D;AAC3D,eAAO,MAAM,MAAM,UAAiB,CAAA;AAUpC;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,OAAO,KAAG,OAAO,CAAC,MAAM,IAAI,CACU,CAAA"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Package-owned invariant companion for `@dsh-cc/compaction-cost-gate`.
3
+ * @module @dsh-cc/compaction-cost-gate/invariant
4
+ */
5
+ const PACKAGE_NAME = '@dsh-cc/compaction-cost-gate';
6
+ /** Cordis companion plugin name. */
7
+ export const name = 'compaction-cost-gate-invariant';
8
+ /** Services required before the companion can register. */
9
+ export const inject = ['invariants'];
10
+ /**
11
+ * No runtime invariant: the gate is a pure arithmetic fold unit-tested
12
+ * table-side, the two-seam split (post-execute arming / idle action) is
13
+ * pinned by the service and composition specs, and the harness compaction
14
+ * engine already enforces the idle-only `compactNow` contract itself.
15
+ */
16
+ const install = () => { };
17
+ /**
18
+ * Register this package's invariant companion.
19
+ * @param ctx - Cordis context carrying the invariant service.
20
+ * @returns the installed registration's disposer after setup succeeds.
21
+ */
22
+ export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
23
+ /* jscpd:ignore-end */
24
+ //# sourceMappingURL=invariant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariant.js","sourceRoot":"","sources":["../src/invariant.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,YAAY,GAAG,8BAA8B,CAAA;AAEnD,oCAAoC;AACpC,MAAM,CAAC,MAAM,IAAI,GAAG,gCAAgC,CAAA;AACpD,2DAA2D;AAC3D,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,YAAY,CAAC,CAAA;AAEpC;;;;;GAKG;AACH,MAAM,OAAO,GAAuB,GAAG,EAAE,GAAE,CAAC,CAAA;AAE5C;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,GAAY,EAAuB,EAAE,CACzD,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAA;AACjE,sBAAsB"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * JSONL ledger for cost-gate decisions: `<dshHome>/compaction-cost-gate/<projectKey>.jsonl`.
3
+ * Append-only, fire-and-forget (the arming/action paths never await on it),
4
+ * error-swallowing into the injected sink (cache-health ledger discipline).
5
+ * @module @dsh-cc/compaction-cost-gate/ledger
6
+ */
7
+ /** One ledger row (one JSON object per jsonl line). */
8
+ export interface LedgerRow {
9
+ /** ISO timestamp of the observation. */
10
+ readonly ts: string;
11
+ /** Root session the row belongs to. */
12
+ readonly sessionId: string;
13
+ /**
14
+ * Row kind: `gate` (an idle evaluation with both sides of the inequality),
15
+ * `compacted`, `skipped:<class>` (expected busy/cancelled/cooldown), or
16
+ * `compaction-unavailable`.
17
+ */
18
+ readonly kind: string;
19
+ /** Mode in effect at evaluation time. */
20
+ readonly mode: 'dry-run' | 'on';
21
+ /** Σ estimateMessage over message-carrying surface nodes at the boundary. */
22
+ readonly contextTokens?: number;
23
+ readonly projectedSavedInput?: number;
24
+ readonly rewriteCost?: number;
25
+ readonly debtTokens?: number;
26
+ readonly pendingSteps?: number;
27
+ readonly requestsPerStep?: number;
28
+ readonly shrink?: number;
29
+ readonly margin?: number;
30
+ /** Whether the gate passed (undefined for rows without a gate decision). */
31
+ readonly pass?: boolean;
32
+ /** Window-pressure override in effect for this evaluation. */
33
+ readonly windowPressureOverride?: boolean;
34
+ readonly provider?: string;
35
+ readonly model?: string;
36
+ /** Free-text reason for skip/failure rows. */
37
+ readonly reason?: string;
38
+ }
39
+ /** Error sink (the plugin passes a warn-logger); never throws. */
40
+ export type LedgerErrorSink = (error: unknown) => void;
41
+ /** projectKey = 8-hex sha256 of the session cwd (cache-health idiom). */
42
+ export declare function projectKeyOf(cwd: string): string;
43
+ /**
44
+ * Append-only ledger store. One instance per mounted plugin; `root` is
45
+ * `dshHomePath('compaction-cost-gate')`.
46
+ */
47
+ export declare class CostGateLedger {
48
+ readonly root: string;
49
+ private readonly onError;
50
+ /** Per-file write chain: serializes appends so rows keep decision order (still floating). */
51
+ private readonly pending;
52
+ constructor(root: string, onError?: LedgerErrorSink);
53
+ /** Ledger file path for one project. */
54
+ pathFor(projectKey: string): string;
55
+ /** Await all outstanding appends (test/diagnostic seam; never used on event paths). */
56
+ flush(): Promise<void>;
57
+ /** Append one row as a FLOATING promise chained per file (call order preserved). Never await on an event path. */
58
+ append(projectKey: string, row: LedgerRow): void;
59
+ }
60
+ //# sourceMappingURL=ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,uDAAuD;AACvD,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,uCAAuC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAA;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAA;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,kEAAkE;AAClE,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;AAEtD,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;GAGG;AACH,qBAAa,cAAc;IAKvB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,6FAA6F;IAC7F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;gBAGhD,IAAI,EAAE,MAAM,EACJ,OAAO,GAAE,eAA0B;IAGtD,wCAAwC;IACxC,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAInC,uFAAuF;IACjF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,kHAAkH;IAClH,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,IAAI;CAiBjD"}
package/lib/ledger.js ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * JSONL ledger for cost-gate decisions: `<dshHome>/compaction-cost-gate/<projectKey>.jsonl`.
3
+ * Append-only, fire-and-forget (the arming/action paths never await on it),
4
+ * error-swallowing into the injected sink (cache-health ledger discipline).
5
+ * @module @dsh-cc/compaction-cost-gate/ledger
6
+ */
7
+ import { appendFile, mkdir } from 'node:fs/promises';
8
+ import { createHash } from 'node:crypto';
9
+ import { dirname, join } from 'node:path';
10
+ /** projectKey = 8-hex sha256 of the session cwd (cache-health idiom). */
11
+ export function projectKeyOf(cwd) {
12
+ return createHash('sha256').update(cwd).digest('hex').slice(0, 8);
13
+ }
14
+ /**
15
+ * Append-only ledger store. One instance per mounted plugin; `root` is
16
+ * `dshHomePath('compaction-cost-gate')`.
17
+ */
18
+ export class CostGateLedger {
19
+ root;
20
+ onError;
21
+ /** Per-file write chain: serializes appends so rows keep decision order (still floating). */
22
+ pending = new Map();
23
+ constructor(root, onError = () => { }) {
24
+ this.root = root;
25
+ this.onError = onError;
26
+ }
27
+ /** Ledger file path for one project. */
28
+ pathFor(projectKey) {
29
+ return join(this.root, `${projectKey}.jsonl`);
30
+ }
31
+ /** Await all outstanding appends (test/diagnostic seam; never used on event paths). */
32
+ async flush() {
33
+ await Promise.all([...this.pending.values()]);
34
+ }
35
+ /** Append one row as a FLOATING promise chained per file (call order preserved). Never await on an event path. */
36
+ append(projectKey, row) {
37
+ const file = this.pathFor(projectKey);
38
+ const write = async () => {
39
+ await mkdir(dirname(file), { recursive: true });
40
+ await appendFile(file, `${JSON.stringify(row)}\n`, 'utf8');
41
+ };
42
+ const prev = this.pending.get(file) ?? Promise.resolve();
43
+ const next = prev.then(write, write);
44
+ this.pending.set(file, next);
45
+ next.catch((error) => {
46
+ try {
47
+ this.onError(error);
48
+ }
49
+ catch {
50
+ // sink failure must never propagate
51
+ }
52
+ });
53
+ }
54
+ }
55
+ //# sourceMappingURL=ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAsCzC,yEAAyE;AACzE,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AACnE,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,cAAc;IAKd;IACQ;IALnB,6FAA6F;IAC5E,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAA;IAE3D,YACW,IAAY,EACJ,UAA2B,GAAG,EAAE,GAAE,CAAC;QAD3C,SAAI,GAAJ,IAAI,CAAQ;QACJ,YAAO,GAAP,OAAO,CAA4B;IACnD,CAAC;IAEJ,wCAAwC;IACxC,OAAO,CAAC,UAAkB;QACxB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,UAAU,QAAQ,CAAC,CAAA;IAC/C,CAAC;IAED,uFAAuF;IACvF,KAAK,CAAC,KAAK;QACT,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAC/C,CAAC;IAED,kHAAkH;IAClH,MAAM,CAAC,UAAkB,EAAE,GAAc;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;QACrC,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;YACtC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YAC/C,MAAM,UAAU,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QAC5D,CAAC,CAAA;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAA;QACxD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QACpC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC5B,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACnB,IAAI,CAAC;gBACH,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YACrB,CAAC;YAAC,MAAM,CAAC;gBACP,oCAAoC;YACtC,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;CACF"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Settings namespace `cc-compaction-cost-gate` (design §3.6), registered
3
+ * idempotently through the `@dsh-cc/settings-ns` safe helpers (PR #82
4
+ * collision rules — mounted inside the cc preset beside ten other settings
5
+ * users). Without a settings provider the schema defaults apply.
6
+ * @module @dsh-cc/compaction-cost-gate/settings
7
+ */
8
+ import z from '@deepseek-ai/schemastery';
9
+ import type { Context } from '@deepseek-ai/cordis';
10
+ import type { SettingsNamespace } from '@deepseek-ai/dsh-settings';
11
+ import type { CostGateSettings } from './types.ts';
12
+ /** The settings namespace carrying the cost-gate settings. */
13
+ export declare const SETTINGS_NAMESPACE: SettingsNamespace;
14
+ export declare const DEFAULT_SETTINGS: CostGateSettings;
15
+ export declare const SettingsSchema: z<CostGateSettings>;
16
+ /**
17
+ * Register the namespace and return the live settings reader. Idempotent
18
+ * through the shared `registerNamespaceSafe` helper: a duplicate mount under
19
+ * the /clear create-before-dispose overlap window degrades to a live read of
20
+ * the already-registered namespace instead of failing the preset mount.
21
+ * @param ctx - the host context.
22
+ * @returns a per-use settings reader (never undefined); hyphenated settings
23
+ * keys are mapped onto the camelCase resolved shape.
24
+ */
25
+ export declare function registerCostGateSettings(ctx: Context): () => CostGateSettings;
26
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAGlE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,8DAA8D;AAC9D,eAAO,MAAM,kBAAkB,EAAgC,iBAAiB,CAAA;AAEhF,eAAO,MAAM,gBAAgB,EAAE,gBAK9B,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,CAAC,CAAC,gBAAgB,CAOX,CAAA;AAEpC;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,gBAAgB,CAwB7E"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Settings namespace `cc-compaction-cost-gate` (design §3.6), registered
3
+ * idempotently through the `@dsh-cc/settings-ns` safe helpers (PR #82
4
+ * collision rules — mounted inside the cc preset beside ten other settings
5
+ * users). Without a settings provider the schema defaults apply.
6
+ * @module @dsh-cc/compaction-cost-gate/settings
7
+ */
8
+ import z from '@deepseek-ai/schemastery';
9
+ import { registerNamespaceSafe } from '@dsh-cc/settings-ns';
10
+ /** The settings namespace carrying the cost-gate settings. */
11
+ export const SETTINGS_NAMESPACE = 'cc-compaction-cost-gate';
12
+ export const DEFAULT_SETTINGS = {
13
+ enabled: false,
14
+ mode: 'dry-run',
15
+ margin: 1.0,
16
+ cooldownMs: 600_000,
17
+ };
18
+ export const SettingsSchema = z.object({
19
+ enabled: z.boolean().default(false),
20
+ mode: z.union(['dry-run', 'on']).default('dry-run'),
21
+ margin: z.number().default(1.0),
22
+ 'cooldown-ms': z.number().default(600_000),
23
+ 'window-pressure-tokens': z.number(),
24
+ 'model-table': z.any(),
25
+ });
26
+ /**
27
+ * Register the namespace and return the live settings reader. Idempotent
28
+ * through the shared `registerNamespaceSafe` helper: a duplicate mount under
29
+ * the /clear create-before-dispose overlap window degrades to a live read of
30
+ * the already-registered namespace instead of failing the preset mount.
31
+ * @param ctx - the host context.
32
+ * @returns a per-use settings reader (never undefined); hyphenated settings
33
+ * keys are mapped onto the camelCase resolved shape.
34
+ */
35
+ export function registerCostGateSettings(ctx) {
36
+ const read = registerNamespaceSafe(ctx, SETTINGS_NAMESPACE, SettingsSchema);
37
+ return () => {
38
+ const raw = read();
39
+ if (raw === undefined)
40
+ return { ...DEFAULT_SETTINGS };
41
+ return {
42
+ enabled: raw.enabled === true,
43
+ mode: raw.mode === 'on' ? 'on' : 'dry-run',
44
+ margin: typeof raw.margin === 'number' ? raw.margin : DEFAULT_SETTINGS.margin,
45
+ cooldownMs: typeof raw['cooldown-ms'] === 'number'
46
+ ? raw['cooldown-ms']
47
+ : DEFAULT_SETTINGS.cooldownMs,
48
+ ...(typeof raw['window-pressure-tokens'] === 'number'
49
+ ? { windowPressureTokens: raw['window-pressure-tokens'] }
50
+ : {}),
51
+ ...(Array.isArray(raw['model-table'])
52
+ ? { modelTable: raw['model-table'] }
53
+ : {}),
54
+ };
55
+ };
56
+ }
57
+ //# sourceMappingURL=settings.js.map