@nxuss/lemma 1.9.1 → 1.11.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 +24 -4
- package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/cjs/cli/lemma-proxy.js +7 -2
- package/dist/cjs/cli/lemma-proxy.js.map +1 -1
- package/dist/cjs/mcp/tool-profiles.d.ts.map +1 -1
- package/dist/cjs/mcp/tool-profiles.js +43 -0
- package/dist/cjs/mcp/tool-profiles.js.map +1 -1
- package/dist/cjs/mcp/tools.d.ts.map +1 -1
- package/dist/cjs/mcp/tools.js +314 -146
- package/dist/cjs/mcp/tools.js.map +1 -1
- package/dist/cjs/subconscious/TheBrainV2.d.ts +27 -1
- package/dist/cjs/subconscious/TheBrainV2.d.ts.map +1 -1
- package/dist/cjs/subconscious/TheBrainV2.js +53 -1
- package/dist/cjs/subconscious/TheBrainV2.js.map +1 -1
- package/dist/cjs/utils/AffectedTests.d.ts +54 -0
- package/dist/cjs/utils/AffectedTests.d.ts.map +1 -0
- package/dist/cjs/utils/AffectedTests.js +195 -0
- package/dist/cjs/utils/AffectedTests.js.map +1 -0
- package/dist/cjs/utils/AstSymbolCache.d.ts +29 -0
- package/dist/cjs/utils/AstSymbolCache.d.ts.map +1 -0
- package/dist/cjs/utils/AstSymbolCache.js +66 -0
- package/dist/cjs/utils/AstSymbolCache.js.map +1 -0
- package/dist/cjs/utils/ContextSqueezer.d.ts +24 -4
- package/dist/cjs/utils/ContextSqueezer.d.ts.map +1 -1
- package/dist/cjs/utils/ContextSqueezer.js +32 -3
- package/dist/cjs/utils/ContextSqueezer.js.map +1 -1
- package/dist/cjs/utils/DepGraph.d.ts +28 -0
- package/dist/cjs/utils/DepGraph.d.ts.map +1 -0
- package/dist/cjs/utils/DepGraph.js +210 -0
- package/dist/cjs/utils/DepGraph.js.map +1 -0
- package/dist/cjs/utils/LocalPrefilter.d.ts +61 -0
- package/dist/cjs/utils/LocalPrefilter.d.ts.map +1 -0
- package/dist/cjs/utils/LocalPrefilter.js +169 -0
- package/dist/cjs/utils/LocalPrefilter.js.map +1 -0
- package/dist/cjs/utils/OutputDelta.d.ts +60 -0
- package/dist/cjs/utils/OutputDelta.d.ts.map +1 -0
- package/dist/cjs/utils/OutputDelta.js +226 -0
- package/dist/cjs/utils/OutputDelta.js.map +1 -0
- package/dist/cjs/utils/OutputLedger.d.ts +60 -0
- package/dist/cjs/utils/OutputLedger.d.ts.map +1 -0
- package/dist/cjs/utils/OutputLedger.js +134 -0
- package/dist/cjs/utils/OutputLedger.js.map +1 -0
- package/dist/cjs/utils/ReadWorkspaceCache.d.ts +8 -2
- package/dist/cjs/utils/ReadWorkspaceCache.d.ts.map +1 -1
- package/dist/cjs/utils/ReadWorkspaceCache.js +82 -4
- package/dist/cjs/utils/ReadWorkspaceCache.js.map +1 -1
- package/dist/cjs/utils/RoiLedger.d.ts +31 -0
- package/dist/cjs/utils/RoiLedger.d.ts.map +1 -0
- package/dist/cjs/utils/RoiLedger.js +113 -0
- package/dist/cjs/utils/RoiLedger.js.map +1 -0
- package/dist/cjs/utils/SharedLedgerStore.d.ts +26 -0
- package/dist/cjs/utils/SharedLedgerStore.d.ts.map +1 -0
- package/dist/cjs/utils/SharedLedgerStore.js +130 -0
- package/dist/cjs/utils/SharedLedgerStore.js.map +1 -0
- package/dist/cjs/utils/SpeculativeWarmer.d.ts +24 -0
- package/dist/cjs/utils/SpeculativeWarmer.d.ts.map +1 -0
- package/dist/cjs/utils/SpeculativeWarmer.js +73 -0
- package/dist/cjs/utils/SpeculativeWarmer.js.map +1 -0
- package/dist/cjs/utils/reportSavings.d.ts.map +1 -1
- package/dist/cjs/utils/reportSavings.js +11 -0
- package/dist/cjs/utils/reportSavings.js.map +1 -1
- package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/esm/cli/lemma-proxy.js +7 -2
- package/dist/esm/cli/lemma-proxy.js.map +1 -1
- package/dist/esm/mcp/tool-profiles.d.ts.map +1 -1
- package/dist/esm/mcp/tool-profiles.js +43 -0
- package/dist/esm/mcp/tool-profiles.js.map +1 -1
- package/dist/esm/mcp/tools.d.ts.map +1 -1
- package/dist/esm/mcp/tools.js +312 -144
- package/dist/esm/mcp/tools.js.map +1 -1
- package/dist/esm/subconscious/TheBrainV2.d.ts +27 -1
- package/dist/esm/subconscious/TheBrainV2.d.ts.map +1 -1
- package/dist/esm/subconscious/TheBrainV2.js +53 -1
- package/dist/esm/subconscious/TheBrainV2.js.map +1 -1
- package/dist/esm/utils/AffectedTests.d.ts +54 -0
- package/dist/esm/utils/AffectedTests.d.ts.map +1 -0
- package/dist/esm/utils/AffectedTests.js +186 -0
- package/dist/esm/utils/AffectedTests.js.map +1 -0
- package/dist/esm/utils/AstSymbolCache.d.ts +29 -0
- package/dist/esm/utils/AstSymbolCache.d.ts.map +1 -0
- package/dist/esm/utils/AstSymbolCache.js +58 -0
- package/dist/esm/utils/AstSymbolCache.js.map +1 -0
- package/dist/esm/utils/ContextSqueezer.d.ts +24 -4
- package/dist/esm/utils/ContextSqueezer.d.ts.map +1 -1
- package/dist/esm/utils/ContextSqueezer.js +32 -3
- package/dist/esm/utils/ContextSqueezer.js.map +1 -1
- package/dist/esm/utils/DepGraph.d.ts +28 -0
- package/dist/esm/utils/DepGraph.d.ts.map +1 -0
- package/dist/esm/utils/DepGraph.js +169 -0
- package/dist/esm/utils/DepGraph.js.map +1 -0
- package/dist/esm/utils/LocalPrefilter.d.ts +61 -0
- package/dist/esm/utils/LocalPrefilter.d.ts.map +1 -0
- package/dist/esm/utils/LocalPrefilter.js +159 -0
- package/dist/esm/utils/LocalPrefilter.js.map +1 -0
- package/dist/esm/utils/OutputDelta.d.ts +60 -0
- package/dist/esm/utils/OutputDelta.d.ts.map +1 -0
- package/dist/esm/utils/OutputDelta.js +221 -0
- package/dist/esm/utils/OutputDelta.js.map +1 -0
- package/dist/esm/utils/OutputLedger.d.ts +60 -0
- package/dist/esm/utils/OutputLedger.d.ts.map +1 -0
- package/dist/esm/utils/OutputLedger.js +125 -0
- package/dist/esm/utils/OutputLedger.js.map +1 -0
- package/dist/esm/utils/ReadWorkspaceCache.d.ts +8 -2
- package/dist/esm/utils/ReadWorkspaceCache.d.ts.map +1 -1
- package/dist/esm/utils/ReadWorkspaceCache.js +81 -4
- package/dist/esm/utils/ReadWorkspaceCache.js.map +1 -1
- package/dist/esm/utils/RoiLedger.d.ts +31 -0
- package/dist/esm/utils/RoiLedger.d.ts.map +1 -0
- package/dist/esm/utils/RoiLedger.js +104 -0
- package/dist/esm/utils/RoiLedger.js.map +1 -0
- package/dist/esm/utils/SharedLedgerStore.d.ts +26 -0
- package/dist/esm/utils/SharedLedgerStore.d.ts.map +1 -0
- package/dist/esm/utils/SharedLedgerStore.js +122 -0
- package/dist/esm/utils/SharedLedgerStore.js.map +1 -0
- package/dist/esm/utils/SpeculativeWarmer.d.ts +24 -0
- package/dist/esm/utils/SpeculativeWarmer.d.ts.map +1 -0
- package/dist/esm/utils/SpeculativeWarmer.js +66 -0
- package/dist/esm/utils/SpeculativeWarmer.js.map +1 -0
- package/dist/esm/utils/reportSavings.d.ts.map +1 -1
- package/dist/esm/utils/reportSavings.js +11 -0
- package/dist/esm/utils/reportSavings.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OutputLedger — session memory of what each tool already returned.
|
|
4
|
+
*
|
|
5
|
+
* `ReadWorkspaceCache` solves this for file reads, but reads are not where the weight is.
|
|
6
|
+
* A debug loop runs `npm test` six times and `tsc --noEmit` four; each run dumps hundreds
|
|
7
|
+
* of near-identical lines into the transcript, and every one of those copies is re-sent on
|
|
8
|
+
* every later turn. By the end of a session the same failing stack trace can account for
|
|
9
|
+
* more context than the source files being debugged.
|
|
10
|
+
*
|
|
11
|
+
* So: key on (tool, call signature), remember the exact text emitted, and on a repeat
|
|
12
|
+
* answer with `IDENTICAL` or a diff instead of the payload.
|
|
13
|
+
*
|
|
14
|
+
* The correctness rule is the same one that governs the read cache, and it is absolute:
|
|
15
|
+
* a ledger hit may only suppress text the model demonstrably already received in this
|
|
16
|
+
* session. Anything else — a cold start, an evicted entry, a caller that didn't record —
|
|
17
|
+
* resolves to sending the full output. Under-suppressing costs tokens; over-suppressing
|
|
18
|
+
* costs the user a correct answer.
|
|
19
|
+
*/
|
|
20
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.ledgerKey = ledgerKey;
|
|
25
|
+
exports.checkOutput = checkOutput;
|
|
26
|
+
exports.recordOutput = recordOutput;
|
|
27
|
+
exports.resetOutputLedger = resetOutputLedger;
|
|
28
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
29
|
+
const OutputDelta_1 = require("./OutputDelta");
|
|
30
|
+
const SharedLedgerStore_1 = require("./SharedLedgerStore");
|
|
31
|
+
const entries = new Map();
|
|
32
|
+
/** Mirrors the read cache's bound: this exists to save tokens, not to grow forever. */
|
|
33
|
+
const MAX_LEDGER_CHARS = 2000000;
|
|
34
|
+
const MAX_ENTRY_CHARS = 400000;
|
|
35
|
+
let ledgerChars = 0;
|
|
36
|
+
let callCounter = 0;
|
|
37
|
+
function sha256(text) {
|
|
38
|
+
return crypto_1.default.createHash("sha256").update(text).digest("hex");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build a stable key from the arguments that determine the output.
|
|
42
|
+
*
|
|
43
|
+
* Only pass arguments that actually change the result. Including an incidental field
|
|
44
|
+
* (a verbosity flag that doesn't alter the payload, a timestamp) makes every call a
|
|
45
|
+
* fresh key and silently disables the ledger; including too few makes two genuinely
|
|
46
|
+
* different calls collide, which would suppress output the model never saw. When unsure,
|
|
47
|
+
* include the field — a missed saving is recoverable, a wrong suppression is not.
|
|
48
|
+
*/
|
|
49
|
+
function ledgerKey(toolName, signature) {
|
|
50
|
+
const normalized = Object.keys(signature)
|
|
51
|
+
.sort()
|
|
52
|
+
.map((k) => `${k}=${JSON.stringify(signature[k]) ?? ""}`)
|
|
53
|
+
.join("&");
|
|
54
|
+
return `${toolName}::${normalized}`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check a freshly produced output against what this tool last returned for the same call.
|
|
58
|
+
*
|
|
59
|
+
* Always call `record` afterwards with the text you actually emitted, including when this
|
|
60
|
+
* returns `new` — otherwise the next repeat has no base to compare against.
|
|
61
|
+
*/
|
|
62
|
+
function checkOutput(key, text, options) {
|
|
63
|
+
let previous = entries.get(key);
|
|
64
|
+
// No in-process history for this key — check whether a sibling fork already produced
|
|
65
|
+
// this exact call in the shared conversation this process's tree inherits. Scoped and
|
|
66
|
+
// opt-in via SharedLedgerStore; a plain miss here is silent and free.
|
|
67
|
+
if (!previous) {
|
|
68
|
+
const shared = (0, SharedLedgerStore_1.getSharedEntry)(process.cwd(), "output-ledger", key);
|
|
69
|
+
if (shared) {
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(shared);
|
|
72
|
+
previous = { text: parsed.text, hash: parsed.hash, callIndex: parsed.callIndex, at: 0 };
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
previous = undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!previous)
|
|
80
|
+
return { status: "new", replacement: null, savedChars: 0 };
|
|
81
|
+
const hash = sha256(text);
|
|
82
|
+
if (hash === previous.hash) {
|
|
83
|
+
const marker = [
|
|
84
|
+
`IDENTICAL: ${options.label} produced byte-for-byte the same output as earlier this session (call #${previous.callIndex}).`,
|
|
85
|
+
`Nothing changed. Reuse that result rather than re-reading it here.`,
|
|
86
|
+
...(options.rerunHint ? [options.rerunHint] : []),
|
|
87
|
+
].join("\n");
|
|
88
|
+
return { status: "identical", replacement: marker, savedChars: Math.max(0, text.length - marker.length) };
|
|
89
|
+
}
|
|
90
|
+
const delta = (0, OutputDelta_1.shouldEmitDelta)(previous.text, text);
|
|
91
|
+
if (!delta.emit)
|
|
92
|
+
return { status: "changed", replacement: null, savedChars: 0 };
|
|
93
|
+
const marker = [
|
|
94
|
+
`CHANGED: ${options.label} — ${delta.hunkCount} hunk(s), +${delta.added}/-${delta.removed} vs. call #${previous.callIndex}.`,
|
|
95
|
+
`Everything else is unchanged from that result. Diff:`,
|
|
96
|
+
``,
|
|
97
|
+
delta.body,
|
|
98
|
+
].join("\n");
|
|
99
|
+
// A diff that ended up longer than the payload is a saving of zero, not a negative one;
|
|
100
|
+
// the caller decides what to emit, this only reports what was actually spared.
|
|
101
|
+
return { status: "changed", replacement: marker, savedChars: Math.max(0, text.length - marker.length) };
|
|
102
|
+
}
|
|
103
|
+
/** Record the text a tool emitted (the full payload, not a marker) as the base for next time. */
|
|
104
|
+
function recordOutput(key, text) {
|
|
105
|
+
const previous = entries.get(key);
|
|
106
|
+
if (previous) {
|
|
107
|
+
ledgerChars -= previous.text.length;
|
|
108
|
+
// `set` on an existing key keeps its original insertion position, so delete first to
|
|
109
|
+
// make a refreshed entry count as recently-written for eviction.
|
|
110
|
+
entries.delete(key);
|
|
111
|
+
}
|
|
112
|
+
if (text.length > MAX_ENTRY_CHARS)
|
|
113
|
+
return;
|
|
114
|
+
callCounter++;
|
|
115
|
+
const hash = sha256(text);
|
|
116
|
+
entries.set(key, { text, hash, callIndex: callCounter, at: Date.now() });
|
|
117
|
+
ledgerChars += text.length;
|
|
118
|
+
(0, SharedLedgerStore_1.setSharedEntry)(process.cwd(), "output-ledger", key, JSON.stringify({ text, hash, callIndex: callCounter }));
|
|
119
|
+
while (ledgerChars > MAX_LEDGER_CHARS) {
|
|
120
|
+
const oldest = entries.keys().next();
|
|
121
|
+
if (oldest.done)
|
|
122
|
+
break;
|
|
123
|
+
const dropped = entries.get(oldest.value);
|
|
124
|
+
entries.delete(oldest.value);
|
|
125
|
+
ledgerChars -= dropped.text.length;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** Test seam — the map is process-global and would otherwise leak between test cases. */
|
|
129
|
+
function resetOutputLedger() {
|
|
130
|
+
entries.clear();
|
|
131
|
+
ledgerChars = 0;
|
|
132
|
+
callCounter = 0;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=OutputLedger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OutputLedger.js","sourceRoot":"","sources":["../../../src/utils/OutputLedger.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,8 +15,14 @@ export interface ReadDedupCheck {
|
|
|
15
15
|
}
|
|
16
16
|
/** Check whether this exact (path, view) content was already sent this session. */
|
|
17
17
|
export declare function checkAlreadySent(resolvedPath: string, view: ReadView, rawContent: string): ReadDedupCheck;
|
|
18
|
+
/**
|
|
19
|
+
* The text last emitted for this (path, view), or null if we have none — because this is
|
|
20
|
+
* the first read, or because the entry was evicted under the size cap. A null here means
|
|
21
|
+
* "send the full file", never "assume it is unchanged".
|
|
22
|
+
*/
|
|
23
|
+
export declare function getLastEmitted(resolvedPath: string, view: ReadView): string | null;
|
|
18
24
|
/** Record that this content was just sent, so future identical reads short-circuit. */
|
|
19
|
-
export declare function recordSent(resolvedPath: string, view: ReadView, hash: string): void;
|
|
20
|
-
/** Test seam — the
|
|
25
|
+
export declare function recordSent(resolvedPath: string, view: ReadView, hash: string, emitted?: string): void;
|
|
26
|
+
/** Test seam — the maps are process-global and would otherwise leak between test cases. */
|
|
21
27
|
export declare function resetReadCache(): void;
|
|
22
28
|
//# sourceMappingURL=ReadWorkspaceCache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReadWorkspaceCache.d.ts","sourceRoot":"","sources":["../../../src/utils/ReadWorkspaceCache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ReadWorkspaceCache.d.ts","sourceRoot":"","sources":["../../../src/utils/ReadWorkspaceCache.ts"],"names":[],"mappings":"AAiCA;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAaD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAiBD,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,CAOzG;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAKlF;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAsCrG;AAED,2FAA2F;AAC3F,wBAAgB,cAAc,IAAI,IAAI,CAIrC"}
|
|
@@ -4,14 +4,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.checkAlreadySent = checkAlreadySent;
|
|
7
|
+
exports.getLastEmitted = getLastEmitted;
|
|
7
8
|
exports.recordSent = recordSent;
|
|
8
9
|
exports.resetReadCache = resetReadCache;
|
|
9
10
|
const crypto_1 = __importDefault(require("crypto"));
|
|
11
|
+
const SharedLedgerStore_1 = require("./SharedLedgerStore");
|
|
10
12
|
// Session-scoped (lives for the MCP server process lifetime, which is normally the
|
|
11
13
|
// whole conversation). Tracks the content hash we last actually sent back to the
|
|
12
14
|
// model for a given (filePath, view) pair, so a second identical read doesn't
|
|
13
15
|
// resend content the model already has in its own context.
|
|
14
16
|
const lastSent = new Map();
|
|
17
|
+
/**
|
|
18
|
+
* The exact text emitted for each key, kept so a later read of a *changed* file can be
|
|
19
|
+
* answered with a diff instead of the whole file again. Only what the model actually
|
|
20
|
+
* received is stored — diffing against anything else would describe a document it never
|
|
21
|
+
* had.
|
|
22
|
+
*/
|
|
23
|
+
const lastEmitted = new Map();
|
|
24
|
+
/**
|
|
25
|
+
* Total characters held in `lastEmitted`. This cache exists to save tokens, so it must
|
|
26
|
+
* not grow without bound in a long session; past the cap the least-recently-written
|
|
27
|
+
* entries are dropped and those reads simply fall back to sending the full file.
|
|
28
|
+
*/
|
|
29
|
+
const MAX_EMITTED_CHARS = 2000000;
|
|
30
|
+
/** A single entry larger than this is never stored — one huge file would evict everything else. */
|
|
31
|
+
const MAX_ENTRY_CHARS = 400000;
|
|
32
|
+
let emittedChars = 0;
|
|
15
33
|
function sha256(content) {
|
|
16
34
|
return crypto_1.default.createHash("sha256").update(content).digest("hex");
|
|
17
35
|
}
|
|
@@ -25,19 +43,79 @@ function cacheKey(resolvedPath, view) {
|
|
|
25
43
|
];
|
|
26
44
|
return parts.join("::");
|
|
27
45
|
}
|
|
46
|
+
function getSharedRead(resolvedPath, view) {
|
|
47
|
+
const raw = (0, SharedLedgerStore_1.getSharedEntry)(process.cwd(), "read-cache", cacheKey(resolvedPath, view));
|
|
48
|
+
if (!raw)
|
|
49
|
+
return null;
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(raw);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
28
57
|
/** Check whether this exact (path, view) content was already sent this session. */
|
|
29
58
|
function checkAlreadySent(resolvedPath, view, rawContent) {
|
|
30
59
|
const hash = sha256(rawContent);
|
|
31
60
|
const key = cacheKey(resolvedPath, view);
|
|
32
|
-
|
|
61
|
+
// In-process history wins; a sibling fork's shared entry only counts when this process
|
|
62
|
+
// has never seen the key itself — same "opt-in, scoped, fail-open" contract as OutputLedger.
|
|
63
|
+
const unchanged = lastSent.has(key) ? lastSent.get(key) === hash : getSharedRead(resolvedPath, view)?.hash === hash;
|
|
33
64
|
return { unchanged, hash };
|
|
34
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* The text last emitted for this (path, view), or null if we have none — because this is
|
|
68
|
+
* the first read, or because the entry was evicted under the size cap. A null here means
|
|
69
|
+
* "send the full file", never "assume it is unchanged".
|
|
70
|
+
*/
|
|
71
|
+
function getLastEmitted(resolvedPath, view) {
|
|
72
|
+
const key = cacheKey(resolvedPath, view);
|
|
73
|
+
const local = lastEmitted.get(key);
|
|
74
|
+
if (local !== undefined)
|
|
75
|
+
return local;
|
|
76
|
+
return getSharedRead(resolvedPath, view)?.emitted ?? null;
|
|
77
|
+
}
|
|
35
78
|
/** Record that this content was just sent, so future identical reads short-circuit. */
|
|
36
|
-
function recordSent(resolvedPath, view, hash) {
|
|
37
|
-
|
|
79
|
+
function recordSent(resolvedPath, view, hash, emitted) {
|
|
80
|
+
const key = cacheKey(resolvedPath, view);
|
|
81
|
+
lastSent.set(key, hash);
|
|
82
|
+
// Mirrors the local size cap: an oversized entry is skipped locally too, so keeping it
|
|
83
|
+
// out of the shared file avoids bloating something every sibling process reads on every hit.
|
|
84
|
+
if (emitted === undefined || emitted.length <= MAX_ENTRY_CHARS) {
|
|
85
|
+
(0, SharedLedgerStore_1.setSharedEntry)(process.cwd(), "read-cache", key, JSON.stringify({ hash, emitted }));
|
|
86
|
+
}
|
|
87
|
+
if (emitted === undefined)
|
|
88
|
+
return;
|
|
89
|
+
const previous = lastEmitted.get(key);
|
|
90
|
+
if (previous !== undefined) {
|
|
91
|
+
emittedChars -= previous.length;
|
|
92
|
+
// Map keeps insertion order, and `set` on an existing key does not move it. Deleting
|
|
93
|
+
// first is what makes a refreshed entry count as recently-written for eviction.
|
|
94
|
+
lastEmitted.delete(key);
|
|
95
|
+
}
|
|
96
|
+
if (emitted.length > MAX_ENTRY_CHARS) {
|
|
97
|
+
// Too big to keep. Drop any stale copy rather than leaving a diff base that no longer
|
|
98
|
+
// matches what the model holds.
|
|
99
|
+
lastEmitted.delete(key);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
lastEmitted.set(key, emitted);
|
|
103
|
+
emittedChars += emitted.length;
|
|
104
|
+
// Map iteration is insertion-ordered, and re-inserting on every write above moves a
|
|
105
|
+
// refreshed key to the back — so evicting from the front drops the least-recently-written.
|
|
106
|
+
while (emittedChars > MAX_EMITTED_CHARS) {
|
|
107
|
+
const oldest = lastEmitted.keys().next();
|
|
108
|
+
if (oldest.done)
|
|
109
|
+
break;
|
|
110
|
+
const dropped = lastEmitted.get(oldest.value);
|
|
111
|
+
lastEmitted.delete(oldest.value);
|
|
112
|
+
emittedChars -= dropped.length;
|
|
113
|
+
}
|
|
38
114
|
}
|
|
39
|
-
/** Test seam — the
|
|
115
|
+
/** Test seam — the maps are process-global and would otherwise leak between test cases. */
|
|
40
116
|
function resetReadCache() {
|
|
41
117
|
lastSent.clear();
|
|
118
|
+
lastEmitted.clear();
|
|
119
|
+
emittedChars = 0;
|
|
42
120
|
}
|
|
43
121
|
//# sourceMappingURL=ReadWorkspaceCache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReadWorkspaceCache.js","sourceRoot":"","sources":["../../../src/utils/ReadWorkspaceCache.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"ReadWorkspaceCache.js","sourceRoot":"","sources":["../../../src/utils/ReadWorkspaceCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RoiLedger — per-(repo, tool) net token history, persisted across sessions.
|
|
3
|
+
*
|
|
4
|
+
* `SavingsLedger` already tracks saved/spent totals, but only by *source* (cache,
|
|
5
|
+
* contextSqueeze, ...) and only in `~/.lemma-cache/ledger.json` — one file, no notion of
|
|
6
|
+
* which repo or which specific tool earned its keep. That is not enough to answer "does
|
|
7
|
+
* `affected_tests` pay for its schema in THIS repo", which is what `tool-profiles.ts` needs
|
|
8
|
+
* to auto-tune the advertised surface instead of relying purely on the hand-curated
|
|
9
|
+
* `CORE_TOOL_NAMES` list.
|
|
10
|
+
*
|
|
11
|
+
* Scope: one small JSON file per repo, keyed by a hash of the repo path, holding cumulative
|
|
12
|
+
* (calls, tokensSaved, tokensSpent) per tool name. Fail-open throughout — a lookup or write
|
|
13
|
+
* that errors must never break the caller; it just means no history this time.
|
|
14
|
+
*
|
|
15
|
+
* Hard rule inherited from `tool-profiles.ts`: the tool surface resolves once at startup and
|
|
16
|
+
* never mutates mid-session. This ledger is written continuously, but only ever *read* by
|
|
17
|
+
* `resolveToolSurface` once, at process start — so a promotion always reflects PRIOR
|
|
18
|
+
* sessions, never the one currently running.
|
|
19
|
+
*/
|
|
20
|
+
export interface ToolRoiStats {
|
|
21
|
+
calls: number;
|
|
22
|
+
tokensSaved: number;
|
|
23
|
+
tokensSpent: number;
|
|
24
|
+
}
|
|
25
|
+
export declare function recordRoiSaved(repoRoot: string, tool: string, tokensSaved: number): void;
|
|
26
|
+
export declare function recordRoiSpent(repoRoot: string, tool: string, tokensSpent: number): void;
|
|
27
|
+
/** All per-tool history for this repo, or an empty map if none exists yet or the read fails. */
|
|
28
|
+
export declare function getRoiStats(repoRoot: string): Record<string, ToolRoiStats>;
|
|
29
|
+
/** Test seam only — production code should never need to wipe another repo's history. */
|
|
30
|
+
export declare function resetRoiLedger(repoRoot: string): void;
|
|
31
|
+
//# sourceMappingURL=RoiLedger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RoiLedger.d.ts","sourceRoot":"","sources":["../../../src/utils/RoiLedger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AA8DD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAExF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAExF;AAED,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAM1E;AAED,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMrD"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RoiLedger — per-(repo, tool) net token history, persisted across sessions.
|
|
4
|
+
*
|
|
5
|
+
* `SavingsLedger` already tracks saved/spent totals, but only by *source* (cache,
|
|
6
|
+
* contextSqueeze, ...) and only in `~/.lemma-cache/ledger.json` — one file, no notion of
|
|
7
|
+
* which repo or which specific tool earned its keep. That is not enough to answer "does
|
|
8
|
+
* `affected_tests` pay for its schema in THIS repo", which is what `tool-profiles.ts` needs
|
|
9
|
+
* to auto-tune the advertised surface instead of relying purely on the hand-curated
|
|
10
|
+
* `CORE_TOOL_NAMES` list.
|
|
11
|
+
*
|
|
12
|
+
* Scope: one small JSON file per repo, keyed by a hash of the repo path, holding cumulative
|
|
13
|
+
* (calls, tokensSaved, tokensSpent) per tool name. Fail-open throughout — a lookup or write
|
|
14
|
+
* that errors must never break the caller; it just means no history this time.
|
|
15
|
+
*
|
|
16
|
+
* Hard rule inherited from `tool-profiles.ts`: the tool surface resolves once at startup and
|
|
17
|
+
* never mutates mid-session. This ledger is written continuously, but only ever *read* by
|
|
18
|
+
* `resolveToolSurface` once, at process start — so a promotion always reflects PRIOR
|
|
19
|
+
* sessions, never the one currently running.
|
|
20
|
+
*/
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.recordRoiSaved = recordRoiSaved;
|
|
26
|
+
exports.recordRoiSpent = recordRoiSpent;
|
|
27
|
+
exports.getRoiStats = getRoiStats;
|
|
28
|
+
exports.resetRoiLedger = resetRoiLedger;
|
|
29
|
+
const fs_1 = __importDefault(require("fs"));
|
|
30
|
+
const path_1 = __importDefault(require("path"));
|
|
31
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
32
|
+
const HOME = process.env.HOME || process.env.USERPROFILE || "~";
|
|
33
|
+
const ROI_DIR = path_1.default.join(HOME, ".lemma-cache", "roi");
|
|
34
|
+
function repoHash(repoRoot) {
|
|
35
|
+
return crypto_1.default.createHash("sha256").update(path_1.default.resolve(repoRoot)).digest("hex").slice(0, 16);
|
|
36
|
+
}
|
|
37
|
+
function roiFilePath(repoRoot) {
|
|
38
|
+
return path_1.default.join(ROI_DIR, `${repoHash(repoRoot)}.json`);
|
|
39
|
+
}
|
|
40
|
+
function emptyStats() {
|
|
41
|
+
return { calls: 0, tokensSaved: 0, tokensSpent: 0 };
|
|
42
|
+
}
|
|
43
|
+
function load(repoRoot) {
|
|
44
|
+
try {
|
|
45
|
+
const p = roiFilePath(repoRoot);
|
|
46
|
+
if (fs_1.default.existsSync(p)) {
|
|
47
|
+
const parsed = JSON.parse(fs_1.default.readFileSync(p, "utf8"));
|
|
48
|
+
if (parsed && typeof parsed === "object" && parsed.tools)
|
|
49
|
+
return parsed;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Corrupt or unreadable file must never break the caller — start clean for this write.
|
|
54
|
+
}
|
|
55
|
+
return { tools: {}, updatedAt: Date.now() };
|
|
56
|
+
}
|
|
57
|
+
function save(repoRoot, data) {
|
|
58
|
+
try {
|
|
59
|
+
if (!fs_1.default.existsSync(ROI_DIR))
|
|
60
|
+
fs_1.default.mkdirSync(ROI_DIR, { recursive: true });
|
|
61
|
+
data.updatedAt = Date.now();
|
|
62
|
+
fs_1.default.writeFileSync(roiFilePath(repoRoot), JSON.stringify(data, null, 2), "utf8");
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Non-critical: losing one write just means the next read sees slightly stale history.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function bump(repoRoot, tool, field, amount) {
|
|
69
|
+
if (!tool || !(amount > 0))
|
|
70
|
+
return;
|
|
71
|
+
try {
|
|
72
|
+
const data = load(repoRoot);
|
|
73
|
+
const stats = data.tools[tool] || emptyStats();
|
|
74
|
+
if (field === "tokensSaved")
|
|
75
|
+
stats.tokensSaved += amount;
|
|
76
|
+
else
|
|
77
|
+
stats.tokensSpent += amount;
|
|
78
|
+
// `calls` counts either kind of event once — it is "how many times this tool did
|
|
79
|
+
// something billable", not a sum of both fields.
|
|
80
|
+
stats.calls += 1;
|
|
81
|
+
data.tools[tool] = stats;
|
|
82
|
+
save(repoRoot, data);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Fail-open: ROI bookkeeping must never be why a tool call fails.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function recordRoiSaved(repoRoot, tool, tokensSaved) {
|
|
89
|
+
bump(repoRoot, tool, "tokensSaved", tokensSaved);
|
|
90
|
+
}
|
|
91
|
+
function recordRoiSpent(repoRoot, tool, tokensSpent) {
|
|
92
|
+
bump(repoRoot, tool, "tokensSpent", tokensSpent);
|
|
93
|
+
}
|
|
94
|
+
/** All per-tool history for this repo, or an empty map if none exists yet or the read fails. */
|
|
95
|
+
function getRoiStats(repoRoot) {
|
|
96
|
+
try {
|
|
97
|
+
return load(repoRoot).tools;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Test seam only — production code should never need to wipe another repo's history. */
|
|
104
|
+
function resetRoiLedger(repoRoot) {
|
|
105
|
+
try {
|
|
106
|
+
if (fs_1.default.existsSync(roiFilePath(repoRoot)))
|
|
107
|
+
fs_1.default.unlinkSync(roiFilePath(repoRoot));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// ignore
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=RoiLedger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RoiLedger.js","sourceRoot":"","sources":["../../../src/utils/RoiLedger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SharedLedgerStore — file-backed cross-process backing for `OutputLedger` and
|
|
3
|
+
* `ReadWorkspaceCache`, so subagents that genuinely share a conversation (a `fork`, which
|
|
4
|
+
* inherits the parent's full context) don't independently re-read the same files or re-run
|
|
5
|
+
* the same commands the parent already put in that shared context.
|
|
6
|
+
*
|
|
7
|
+
* OFF BY DEFAULT — opt in with `LEMMA_SHARED_LEDGER=1` (or `mcp.sharedLedger: true` in
|
|
8
|
+
* lemma.config.json) AND a `LEMMA_AGENT_TREE_ID` env var identifying the fork tree. Both are
|
|
9
|
+
* required on purpose:
|
|
10
|
+
*
|
|
11
|
+
* - Without a tree id, every process on the machine would share one ledger, and a plain
|
|
12
|
+
* fresh subagent (no inherited context) would get told a file is "UNCHANGED" or a command
|
|
13
|
+
* "IDENTICAL" for content it never actually received — suppressing content the model
|
|
14
|
+
* never saw, which is the one thing these caches must never do.
|
|
15
|
+
* - The tree id scopes sharing to processes an orchestrator has explicitly declared share a
|
|
16
|
+
* context (i.e. `fork` subagents of the same parent), which is the only case where "another
|
|
17
|
+
* process already has this" is actually true for THIS process too.
|
|
18
|
+
*
|
|
19
|
+
* TTL is short (30 min): a fork tree is a single task, not a persistent cache.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isSharedLedgerEnabled(cwd?: string): boolean;
|
|
22
|
+
/** Get a shared value for `key` (namespaced), or null if disabled, missing, or expired. */
|
|
23
|
+
export declare function getSharedEntry(cwd: string, namespace: "output-ledger" | "read-cache", key: string): string | null;
|
|
24
|
+
/** Record a value under `key` (namespaced) for other trees-scoped processes to read. */
|
|
25
|
+
export declare function setSharedEntry(cwd: string, namespace: "output-ledger" | "read-cache", key: string, value: string): void;
|
|
26
|
+
//# sourceMappingURL=SharedLedgerStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SharedLedgerStore.d.ts","sourceRoot":"","sources":["../../../src/utils/SharedLedgerStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AASH,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAa1E;AAgDD,2FAA2F;AAC3F,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,eAAe,GAAG,YAAY,EACzC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,IAAI,CAWf;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,eAAe,GAAG,YAAY,EACzC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,IAAI,CAcN"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SharedLedgerStore — file-backed cross-process backing for `OutputLedger` and
|
|
4
|
+
* `ReadWorkspaceCache`, so subagents that genuinely share a conversation (a `fork`, which
|
|
5
|
+
* inherits the parent's full context) don't independently re-read the same files or re-run
|
|
6
|
+
* the same commands the parent already put in that shared context.
|
|
7
|
+
*
|
|
8
|
+
* OFF BY DEFAULT — opt in with `LEMMA_SHARED_LEDGER=1` (or `mcp.sharedLedger: true` in
|
|
9
|
+
* lemma.config.json) AND a `LEMMA_AGENT_TREE_ID` env var identifying the fork tree. Both are
|
|
10
|
+
* required on purpose:
|
|
11
|
+
*
|
|
12
|
+
* - Without a tree id, every process on the machine would share one ledger, and a plain
|
|
13
|
+
* fresh subagent (no inherited context) would get told a file is "UNCHANGED" or a command
|
|
14
|
+
* "IDENTICAL" for content it never actually received — suppressing content the model
|
|
15
|
+
* never saw, which is the one thing these caches must never do.
|
|
16
|
+
* - The tree id scopes sharing to processes an orchestrator has explicitly declared share a
|
|
17
|
+
* context (i.e. `fork` subagents of the same parent), which is the only case where "another
|
|
18
|
+
* process already has this" is actually true for THIS process too.
|
|
19
|
+
*
|
|
20
|
+
* TTL is short (30 min): a fork tree is a single task, not a persistent cache.
|
|
21
|
+
*/
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.isSharedLedgerEnabled = isSharedLedgerEnabled;
|
|
27
|
+
exports.getSharedEntry = getSharedEntry;
|
|
28
|
+
exports.setSharedEntry = setSharedEntry;
|
|
29
|
+
const fs_1 = __importDefault(require("fs"));
|
|
30
|
+
const path_1 = __importDefault(require("path"));
|
|
31
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
32
|
+
const HOME = process.env.HOME || process.env.USERPROFILE || "~";
|
|
33
|
+
const TTL_MS = 30 * 60 * 1000;
|
|
34
|
+
function isSharedLedgerEnabled(cwd = process.cwd()) {
|
|
35
|
+
if (!process.env.LEMMA_AGENT_TREE_ID)
|
|
36
|
+
return false;
|
|
37
|
+
if (process.env.LEMMA_SHARED_LEDGER === "1")
|
|
38
|
+
return true;
|
|
39
|
+
try {
|
|
40
|
+
const configPath = path_1.default.join(cwd, "lemma.config.json");
|
|
41
|
+
if (fs_1.default.existsSync(configPath)) {
|
|
42
|
+
const config = JSON.parse(fs_1.default.readFileSync(configPath, "utf8"));
|
|
43
|
+
return config?.mcp?.sharedLedger === true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Config errors must never silently turn ON cross-process sharing.
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
function workspaceHash(cwd) {
|
|
52
|
+
return crypto_1.default.createHash("sha256").update(path_1.default.resolve(cwd)).digest("hex").slice(0, 16);
|
|
53
|
+
}
|
|
54
|
+
function storeDir(cwd) {
|
|
55
|
+
const treeId = process.env.LEMMA_AGENT_TREE_ID;
|
|
56
|
+
if (!treeId)
|
|
57
|
+
return null;
|
|
58
|
+
// Sanitize: the tree id lands in a path segment.
|
|
59
|
+
const safeTreeId = treeId.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 64) || "default";
|
|
60
|
+
return path_1.default.join(HOME, ".lemma-cache", "session", workspaceHash(cwd), safeTreeId);
|
|
61
|
+
}
|
|
62
|
+
function storeFile(cwd, namespace) {
|
|
63
|
+
const dir = storeDir(cwd);
|
|
64
|
+
if (!dir)
|
|
65
|
+
return null;
|
|
66
|
+
return path_1.default.join(dir, `${namespace}.json`);
|
|
67
|
+
}
|
|
68
|
+
function loadStore(cwd, namespace) {
|
|
69
|
+
try {
|
|
70
|
+
const file = storeFile(cwd, namespace);
|
|
71
|
+
if (!file || !fs_1.default.existsSync(file))
|
|
72
|
+
return {};
|
|
73
|
+
const parsed = JSON.parse(fs_1.default.readFileSync(file, "utf8"));
|
|
74
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function saveStore(cwd, namespace, data) {
|
|
81
|
+
try {
|
|
82
|
+
const dir = storeDir(cwd);
|
|
83
|
+
const file = storeFile(cwd, namespace);
|
|
84
|
+
if (!dir || !file)
|
|
85
|
+
return;
|
|
86
|
+
if (!fs_1.default.existsSync(dir))
|
|
87
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
88
|
+
fs_1.default.writeFileSync(file, JSON.stringify(data), "utf8");
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Fail-open: the caller falls back to in-process-only behavior.
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Get a shared value for `key` (namespaced), or null if disabled, missing, or expired. */
|
|
95
|
+
function getSharedEntry(cwd, namespace, key) {
|
|
96
|
+
if (!isSharedLedgerEnabled(cwd))
|
|
97
|
+
return null;
|
|
98
|
+
try {
|
|
99
|
+
const data = loadStore(cwd, namespace);
|
|
100
|
+
const entry = data[key];
|
|
101
|
+
if (!entry)
|
|
102
|
+
return null;
|
|
103
|
+
if (Date.now() - entry.at > TTL_MS)
|
|
104
|
+
return null;
|
|
105
|
+
return entry.value;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Record a value under `key` (namespaced) for other trees-scoped processes to read. */
|
|
112
|
+
function setSharedEntry(cwd, namespace, key, value) {
|
|
113
|
+
if (!isSharedLedgerEnabled(cwd))
|
|
114
|
+
return;
|
|
115
|
+
try {
|
|
116
|
+
const data = loadStore(cwd, namespace);
|
|
117
|
+
data[key] = { value, at: Date.now() };
|
|
118
|
+
// Bound the file: drop expired entries whenever we touch it, rather than growing forever.
|
|
119
|
+
const now = Date.now();
|
|
120
|
+
for (const k of Object.keys(data)) {
|
|
121
|
+
if (now - data[k].at > TTL_MS)
|
|
122
|
+
delete data[k];
|
|
123
|
+
}
|
|
124
|
+
saveStore(cwd, namespace, data);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Fail-open.
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=SharedLedgerStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SharedLedgerStore.js","sourceRoot":"","sources":["../../../src/utils/SharedLedgerStore.ts"],"names":[],"mappings":"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"}
|