@nxuss/lemma 1.10.0 → 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.
Files changed (77) hide show
  1. package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
  2. package/dist/cjs/cli/lemma-proxy.js +7 -2
  3. package/dist/cjs/cli/lemma-proxy.js.map +1 -1
  4. package/dist/cjs/mcp/tool-profiles.d.ts.map +1 -1
  5. package/dist/cjs/mcp/tool-profiles.js +40 -0
  6. package/dist/cjs/mcp/tool-profiles.js.map +1 -1
  7. package/dist/cjs/mcp/tools.d.ts.map +1 -1
  8. package/dist/cjs/mcp/tools.js +24 -8
  9. package/dist/cjs/mcp/tools.js.map +1 -1
  10. package/dist/cjs/utils/AstSymbolCache.d.ts +29 -0
  11. package/dist/cjs/utils/AstSymbolCache.d.ts.map +1 -0
  12. package/dist/cjs/utils/AstSymbolCache.js +66 -0
  13. package/dist/cjs/utils/AstSymbolCache.js.map +1 -0
  14. package/dist/cjs/utils/ContextSqueezer.d.ts +24 -4
  15. package/dist/cjs/utils/ContextSqueezer.d.ts.map +1 -1
  16. package/dist/cjs/utils/ContextSqueezer.js +32 -3
  17. package/dist/cjs/utils/ContextSqueezer.js.map +1 -1
  18. package/dist/cjs/utils/OutputLedger.d.ts.map +1 -1
  19. package/dist/cjs/utils/OutputLedger.js +20 -2
  20. package/dist/cjs/utils/OutputLedger.js.map +1 -1
  21. package/dist/cjs/utils/ReadWorkspaceCache.d.ts.map +1 -1
  22. package/dist/cjs/utils/ReadWorkspaceCache.js +25 -2
  23. package/dist/cjs/utils/ReadWorkspaceCache.js.map +1 -1
  24. package/dist/cjs/utils/RoiLedger.d.ts +31 -0
  25. package/dist/cjs/utils/RoiLedger.d.ts.map +1 -0
  26. package/dist/cjs/utils/RoiLedger.js +113 -0
  27. package/dist/cjs/utils/RoiLedger.js.map +1 -0
  28. package/dist/cjs/utils/SharedLedgerStore.d.ts +26 -0
  29. package/dist/cjs/utils/SharedLedgerStore.d.ts.map +1 -0
  30. package/dist/cjs/utils/SharedLedgerStore.js +130 -0
  31. package/dist/cjs/utils/SharedLedgerStore.js.map +1 -0
  32. package/dist/cjs/utils/SpeculativeWarmer.d.ts +24 -0
  33. package/dist/cjs/utils/SpeculativeWarmer.d.ts.map +1 -0
  34. package/dist/cjs/utils/SpeculativeWarmer.js +73 -0
  35. package/dist/cjs/utils/SpeculativeWarmer.js.map +1 -0
  36. package/dist/cjs/utils/reportSavings.d.ts.map +1 -1
  37. package/dist/cjs/utils/reportSavings.js +11 -0
  38. package/dist/cjs/utils/reportSavings.js.map +1 -1
  39. package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
  40. package/dist/esm/cli/lemma-proxy.js +7 -2
  41. package/dist/esm/cli/lemma-proxy.js.map +1 -1
  42. package/dist/esm/mcp/tool-profiles.d.ts.map +1 -1
  43. package/dist/esm/mcp/tool-profiles.js +40 -0
  44. package/dist/esm/mcp/tool-profiles.js.map +1 -1
  45. package/dist/esm/mcp/tools.d.ts.map +1 -1
  46. package/dist/esm/mcp/tools.js +24 -8
  47. package/dist/esm/mcp/tools.js.map +1 -1
  48. package/dist/esm/utils/AstSymbolCache.d.ts +29 -0
  49. package/dist/esm/utils/AstSymbolCache.d.ts.map +1 -0
  50. package/dist/esm/utils/AstSymbolCache.js +58 -0
  51. package/dist/esm/utils/AstSymbolCache.js.map +1 -0
  52. package/dist/esm/utils/ContextSqueezer.d.ts +24 -4
  53. package/dist/esm/utils/ContextSqueezer.d.ts.map +1 -1
  54. package/dist/esm/utils/ContextSqueezer.js +32 -3
  55. package/dist/esm/utils/ContextSqueezer.js.map +1 -1
  56. package/dist/esm/utils/OutputLedger.d.ts.map +1 -1
  57. package/dist/esm/utils/OutputLedger.js +20 -2
  58. package/dist/esm/utils/OutputLedger.js.map +1 -1
  59. package/dist/esm/utils/ReadWorkspaceCache.d.ts.map +1 -1
  60. package/dist/esm/utils/ReadWorkspaceCache.js +25 -2
  61. package/dist/esm/utils/ReadWorkspaceCache.js.map +1 -1
  62. package/dist/esm/utils/RoiLedger.d.ts +31 -0
  63. package/dist/esm/utils/RoiLedger.d.ts.map +1 -0
  64. package/dist/esm/utils/RoiLedger.js +104 -0
  65. package/dist/esm/utils/RoiLedger.js.map +1 -0
  66. package/dist/esm/utils/SharedLedgerStore.d.ts +26 -0
  67. package/dist/esm/utils/SharedLedgerStore.d.ts.map +1 -0
  68. package/dist/esm/utils/SharedLedgerStore.js +122 -0
  69. package/dist/esm/utils/SharedLedgerStore.js.map +1 -0
  70. package/dist/esm/utils/SpeculativeWarmer.d.ts +24 -0
  71. package/dist/esm/utils/SpeculativeWarmer.d.ts.map +1 -0
  72. package/dist/esm/utils/SpeculativeWarmer.js +66 -0
  73. package/dist/esm/utils/SpeculativeWarmer.js.map +1 -0
  74. package/dist/esm/utils/reportSavings.d.ts.map +1 -1
  75. package/dist/esm/utils/reportSavings.js +11 -0
  76. package/dist/esm/utils/reportSavings.js.map +1 -1
  77. package/package.json +1 -1
@@ -0,0 +1,122 @@
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
+ import fs from "fs";
22
+ import path from "path";
23
+ import crypto from "crypto";
24
+ const HOME = process.env.HOME || process.env.USERPROFILE || "~";
25
+ const TTL_MS = 30 * 60 * 1000;
26
+ export function isSharedLedgerEnabled(cwd = process.cwd()) {
27
+ if (!process.env.LEMMA_AGENT_TREE_ID)
28
+ return false;
29
+ if (process.env.LEMMA_SHARED_LEDGER === "1")
30
+ return true;
31
+ try {
32
+ const configPath = path.join(cwd, "lemma.config.json");
33
+ if (fs.existsSync(configPath)) {
34
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
35
+ return config?.mcp?.sharedLedger === true;
36
+ }
37
+ }
38
+ catch {
39
+ // Config errors must never silently turn ON cross-process sharing.
40
+ }
41
+ return false;
42
+ }
43
+ function workspaceHash(cwd) {
44
+ return crypto.createHash("sha256").update(path.resolve(cwd)).digest("hex").slice(0, 16);
45
+ }
46
+ function storeDir(cwd) {
47
+ const treeId = process.env.LEMMA_AGENT_TREE_ID;
48
+ if (!treeId)
49
+ return null;
50
+ // Sanitize: the tree id lands in a path segment.
51
+ const safeTreeId = treeId.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 64) || "default";
52
+ return path.join(HOME, ".lemma-cache", "session", workspaceHash(cwd), safeTreeId);
53
+ }
54
+ function storeFile(cwd, namespace) {
55
+ const dir = storeDir(cwd);
56
+ if (!dir)
57
+ return null;
58
+ return path.join(dir, `${namespace}.json`);
59
+ }
60
+ function loadStore(cwd, namespace) {
61
+ try {
62
+ const file = storeFile(cwd, namespace);
63
+ if (!file || !fs.existsSync(file))
64
+ return {};
65
+ const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
66
+ return parsed && typeof parsed === "object" ? parsed : {};
67
+ }
68
+ catch {
69
+ return {};
70
+ }
71
+ }
72
+ function saveStore(cwd, namespace, data) {
73
+ try {
74
+ const dir = storeDir(cwd);
75
+ const file = storeFile(cwd, namespace);
76
+ if (!dir || !file)
77
+ return;
78
+ if (!fs.existsSync(dir))
79
+ fs.mkdirSync(dir, { recursive: true });
80
+ fs.writeFileSync(file, JSON.stringify(data), "utf8");
81
+ }
82
+ catch {
83
+ // Fail-open: the caller falls back to in-process-only behavior.
84
+ }
85
+ }
86
+ /** Get a shared value for `key` (namespaced), or null if disabled, missing, or expired. */
87
+ export function getSharedEntry(cwd, namespace, key) {
88
+ if (!isSharedLedgerEnabled(cwd))
89
+ return null;
90
+ try {
91
+ const data = loadStore(cwd, namespace);
92
+ const entry = data[key];
93
+ if (!entry)
94
+ return null;
95
+ if (Date.now() - entry.at > TTL_MS)
96
+ return null;
97
+ return entry.value;
98
+ }
99
+ catch {
100
+ return null;
101
+ }
102
+ }
103
+ /** Record a value under `key` (namespaced) for other trees-scoped processes to read. */
104
+ export function setSharedEntry(cwd, namespace, key, value) {
105
+ if (!isSharedLedgerEnabled(cwd))
106
+ return;
107
+ try {
108
+ const data = loadStore(cwd, namespace);
109
+ data[key] = { value, at: Date.now() };
110
+ // Bound the file: drop expired entries whenever we touch it, rather than growing forever.
111
+ const now = Date.now();
112
+ for (const k of Object.keys(data)) {
113
+ if (now - data[k].at > TTL_MS)
114
+ delete data[k];
115
+ }
116
+ saveStore(cwd, namespace, data);
117
+ }
118
+ catch {
119
+ // Fail-open.
120
+ }
121
+ }
122
+ //# 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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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * SpeculativeWarmer — precomputes local caches for a file's import neighbors while the
3
+ * model is off reasoning about what it just read, so a follow-up `get_ast_hologram` or
4
+ * `depgraph`/`affected_tests` call on a neighbor is a cache hit instead of a cold parse.
5
+ *
6
+ * HARD RULE: this warms local caches only (`DepGraph`'s graph cache, `AstSymbolCache`) and
7
+ * must never touch anything that reaches the model's context. It runs after the response for
8
+ * the triggering tool call has already been returned, off the event loop's current tick, and
9
+ * every failure is swallowed — a warming run that throws or runs slow must never be visible
10
+ * to whoever is waiting on the actual tool call.
11
+ */
12
+ import { AstSymbol } from "./AstSymbolCache.js";
13
+ /**
14
+ * `extractSymbolsWithTsCompiler` lives in `mcp/tools.ts`, which already imports this module —
15
+ * importing it back here would be circular. The dispatcher registers it once at startup instead.
16
+ */
17
+ export declare function registerSymbolExtractor(extractor: (absPath: string, relPath: string, parseErrors?: string[]) => AstSymbol[]): void;
18
+ /**
19
+ * Fire-and-forget: warm the import-neighborhood of `filePath` after a real read. Safe to call
20
+ * unconditionally — it is a no-op (not just cheap, actually skipped) whenever the extractor
21
+ * hasn't been registered yet, since that means startup isn't finished.
22
+ */
23
+ export declare function warmNeighbors(workspaceRoot: string, filePath: string): void;
24
+ //# sourceMappingURL=SpeculativeWarmer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpeculativeWarmer.d.ts","sourceRoot":"","sources":["../../../src/utils/SpeculativeWarmer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAsC,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAQjF;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,GACnF,IAAI,CAEN;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CA4B3E"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * SpeculativeWarmer — precomputes local caches for a file's import neighbors while the
3
+ * model is off reasoning about what it just read, so a follow-up `get_ast_hologram` or
4
+ * `depgraph`/`affected_tests` call on a neighbor is a cache hit instead of a cold parse.
5
+ *
6
+ * HARD RULE: this warms local caches only (`DepGraph`'s graph cache, `AstSymbolCache`) and
7
+ * must never touch anything that reaches the model's context. It runs after the response for
8
+ * the triggering tool call has already been returned, off the event loop's current tick, and
9
+ * every failure is swallowed — a warming run that throws or runs slow must never be visible
10
+ * to whoever is waiting on the actual tool call.
11
+ */
12
+ import fs from "fs";
13
+ import path from "path";
14
+ import { buildDepGraphMap, bfsDepGraph } from "./DepGraph.js";
15
+ import { getOrExtractSymbols, hasFreshEntry } from "./AstSymbolCache.js";
16
+ /** How many neighbor files a single warm pass will touch — bounded so this stays cheap. */
17
+ const MAX_NEIGHBORS = 8;
18
+ const MAX_DEPTH = 1;
19
+ let extractorRef = null;
20
+ /**
21
+ * `extractSymbolsWithTsCompiler` lives in `mcp/tools.ts`, which already imports this module —
22
+ * importing it back here would be circular. The dispatcher registers it once at startup instead.
23
+ */
24
+ export function registerSymbolExtractor(extractor) {
25
+ extractorRef = extractor;
26
+ }
27
+ /**
28
+ * Fire-and-forget: warm the import-neighborhood of `filePath` after a real read. Safe to call
29
+ * unconditionally — it is a no-op (not just cheap, actually skipped) whenever the extractor
30
+ * hasn't been registered yet, since that means startup isn't finished.
31
+ */
32
+ export function warmNeighbors(workspaceRoot, filePath) {
33
+ if (!extractorRef)
34
+ return;
35
+ const extractor = extractorRef;
36
+ // Deferred and unref'd so it can never keep the process alive or delay the response that
37
+ // triggered it — this must cost the triggering call nothing.
38
+ const timer = setTimeout(() => {
39
+ try {
40
+ const rel = path.relative(workspaceRoot, filePath).split(path.sep).join("/");
41
+ const graph = buildDepGraphMap(workspaceRoot);
42
+ const node = graph.get(rel);
43
+ if (!node)
44
+ return;
45
+ const neighbors = bfsDepGraph(graph, rel, "forward", MAX_DEPTH);
46
+ let warmed = 0;
47
+ for (const [neighborRel, { node: neighborNode }] of neighbors) {
48
+ if (warmed >= MAX_NEIGHBORS)
49
+ break;
50
+ if (neighborRel === rel)
51
+ continue;
52
+ if (hasFreshEntry(neighborNode.abs))
53
+ continue; // already warm — nothing to do
54
+ if (!fs.existsSync(neighborNode.abs))
55
+ continue;
56
+ getOrExtractSymbols(neighborNode.abs, neighborRel, extractor);
57
+ warmed++;
58
+ }
59
+ }
60
+ catch {
61
+ // Warming must never surface an error anywhere the model or the user can see it.
62
+ }
63
+ }, 0);
64
+ timer.unref?.();
65
+ }
66
+ //# sourceMappingURL=SpeculativeWarmer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpeculativeWarmer.js","sourceRoot":"","sources":["../../../src/utils/SpeculativeWarmer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAa,MAAM,kBAAkB,CAAC;AAEjF,2FAA2F;AAC3F,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,IAAI,YAAY,GAAuF,IAAI,CAAC;AAE5G;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,SAAoF;IAEpF,YAAY,GAAG,SAAS,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,aAAqB,EAAE,QAAgB;IACnE,IAAI,CAAC,YAAY;QAAE,OAAO;IAC1B,MAAM,SAAS,GAAG,YAAY,CAAC;IAE/B,yFAAyF;IACzF,6DAA6D;IAC7D,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;QAC5B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7E,MAAM,KAAK,GAAG,gBAAgB,CAAC,aAAa,CAAC,CAAC;YAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,CAAC,IAAI;gBAAE,OAAO;YAElB,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;YAChE,IAAI,MAAM,GAAG,CAAC,CAAC;YACf,KAAK,MAAM,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,IAAI,SAAS,EAAE,CAAC;gBAC9D,IAAI,MAAM,IAAI,aAAa;oBAAE,MAAM;gBACnC,IAAI,WAAW,KAAK,GAAG;oBAAE,SAAS;gBAClC,IAAI,aAAa,CAAC,YAAY,CAAC,GAAG,CAAC;oBAAE,SAAS,CAAC,+BAA+B;gBAC9E,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAC/C,mBAAmB,CAAC,YAAY,CAAC,GAAG,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;gBAC9D,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,iFAAiF;QACnF,CAAC;IACH,CAAC,EAAE,CAAC,CAAC,CAAC;IACN,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;AAClB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"reportSavings.d.ts","sourceRoot":"","sources":["../../../src/utils/reportSavings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,KAAK,aAAa,GAAG,WAAW,GAAG,gBAAgB,GAAG,cAAc,GAAG,mBAAmB,GAAG,OAAO,CAAC;AAErG,UAAU,aAAa;IACrB,MAAM,EAAE,aAAa,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,aAAa,GAAG,aAAa,GAAG,cAAc,CAAC;AAYpD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAE1D;AAED,KAAK,UAAU,GAAG,iBAAiB,GAAG,YAAY,GAAG,WAAW,CAAC;AAEjE,UAAU,WAAW;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAErD"}
1
+ {"version":3,"file":"reportSavings.d.ts","sourceRoot":"","sources":["../../../src/utils/reportSavings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,KAAK,aAAa,GAAG,WAAW,GAAG,gBAAgB,GAAG,cAAc,GAAG,mBAAmB,GAAG,OAAO,CAAC;AAErG,UAAU,aAAa;IACrB,MAAM,EAAE,aAAa,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,aAAa,GAAG,aAAa,GAAG,cAAc,CAAC;AAYpD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAQ1D;AAED,KAAK,UAAU,GAAG,iBAAiB,GAAG,YAAY,GAAG,WAAW,CAAC;AAEjE,UAAU,WAAW;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAMrD"}
@@ -8,6 +8,7 @@ import fs from 'fs';
8
8
  import path from 'path';
9
9
  import os from 'os';
10
10
  import http from 'http';
11
+ import { recordRoiSaved, recordRoiSpent } from './RoiLedger.js';
11
12
  function getProxyPort() {
12
13
  try {
13
14
  const portFile = path.join(os.homedir(), '.lemma-cache/proxy.port');
@@ -24,6 +25,12 @@ function getProxyPort() {
24
25
  */
25
26
  export function reportSavings(payload) {
26
27
  post('/api/savings-record', payload);
28
+ if (payload.toolName) {
29
+ const tokens = 'tokens' in payload ? payload.tokens : Math.floor(Math.max(0, payload.charsBefore - payload.charsAfter) / 4);
30
+ // Best-effort, repo-scoped: process.cwd() is the workspace root for every MCP tool
31
+ // handler, same assumption `tools.ts` already makes throughout.
32
+ recordRoiSaved(process.cwd(), payload.toolName, tokens);
33
+ }
27
34
  }
28
35
  /**
29
36
  * Report tokens Lemma *spent* to the proxy's ledger. Same fire-and-forget contract
@@ -32,6 +39,10 @@ export function reportSavings(payload) {
32
39
  */
33
40
  export function reportCost(payload) {
34
41
  post('/api/cost-record', payload);
42
+ if (payload.toolName) {
43
+ const tokens = typeof payload.tokens === 'number' ? payload.tokens : Math.floor(Math.max(0, payload.chars || 0) / 4);
44
+ recordRoiSpent(process.cwd(), payload.toolName, tokens);
45
+ }
35
46
  }
36
47
  function post(pathname, payload) {
37
48
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"reportSavings.js","sourceRoot":"","sources":["../../../src/utils/reportSavings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAuBxB,SAAS,YAAY;IACnB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,yBAAyB,CAAC,CAAC;QACpE,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAClD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAAsB;IAClD,IAAI,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC;AAaD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,OAAoB;IAC7C,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,IAAI,CAAC,QAAgB,EAAE,OAAgB;IAC9C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAErC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;YACvB,QAAQ,EAAE,WAAW;YACrB,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;YACpB,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;aAC1C;SACF,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,yCAAyC;QACpE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChB,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,kDAAkD;IACpD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"reportSavings.js","sourceRoot":"","sources":["../../../src/utils/reportSavings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAuB7D,SAAS,YAAY;IACnB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,yBAAyB,CAAC,CAAC;QACpE,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAClD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAAsB;IAClD,IAAI,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5H,mFAAmF;QACnF,gEAAgE;QAChE,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC;AAaD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,OAAoB;IAC7C,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC;IAClC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACrH,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC;AAED,SAAS,IAAI,CAAC,QAAgB,EAAE,OAAgB;IAC9C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAErC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;YACvB,QAAQ,EAAE,WAAW;YACrB,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;YACpB,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;aAC1C;SACF,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,yCAAyC;QACpE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChB,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,kDAAkD;IACpD,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxuss/lemma",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "Intelligent AI Gateway for IDEs & Agents — Semantic cache, Privacy Firewall, Infrastructure Command Center, and Autonomous Cost-Optimization.",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",