@dsh-cc/context-crusher 0.6.2

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.
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Settings namespace registration for the `cc-context-compression` overlay.
3
+ * Model-aliases style: the namespace is registered once at mount; the live
4
+ * scope is re-read on EVERY use so a settings write applies immediately.
5
+ * @module @dsh-cc/context-crusher/settings
6
+ */
7
+ import { Config, SettingsSchema } from "./config.js";
8
+ /** The settings namespace carrying the live crusher overlay. */
9
+ export const SETTINGS_NAMESPACE = 'cc-context-compression';
10
+ /**
11
+ * Register the settings namespace and return a per-use reader. Graceful
12
+ * without a settings provider: the reader resolves `undefined` and callers
13
+ * fall back to the config defaults.
14
+ * @param ctx - the plug context.
15
+ * @param base - resolved config-layer defaults (validated once here too).
16
+ * @returns the live scope reader.
17
+ */
18
+ export function registerSettings(ctx, base) {
19
+ const settings = ctx.get('settings');
20
+ const scope = settings?.register(SETTINGS_NAMESPACE, SettingsSchema, {
21
+ validate: (value) => {
22
+ // Re-run the same cross-field validation as the config layer so a
23
+ // half-written scope is rejected at write time.
24
+ const merged = { ...base, ...value };
25
+ Config(merged);
26
+ },
27
+ });
28
+ return () => scope?.get?.();
29
+ }
30
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGpD,gEAAgE;AAChE,MAAM,CAAC,MAAM,kBAAkB,GAAG,wBAA6C,CAAA;AAK/E;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAY,EAAE,IAAoB;IACjE,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,UAAU,CAAiC,CAAA;IACpE,MAAM,KAAK,GAAG,QAAQ,EAAE,QAAQ,CAAC,kBAAkB,EAAE,cAAc,EAAE;QACnE,QAAQ,EAAE,CAAC,KAAoB,EAAE,EAAE;YACjC,kEAAkE;YAClE,gDAAgD;YAChD,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,EAAmB,CAAA;YACrD,MAAM,CAAC,MAAM,CAAC,CAAA;QAChB,CAAC;KACF,CAAC,CAAA;IACF,OAAO,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAA+B,CAAA;AAC1D,CAAC"}
package/lib/store.d.ts ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Content-addressed, project-keyed store for crushed tool-output originals.
3
+ *
4
+ * Layout: `<root>/<projectKey>/<hash16>` where hash16 = sha256(original
5
+ * utf8).slice(0,16) and projectKey = sha256(session cwd).slice(0,16) — the
6
+ * worktree path divergence from the TUI's project-root convention is accepted
7
+ * here: the store is self-consistent (writers and `context_retrieve` both key
8
+ * off the session cwd). Files are plain UTF-8 JSON envelopes
9
+ * `{v, ts, text}`; writes are atomic (temp file + rename). LRU 200 entries
10
+ * and TTL 3600s are hard-coded constants; the sweep is invoked fire-and-forget
11
+ * after a store write. Corrupt/expired entries fail closed on retrieve.
12
+ *
13
+ * @module @dsh-cc/context-crusher/store
14
+ */
15
+ import type { RetrieveError } from './types.ts';
16
+ export declare const STORE_MAX_ENTRIES = 200;
17
+ export declare const STORE_TTL_MS = 3600000;
18
+ /** sha256 hex, first 16 chars — the shared content/project key shape. */
19
+ export declare function shortHash(input: string): string;
20
+ /** Injectable clock signature (tests pass a fake; never sleep in tests). */
21
+ export type NowFn = () => number;
22
+ export type RetrieveResult = {
23
+ ok: true;
24
+ text: string;
25
+ } | {
26
+ ok: false;
27
+ error: RetrieveError;
28
+ };
29
+ /**
30
+ * The crusher store. One instance per mounted plugin; `root` is
31
+ * `dshHomePath('ccr')`. All I/O errors during sweep are swallowed
32
+ * (fire-and-forget); retrieve errors are typed, never thrown.
33
+ */
34
+ export declare class CrusherStore {
35
+ private readonly entries;
36
+ private readonly now;
37
+ readonly root: string;
38
+ constructor(root: string, now?: NowFn);
39
+ /** Persist one original atomically; returns its content hash. */
40
+ put(projectKey: string, text: string): Promise<string>;
41
+ /** Retrieve one original, failing closed on unknown/expired/corrupt. */
42
+ get(projectKey: string, hash: string): Promise<RetrieveResult>;
43
+ /**
44
+ * Fire-and-forget hygiene: drop expired files and LRU-overflow beyond
45
+ * {@link STORE_MAX_ENTRIES}. Never throws.
46
+ */
47
+ sweep(): Promise<void>;
48
+ /** Drop the LRU overflow synchronously (in-memory); files go fire-and-forget. */
49
+ private evictOverCap;
50
+ private touch;
51
+ }
52
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,eAAO,MAAM,iBAAiB,MAAM,CAAA;AACpC,eAAO,MAAM,YAAY,UAAW,CAAA;AAGpC,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,4EAA4E;AAC5E,MAAM,MAAM,KAAK,GAAG,MAAM,MAAM,CAAA;AAQhC,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAA;AAEvC;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAO;IAE3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;gBAET,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,KAAgB;IAK/C,iEAAiE;IAC3D,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkB5D,wEAAwE;IAClE,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAyBpE;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B,iFAAiF;IACjF,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,KAAK;CAId"}
package/lib/store.js ADDED
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Content-addressed, project-keyed store for crushed tool-output originals.
3
+ *
4
+ * Layout: `<root>/<projectKey>/<hash16>` where hash16 = sha256(original
5
+ * utf8).slice(0,16) and projectKey = sha256(session cwd).slice(0,16) — the
6
+ * worktree path divergence from the TUI's project-root convention is accepted
7
+ * here: the store is self-consistent (writers and `context_retrieve` both key
8
+ * off the session cwd). Files are plain UTF-8 JSON envelopes
9
+ * `{v, ts, text}`; writes are atomic (temp file + rename). LRU 200 entries
10
+ * and TTL 3600s are hard-coded constants; the sweep is invoked fire-and-forget
11
+ * after a store write. Corrupt/expired entries fail closed on retrieve.
12
+ *
13
+ * @module @dsh-cc/context-crusher/store
14
+ */
15
+ import { createHash, randomBytes } from 'node:crypto';
16
+ import { rmSync } from 'node:fs';
17
+ import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
18
+ import { join } from 'node:path';
19
+ export const STORE_MAX_ENTRIES = 200;
20
+ export const STORE_TTL_MS = 3600_000;
21
+ const HASH_RE = /^[0-9a-f]{16}$/;
22
+ /** sha256 hex, first 16 chars — the shared content/project key shape. */
23
+ export function shortHash(input) {
24
+ return createHash('sha256').update(input, 'utf8').digest('hex').slice(0, 16);
25
+ }
26
+ /**
27
+ * The crusher store. One instance per mounted plugin; `root` is
28
+ * `dshHomePath('ccr')`. All I/O errors during sweep are swallowed
29
+ * (fire-and-forget); retrieve errors are typed, never thrown.
30
+ */
31
+ export class CrusherStore {
32
+ entries = new Map(); // `${projectKey}/${hash}` → lastAccess
33
+ now;
34
+ root;
35
+ constructor(root, now = Date.now) {
36
+ this.root = root;
37
+ this.now = now;
38
+ }
39
+ /** Persist one original atomically; returns its content hash. */
40
+ async put(projectKey, text) {
41
+ const hash = shortHash(text);
42
+ const key = `${projectKey}/${hash}`;
43
+ const file = join(this.root, projectKey, hash);
44
+ const envelope = { v: 1, ts: this.now(), text };
45
+ const body = JSON.stringify(envelope);
46
+ await mkdir(join(this.root, projectKey), { recursive: true });
47
+ // Atomic write: same-directory temp file + rename, so a reader never
48
+ // observes a partial file.
49
+ const tmp = `${file}.tmp-${randomBytes(6).toString('hex')}`;
50
+ await writeFile(tmp, body, 'utf8');
51
+ await rename(tmp, file);
52
+ this.touch(key);
53
+ this.evictOverCap();
54
+ void this.sweep(); // sweep-on-write, fire-and-forget (D6)
55
+ return hash;
56
+ }
57
+ /** Retrieve one original, failing closed on unknown/expired/corrupt. */
58
+ async get(projectKey, hash) {
59
+ if (!HASH_RE.test(hash))
60
+ return { ok: false, error: 'unknown_hash' };
61
+ const key = `${projectKey}/${hash}`;
62
+ const file = join(this.root, projectKey, hash);
63
+ let body;
64
+ try {
65
+ body = await readFile(file, 'utf8');
66
+ }
67
+ catch {
68
+ return { ok: false, error: 'unknown_hash' };
69
+ }
70
+ let envelope;
71
+ try {
72
+ const parsed = JSON.parse(body);
73
+ if (typeof parsed !== 'object' || parsed === null
74
+ || parsed.v !== 1 || typeof parsed.ts !== 'number'
75
+ || typeof parsed.text !== 'string')
76
+ throw new Error('shape');
77
+ envelope = parsed;
78
+ }
79
+ catch {
80
+ return { ok: false, error: 'corrupt' };
81
+ }
82
+ if (this.now() - envelope.ts > STORE_TTL_MS)
83
+ return { ok: false, error: 'expired' };
84
+ this.touch(key);
85
+ return { ok: true, text: envelope.text };
86
+ }
87
+ /**
88
+ * Fire-and-forget hygiene: drop expired files and LRU-overflow beyond
89
+ * {@link STORE_MAX_ENTRIES}. Never throws.
90
+ */
91
+ async sweep() {
92
+ try {
93
+ const now = this.now();
94
+ for (const [key, ts] of this.entries) {
95
+ if (now - ts > STORE_TTL_MS)
96
+ this.entries.delete(key);
97
+ }
98
+ this.evictOverCap();
99
+ }
100
+ catch {
101
+ // Sweep is best-effort hygiene; a failure must never reach the listener.
102
+ }
103
+ }
104
+ /** Drop the LRU overflow synchronously (in-memory); files go fire-and-forget. */
105
+ evictOverCap() {
106
+ while (this.entries.size > STORE_MAX_ENTRIES) {
107
+ const oldest = [...this.entries.entries()].sort((a, b) => a[1] - b[1])[0];
108
+ if (oldest === undefined)
109
+ break;
110
+ const [key] = oldest;
111
+ this.entries.delete(key);
112
+ const [projectKey, hash] = key.split('/');
113
+ if (projectKey === undefined || hash === undefined)
114
+ continue;
115
+ // Synchronous single-file delete: eviction is rare and bounded, and a
116
+ // sync delete keeps the LRU/file state coherent without async bookkeeping.
117
+ try {
118
+ rmSync(join(this.root, projectKey, hash), { force: true });
119
+ }
120
+ catch {
121
+ // best-effort
122
+ }
123
+ }
124
+ }
125
+ touch(key) {
126
+ this.entries.delete(key);
127
+ this.entries.set(key, this.now());
128
+ }
129
+ }
130
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
package/lib/types.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Configuration types for the CCR tool-output crusher.
3
+ * @module @dsh-cc/context-crusher/types
4
+ */
5
+ /** Compression mode. `dry-run` measures only; `on` replaces tool results. */
6
+ export type CrusherMode = 'dry-run' | 'on';
7
+ /**
8
+ * Plugin configuration: row-level deployment defaults overlaid by the live
9
+ * `cc-context-compression` settings scope. Kebab keys match the settings keys
10
+ * exactly (the plan names them `min-bytes` / `min-savings-ratio` /
11
+ * `protected-tools`; keep those spellings).
12
+ */
13
+ export interface CrusherConfig {
14
+ /** Master flag. Defaults to `false` — the feature is opt-in. */
15
+ enabled?: boolean;
16
+ /** `'dry-run'` (default) measures and ledgers only; `'on'` replaces. */
17
+ mode?: CrusherMode;
18
+ /**
19
+ * Minimum size before a result is eligible, expressed in tokenMeter tokens
20
+ * (sizing is token-based; only the settings key keeps the legacy name).
21
+ * Defaults to `8192`.
22
+ */
23
+ 'min-bytes'?: number;
24
+ /** Minimum tokensAfter/tokensBefore saving ratio. Defaults to `0.4`. */
25
+ 'min-savings-ratio'?: number;
26
+ /**
27
+ * Protected tool names that are NEVER crushed. REPLACE semantics: an
28
+ * explicitly set list replaces the defaults entirely (not a union).
29
+ */
30
+ 'protected-tools'?: string[];
31
+ }
32
+ /** Validated, detached, deeply immutable configuration. */
33
+ export interface ResolvedConfig {
34
+ readonly enabled: boolean;
35
+ readonly mode: CrusherMode;
36
+ readonly minBytes: number;
37
+ readonly minSavingsRatio: number;
38
+ readonly protectedTools: readonly string[];
39
+ }
40
+ /** One append-only savings-ledger row (`savings.jsonl`). */
41
+ export interface LedgerRow {
42
+ readonly ts: string;
43
+ readonly sessionId: string;
44
+ readonly tool: string;
45
+ /** Router kind that fired, or the reason a gate passed through. */
46
+ readonly kind: 'search' | 'log';
47
+ readonly charsBefore: number;
48
+ readonly charsAfter: number;
49
+ readonly tokensBefore: number;
50
+ readonly tokensAfter: number;
51
+ readonly applied: boolean;
52
+ /** Present only when the original was persisted to the store. */
53
+ readonly hash?: string;
54
+ }
55
+ /** Typed retrieval failure for one store lookup. */
56
+ export type RetrieveError = 'unknown_hash' | 'expired' | 'corrupt';
57
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,6EAA6E;AAC7E,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,IAAI,CAAA;AAE1C;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wEAAwE;IACxE,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC7B;AAED,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;CAC3C;AAED,4DAA4D;AAC5D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAA;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,oDAAoD;AACpD,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,SAAS,GAAG,SAAS,CAAA"}
package/lib/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Configuration types for the CCR tool-output crusher.
3
+ * @module @dsh-cc/context-crusher/types
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@dsh-cc/context-crusher",
3
+ "description": "CCR reversible tool-output compression: compress large grep/log tool results, cache the original, retrieve it via context_retrieve",
4
+ "version": "0.6.2",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/dsh-cc/dsh-cc.git",
8
+ "directory": "packages/context/context-crusher"
9
+ },
10
+ "type": "module",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./lib/index.d.ts",
14
+ "default": "./lib/index.js"
15
+ },
16
+ "./invariant": {
17
+ "types": "./lib/invariant.d.ts",
18
+ "default": "./lib/invariant.js"
19
+ },
20
+ "./src/*": "./src/*",
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "lib"
25
+ ],
26
+ "license": "Apache-2.0",
27
+ "peerDependencies": {
28
+ "@deepseek-ai/dsh-agent": ">=0.1.2-rc.1",
29
+ "@deepseek-ai/dsh-invariants": ">=0.1.2-rc.1",
30
+ "@deepseek-ai/dsh-llm": ">=0.1.2-rc.1",
31
+ "@deepseek-ai/dsh-settings": ">=0.1.2-rc.1",
32
+ "@deepseek-ai/dsh-token-meter": ">=0.1.2-rc.1",
33
+ "@deepseek-ai/dsh-tools": ">=0.1.2-rc.1",
34
+ "@deepseek-ai/dsh-util-values": ">=0.1.2-rc.1",
35
+ "@deepseek-ai/cordis": ">=0.1.2-rc.1",
36
+ "@deepseek-ai/schemastery": "^3.18.1",
37
+ "@dsh-cc/session-cwd": "^0.6.2",
38
+ "@dsh-cc/tools": "^0.6.2"
39
+ },
40
+ "devDependencies": {
41
+ "@deepseek-ai/cordis": "link:../../../../deepseek-harness/vendor/cordis",
42
+ "@deepseek-ai/cordis-plugin-include": "link:../../../../deepseek-harness/vendor/include",
43
+ "@deepseek-ai/cordis-plugin-loader": "link:../../../../deepseek-harness/vendor/loader",
44
+ "@deepseek-ai/dsh-agent": "link:../../../../deepseek-harness/packages/core/agent",
45
+ "@deepseek-ai/dsh-invariants": "link:../../../../deepseek-harness/packages/runtime-diagnostics/invariants",
46
+ "@deepseek-ai/dsh-llm": "link:../../../../deepseek-harness/packages/llm/llm",
47
+ "@deepseek-ai/dsh-settings": "link:../../../../deepseek-harness/packages/settings/settings",
48
+ "@deepseek-ai/dsh-token-meter": "link:../../../../deepseek-harness/packages/llm/token-meter",
49
+ "@deepseek-ai/dsh-tools": "link:../../../../deepseek-harness/packages/core/tools",
50
+ "@deepseek-ai/dsh-util-values": "link:../../../../deepseek-harness/packages/util/values",
51
+ "@deepseek-ai/schemastery": "link:../../../../deepseek-harness/vendor/schemastery",
52
+ "@deepseek-ai/dsh-agent-loop": "link:../../../../deepseek-harness/packages/core/agent-loop",
53
+ "@deepseek-ai/dsh-agent-loop-testkit": "link:../../../../deepseek-harness/packages/test-support/agent-loop-testkit",
54
+ "@deepseek-ai/dsh-bash-local": "link:../../../../deepseek-harness/packages/shell/bash-local",
55
+ "@deepseek-ai/dsh-subprocess-local": "link:../../../../deepseek-harness/packages/subprocess/subprocess-local",
56
+ "@deepseek-ai/dsh-session": "link:../../../../deepseek-harness/packages/core/session",
57
+ "@deepseek-ai/dsh-session-projection": "link:../../../../deepseek-harness/packages/session/session-projection",
58
+ "vitest": "^4.1.8",
59
+ "@dsh-cc/session-cwd": "^0.6.2",
60
+ "@dsh-cc/tools": "^0.6.2",
61
+ "@dsh-cc/hooks-claude-code": "^0.6.2",
62
+ "@dsh-cc/agent-loop-mock": "^0.1.0"
63
+ },
64
+ "publishConfig": {
65
+ "access": "public"
66
+ }
67
+ }