@nxuss/lemma 1.7.1 → 1.7.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.
@@ -1,11 +1,18 @@
1
1
  export interface ComplexityRouterConfig {
2
2
  cheapModel?: string;
3
+ /**
4
+ * Cheap model to fall back to per provider family, keyed by a substring of the
5
+ * requested model id ('claude', 'gemini', ...). Overridable from lemma.config.json
6
+ * so a retired model id can be corrected without shipping a new build.
7
+ */
8
+ cheapModelsByFamily?: Record<string, string>;
3
9
  complexKeywords?: string[];
4
10
  complexityThreshold?: number;
5
11
  disabled?: boolean;
6
12
  }
7
13
  export declare class ComplexityRouter {
8
14
  private cheapModel;
15
+ private cheapModelsByFamily;
9
16
  private complexKeywords;
10
17
  private syntaxDensityThreshold;
11
18
  constructor(config?: ComplexityRouterConfig);
@@ -1 +1 @@
1
- {"version":3,"file":"ComplexityRouter.d.ts","sourceRoot":"","sources":["../../../src/proxy/ComplexityRouter.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAW;IAClC,OAAO,CAAC,sBAAsB,CAAS;gBAE3B,MAAM,GAAE,sBAA2B;IAWxC,YAAY,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAMlD,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE;CAqEvG;AAED,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"ComplexityRouter.d.ts","sourceRoot":"","sources":["../../../src/proxy/ComplexityRouter.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAYD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,mBAAmB,CAAyB;IACpD,OAAO,CAAC,eAAe,CAAW;IAClC,OAAO,CAAC,sBAAsB,CAAS;gBAE3B,MAAM,GAAE,sBAA2B;IAYxC,YAAY,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IASlD,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE;CAsEvG;AAED,eAAe,gBAAgB,CAAC"}
@@ -1,9 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ComplexityRouter = void 0;
4
+ /**
5
+ * Model ids go stale as providers retire them; these are defaults, not truth. If the
6
+ * router starts recommending a model your provider rejects, override
7
+ * routing.complexity.cheapModelsByFamily in lemma.config.json.
8
+ */
9
+ const DEFAULT_CHEAP_MODELS_BY_FAMILY = {
10
+ claude: 'claude-haiku-4-5-20251001',
11
+ gemini: 'gemini-2.0-flash',
12
+ };
4
13
  class ComplexityRouter {
5
14
  constructor(config = {}) {
6
15
  this.cheapModel = config.cheapModel || 'gpt-4o-mini';
16
+ this.cheapModelsByFamily = { ...DEFAULT_CHEAP_MODELS_BY_FAMILY, ...(config.cheapModelsByFamily || {}) };
7
17
  this.complexKeywords = config.complexKeywords || [
8
18
  'architecture', 'design', 'debug', 'trace', 'system', 'microservices',
9
19
  'refactor', 'optimize', 'implement', 'algorithm', 'distributed',
@@ -15,6 +25,9 @@ class ComplexityRouter {
15
25
  updateConfig(config) {
16
26
  if (config.cheapModel)
17
27
  this.cheapModel = config.cheapModel;
28
+ if (config.cheapModelsByFamily) {
29
+ this.cheapModelsByFamily = { ...this.cheapModelsByFamily, ...config.cheapModelsByFamily };
30
+ }
18
31
  if (config.complexKeywords)
19
32
  this.complexKeywords = config.complexKeywords;
20
33
  if (config.complexityThreshold)
@@ -72,11 +85,11 @@ class ComplexityRouter {
72
85
  let cheapModel = this.cheapModel;
73
86
  if (originalModel) {
74
87
  const lowerModel = originalModel.toLowerCase();
75
- if (lowerModel.includes('claude')) {
76
- cheapModel = 'claude-3-5-haiku-20241022';
77
- }
78
- else if (lowerModel.includes('gemini')) {
79
- cheapModel = 'gemini-1.5-flash';
88
+ for (const [family, model] of Object.entries(this.cheapModelsByFamily)) {
89
+ if (lowerModel.includes(family)) {
90
+ cheapModel = model;
91
+ break;
92
+ }
80
93
  }
81
94
  }
82
95
  console.log(`[ComplexityRouter] Low complexity detected. Routing to ${cheapModel}`);
@@ -1 +1 @@
1
- {"version":3,"file":"ComplexityRouter.js","sourceRoot":"","sources":["../../../src/proxy/ComplexityRouter.ts"],"names":[],"mappings":";;;AAQA,MAAa,gBAAgB;IAK3B,YAAY,SAAiC,EAAE;QAC7C,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,aAAa,CAAC;QACrD,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,IAAI;YAC/C,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe;YACrE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa;YAC/D,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU;YAC7D,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB;SAC7D,CAAC;QACF,IAAI,CAAC,sBAAsB,GAAG,MAAM,CAAC,mBAAmB,IAAI,IAAI,CAAC,CAAC,uBAAuB;IAC3F,CAAC;IAEM,YAAY,CAAC,MAA8B;QAChD,IAAI,MAAM,CAAC,UAAU;YAAE,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QAC3D,IAAI,MAAM,CAAC,eAAe;YAAE,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;QAC1E,IAAI,MAAM,CAAC,mBAAmB;YAAE,IAAI,CAAC,sBAAsB,GAAG,MAAM,CAAC,mBAAmB,CAAC;IAC3F,CAAC;IAEM,QAAQ,CAAC,MAAc,EAAE,aAAsB;QACpD,+DAA+D;QAC/D,MAAM,OAAO,GAAG,aAAa,EAAE,UAAU,CAAC,OAAO,CAAC;YAClC,aAAa,EAAE,UAAU,CAAC,SAAS,CAAC;YACpC,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC;YACnC,aAAa,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;QAEnD,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,EAAE,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QAClE,CAAC;QAED,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAClC,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;QACvF,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;QACnC,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,gBAAgB,GAAG,EAAE,CAAC;QAE1B,+CAA+C;QAC/C,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,SAAS,GAAG,IAAI,CAAC;YACjB,gBAAgB,GAAG,gCAAgC,CAAC;QACtD,CAAC;QAED,oCAAoC;QACpC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,cAAc,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;YAC9E,MAAM,OAAO,GAAG,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC;YAC/C,IAAI,OAAO,GAAG,IAAI,CAAC,sBAAsB,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;gBAChE,SAAS,GAAG,IAAI,CAAC;gBACjB,gBAAgB,GAAG,wBAAwB,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;YAC5E,CAAC;QACH,CAAC;QAED,kDAAkD;QAClD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBACtC,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;oBACvB,SAAS,GAAG,IAAI,CAAC;oBACjB,gBAAgB,GAAG,6BAA6B,EAAE,GAAG,CAAC;oBACtD,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QAED,iFAAiF;QACjF,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;YACvC,SAAS,GAAG,IAAI,CAAC;YACjB,gBAAgB,GAAG,yBAAyB,MAAM,CAAC,MAAM,SAAS,CAAC;QACrE,CAAC;QAED,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,gDAAgD,gBAAgB,iBAAiB,aAAa,IAAI,QAAQ,EAAE,CAAC,CAAC;YAC1H,OAAO,EAAE,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QAClE,CAAC;aAAM,CAAC;YACN,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;YACjC,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,UAAU,GAAG,aAAa,CAAC,WAAW,EAAE,CAAC;gBAC/C,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAClC,UAAU,GAAG,2BAA2B,CAAC;gBAC3C,CAAC;qBAAM,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACzC,UAAU,GAAG,kBAAkB,CAAC;gBAClC,CAAC;YACH,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,0DAA0D,UAAU,EAAE,CAAC,CAAC;YACpF,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;QAClD,CAAC;IACH,CAAC;CACF;AA3FD,4CA2FC;AAED,kBAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"ComplexityRouter.js","sourceRoot":"","sources":["../../../src/proxy/ComplexityRouter.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,12 @@ export interface ReceiptEvent {
14
14
  meta?: Record<string, unknown>;
15
15
  }
16
16
  export declare function recordReceiptEvent(type: ReceiptEventType, label: string, meta?: Record<string, unknown>): void;
17
+ /**
18
+ * Number of events recorded so far. The dispatcher samples this around a handler to
19
+ * tell whether the handler already logged something specific (a cache hit, a miss)
20
+ * before falling back to a generic tool_call entry.
21
+ */
22
+ export declare function getLedgerLength(): number;
17
23
  export interface ReceiptSummary {
18
24
  totalEvents: number;
19
25
  byType: Record<ReceiptEventType, number>;
@@ -1 +1 @@
1
- {"version":3,"file":"TokenReceipt.d.ts","sourceRoot":"","sources":["../../../src/utils/TokenReceipt.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,gBAAgB,GACxB,iBAAiB,GACjB,oBAAoB,GACpB,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAID,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAK9G;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,SAAK,GAAG,cAAc,CAc5D;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
1
+ {"version":3,"file":"TokenReceipt.d.ts","sourceRoot":"","sources":["../../../src/utils/TokenReceipt.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,gBAAgB,GACxB,iBAAiB,GACjB,oBAAoB,GACpB,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAID,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAK9G;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,SAAK,GAAG,cAAc,CAc5D;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -9,6 +9,7 @@
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.recordReceiptEvent = recordReceiptEvent;
12
+ exports.getLedgerLength = getLedgerLength;
12
13
  exports.getReceiptSummary = getReceiptSummary;
13
14
  exports.clearReceiptLedger = clearReceiptLedger;
14
15
  const MAX_EVENTS = 2000;
@@ -19,6 +20,14 @@ function recordReceiptEvent(type, label, meta) {
19
20
  sessionLedger.splice(0, sessionLedger.length - MAX_EVENTS);
20
21
  }
21
22
  }
23
+ /**
24
+ * Number of events recorded so far. The dispatcher samples this around a handler to
25
+ * tell whether the handler already logged something specific (a cache hit, a miss)
26
+ * before falling back to a generic tool_call entry.
27
+ */
28
+ function getLedgerLength() {
29
+ return sessionLedger.length;
30
+ }
22
31
  function getReceiptSummary(limit = 20) {
23
32
  const byType = {
24
33
  exact_cache_hit: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"TokenReceipt.js","sourceRoot":"","sources":["../../../src/utils/TokenReceipt.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAoBH,gDAKC;AAQD,8CAcC;AAED,gDAEC;AAjDD,MAAM,UAAU,GAAG,IAAI,CAAC;AAgBxB,MAAM,aAAa,GAAmB,EAAE,CAAC;AAEzC,SAAgB,kBAAkB,CAAC,IAAsB,EAAE,KAAa,EAAE,IAA8B;IACtG,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,IAAI,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE,CAAC;QACtC,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAQD,SAAgB,iBAAiB,CAAC,KAAK,GAAG,EAAE;IAC1C,MAAM,MAAM,GAAqC;QAC/C,eAAe,EAAE,CAAC;QAClB,kBAAkB,EAAE,CAAC;QACrB,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,CAAC;KACb,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,aAAa;QAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;IAChD,OAAO;QACL,WAAW,EAAE,aAAa,CAAC,MAAM;QACjC,MAAM;QACN,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,SAAgB,kBAAkB;IAChC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"TokenReceipt.js","sourceRoot":"","sources":["../../../src/utils/TokenReceipt.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAoBH,gDAKC;AAOD,0CAEC;AAQD,8CAcC;AAED,gDAEC;AA1DD,MAAM,UAAU,GAAG,IAAI,CAAC;AAgBxB,MAAM,aAAa,GAAmB,EAAE,CAAC;AAEzC,SAAgB,kBAAkB,CAAC,IAAsB,EAAE,KAAa,EAAE,IAA8B;IACtG,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,IAAI,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE,CAAC;QACtC,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe;IAC7B,OAAO,aAAa,CAAC,MAAM,CAAC;AAC9B,CAAC;AAQD,SAAgB,iBAAiB,CAAC,KAAK,GAAG,EAAE;IAC1C,MAAM,MAAM,GAAqC;QAC/C,eAAe,EAAE,CAAC;QAClB,kBAAkB,EAAE,CAAC;QACrB,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,CAAC;KACb,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,aAAa;QAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;IAChD,OAAO;QACL,WAAW,EAAE,aAAa,CAAC,MAAM;QACjC,MAAM;QACN,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,SAAgB,kBAAkB;IAChC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;AAC3B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AA2hCnE,oFAAoF;AACpF,wBAAgB,kBAAkB,IAAI,MAAM,CAM3C;AAED,oEAAoE;AACpE,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAED,iEAAiE;AACjE,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,IAAI;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAQA;AAoHD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAC1C,IAAI,CA+DN"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAiiCnE,oFAAoF;AACpF,wBAAgB,kBAAkB,IAAI,MAAM,CAM3C;AAED,oEAAoE;AACpE,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAED,iEAAiE;AACjE,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,IAAI;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAQA;AAoHD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqCD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAC1C,IAAI,CAwEN"}
@@ -33,7 +33,7 @@ import { surgicalASTInsert } from "../utils/SurgicalASTInsert.js";
33
33
  import { getInfraToolDefinitions, getInfraToolHandlers, INFRA_TOOL_NAMES } from "../infra/mcp-tools.js";
34
34
  import { resolveToolSurface, buildToolboxCatalog } from "./tool-profiles.js";
35
35
  import { lookupStateHash, storeStateHash } from "../utils/StateHashCache.js";
36
- import { recordReceiptEvent, getReceiptSummary } from "../utils/TokenReceipt.js";
36
+ import { recordReceiptEvent, getReceiptSummary, getLedgerLength } from "../utils/TokenReceipt.js";
37
37
  import { distillCommandOutput, buildDistillFooter, readRegion } from "../utils/CommandOutputDistiller.js";
38
38
  import { findMatch, reindentReplacement } from "../utils/PatchMatcher.js";
39
39
  import { searchWorkspace, groupSearchResults, parseExtensionFilter } from "../utils/WorkspaceSearch.js";
@@ -103,6 +103,11 @@ const toolDefinitions = [
103
103
  properties: {
104
104
  query: { type: "string", description: "The natural language query" },
105
105
  limit: { type: "number", description: "Maximum results to return", default: 5 },
106
+ minSimilarity: {
107
+ type: "number",
108
+ description: "Similarity floor (0.0-1.0) a memory must clear to be returned. Lowering this surfaces loosely related memories that are usually noise — leave it alone unless a known-relevant memory is being filtered out.",
109
+ default: 0.75,
110
+ },
106
111
  },
107
112
  required: ["query"],
108
113
  },
@@ -1156,6 +1161,41 @@ async function handleToolbox(args) {
1156
1161
  }
1157
1162
  throw new Error(`Unknown action: ${action}. Use 'list', 'schema', or 'call'.`);
1158
1163
  }
1164
+ /** Tools whose whole job is to put file contents into the model's context. */
1165
+ const FILE_READ_TOOLS = new Set([
1166
+ "read_workspace_file",
1167
+ "smart_file_slice",
1168
+ "get_symbol_surgical_context",
1169
+ "get_ast_hologram",
1170
+ "list_workspace_dir",
1171
+ "search_workspace",
1172
+ "read_token_budgeted",
1173
+ "bulk_file_digest",
1174
+ "import_tree_context",
1175
+ ]);
1176
+ function receiptTypeForTool(name) {
1177
+ return FILE_READ_TOOLS.has(name) ? "file_read" : "tool_call";
1178
+ }
1179
+ /**
1180
+ * A receipt entry is only useful if it says what the call was about. Pull the most
1181
+ * identifying argument without dragging whole file contents into the ledger.
1182
+ */
1183
+ function receiptLabelMeta(name, args) {
1184
+ if (!args)
1185
+ return {};
1186
+ const meta = {};
1187
+ if (typeof args.filePath === "string")
1188
+ meta.filePath = args.filePath;
1189
+ if (typeof args.dirPath === "string")
1190
+ meta.dirPath = args.dirPath;
1191
+ if (typeof args.query === "string")
1192
+ meta.query = args.query.substring(0, 100);
1193
+ if (typeof args.command === "string")
1194
+ meta.command = args.command.substring(0, 100);
1195
+ if (typeof args.symbolName === "string")
1196
+ meta.symbolName = args.symbolName;
1197
+ return meta;
1198
+ }
1159
1199
  export function setupToolsHandlers(server, onToolCall) {
1160
1200
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
1161
1201
  tools: toolDefinitionsArray,
@@ -1190,7 +1230,16 @@ export function setupToolsHandlers(server, onToolCall) {
1190
1230
  throw new Error(`Unknown tool: ${name}`);
1191
1231
  }
1192
1232
  try {
1233
+ // Sampled around the handler so a tool that logs its own, more specific event
1234
+ // (a cache hit, a miss) isn't double-counted by the generic entry below.
1235
+ const ledgerBefore = getLedgerLength();
1193
1236
  const result = await handler((args || {}));
1237
+ if (getLedgerLength() === ledgerBefore) {
1238
+ recordReceiptEvent(receiptTypeForTool(name), name, {
1239
+ tool: name,
1240
+ ...receiptLabelMeta(name, args),
1241
+ });
1242
+ }
1194
1243
  const tokensImpact = estimateTokensFromResult(result);
1195
1244
  onToolCall?.({
1196
1245
  tool: name,
@@ -1225,16 +1274,35 @@ async function handleScrubPrivacy(args) {
1225
1274
  const { maskedPrompt } = scrubber.mask(text);
1226
1275
  return { content: [{ type: "text", text: maskedPrompt }] };
1227
1276
  }
1277
+ /**
1278
+ * Minimum similarity for a stored memory to be offered for reuse. Shared by
1279
+ * search_memory and smarter_cache so the two never disagree about whether the same
1280
+ * query is a hit.
1281
+ */
1282
+ const DEFAULT_MEMORY_SIMILARITY_FLOOR = 0.75;
1228
1283
  async function handleSearchMemory(args) {
1229
1284
  const query = args?.query;
1230
1285
  const limit = args?.limit || 5;
1286
+ // A floor of 0 returns the nearest neighbour no matter how unrelated it is, and the
1287
+ // formatting below then presents it as a reusable memory. Match smarter_cache's
1288
+ // threshold so both paths agree on what counts as a hit.
1289
+ const minSimilarity = typeof args?.minSimilarity === "number" ? args.minSimilarity : DEFAULT_MEMORY_SIMILARITY_FLOOR;
1231
1290
  if (!query)
1232
1291
  throw new Error("Query is required");
1233
1292
  try {
1234
1293
  const brain = getBrain();
1235
- const results = brain.search(query, limit, 0, { projectId: deriveProjectId() });
1294
+ const results = brain.search(query, limit, minSimilarity, { projectId: deriveProjectId() });
1236
1295
  if (results.length === 0) {
1237
- return { content: [{ type: "text", text: "No relevant memories found in Lemma's Brain." }] };
1296
+ recordReceiptEvent("reasoning", query.substring(0, 100), {
1297
+ tool: "search_memory",
1298
+ reason: `no memory above the ${minSimilarity} similarity floor`,
1299
+ });
1300
+ return {
1301
+ content: [{
1302
+ type: "text",
1303
+ text: `No memory in Lemma's Brain scored at or above the ${(minSimilarity * 100).toFixed(0)}% similarity floor for this query. Investigate from scratch — there is nothing safe to reuse.`,
1304
+ }],
1305
+ };
1238
1306
  }
1239
1307
  const fresh = results.filter((r) => r.fresh);
1240
1308
  const stale = results.filter((r) => !r.fresh);
@@ -1245,6 +1313,17 @@ async function handleSearchMemory(args) {
1245
1313
  const bestFresh = fresh[0];
1246
1314
  const tokensSaved = Math.max(100, Math.floor(String(bestFresh.response).length / 4));
1247
1315
  reportSavings({ source: "cache", tokens: tokensSaved, toolName: "search_memory", query: query.substring(0, 100) });
1316
+ recordReceiptEvent("semantic_cache_hit", query.substring(0, 100), {
1317
+ tool: "search_memory",
1318
+ similarity: bestFresh.similarity,
1319
+ tokensSaved,
1320
+ });
1321
+ }
1322
+ else {
1323
+ recordReceiptEvent("reasoning", query.substring(0, 100), {
1324
+ tool: "search_memory",
1325
+ reason: `${stale.length} similar memory/memories found but all stale — must re-verify`,
1326
+ });
1248
1327
  }
1249
1328
  const parts = [];
1250
1329
  if (fresh.length > 0) {
@@ -1273,12 +1352,13 @@ async function handleStoreMemory(args) {
1273
1352
  try {
1274
1353
  const brain = getBrain();
1275
1354
  const storeRes = brain.store(query, responseText, provider, 0.92, filePaths);
1276
- const tokensSaved = Math.max(100, Math.floor(responseText.length / 4));
1277
- reportSavings({
1278
- source: "cache",
1279
- tokens: tokensSaved,
1280
- toolName: "store_memory",
1281
- query: query.substring(0, 100),
1355
+ // Storing a memory saves nothing — it only creates the chance of a saving later.
1356
+ // Crediting tokens here inflated the ledger on write and then credited the same
1357
+ // answer again on every read. The saving is booked by search_memory on a fresh hit.
1358
+ recordReceiptEvent("tool_call", `store_memory: ${query.substring(0, 100)}`, {
1359
+ tool: "store_memory",
1360
+ filePaths,
1361
+ note: "memory written — no tokens saved yet",
1282
1362
  });
1283
1363
  const trackingNote = filePaths && filePaths.length > 0 ? ` Tracking freshness against ${filePaths.length} file(s) — this memory auto-invalidates if they change.` : "";
1284
1364
  return { content: [{ type: "text", text: `Success: Memory stored. ${storeRes.reason}${trackingNote}` }] };
@@ -1800,7 +1880,7 @@ async function handleGetProjectOnboarding(_args) {
1800
1880
  ],
1801
1881
  };
1802
1882
  }
1803
- function extractSymbolsWithTsCompiler(filePath, relPath) {
1883
+ function extractSymbolsWithTsCompiler(filePath, relPath, parseErrors) {
1804
1884
  try {
1805
1885
  const src = fs.readFileSync(filePath, "utf8");
1806
1886
  const sourceFile = ts.createSourceFile(filePath, src, ts.ScriptTarget.Latest, true);
@@ -1808,7 +1888,10 @@ function extractSymbolsWithTsCompiler(filePath, relPath) {
1808
1888
  function visit(node) {
1809
1889
  const mods = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
1810
1890
  const isExport = mods?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
1811
- if (isExport || ts.isSourceFile(node.parent)) {
1891
+ // node.parent is undefined on the SourceFile root itself, and ts.isSourceFile()
1892
+ // dereferences .kind without a guard — calling it unguarded threw on the very
1893
+ // first visit(), so the catch below swallowed it and every file returned [].
1894
+ if (isExport || (node.parent && ts.isSourceFile(node.parent))) {
1812
1895
  if (ts.isFunctionDeclaration(node) && node.name) {
1813
1896
  symbols.push({
1814
1897
  kind: "function",
@@ -1877,10 +1960,11 @@ function extractSymbolsWithTsCompiler(filePath, relPath) {
1877
1960
  }
1878
1961
  catch (err) {
1879
1962
  logWarn("get_ast_hologram", `Could not parse ${relPath}: ${err}`);
1963
+ parseErrors?.push(`${relPath}: ${err}`);
1880
1964
  return [];
1881
1965
  }
1882
1966
  }
1883
- function walkDirForHologram(dir, rel, extensions) {
1967
+ function walkDirForHologram(dir, rel, extensions, stats) {
1884
1968
  let allSymbols = [];
1885
1969
  try {
1886
1970
  const entries = fs.readdirSync(dir, { withFileTypes: true });
@@ -1890,18 +1974,20 @@ function walkDirForHologram(dir, rel, extensions) {
1890
1974
  const fullPath = path.join(dir, entry.name);
1891
1975
  const relPath = rel ? path.join(rel, entry.name) : entry.name;
1892
1976
  if (entry.isDirectory()) {
1893
- allSymbols = allSymbols.concat(walkDirForHologram(fullPath, relPath, extensions));
1977
+ allSymbols = allSymbols.concat(walkDirForHologram(fullPath, relPath, extensions, stats));
1894
1978
  }
1895
1979
  else {
1896
1980
  const ext = entry.name.split(".").pop() || "";
1897
1981
  if (extensions.includes(ext)) {
1898
- allSymbols = allSymbols.concat(extractSymbolsWithTsCompiler(fullPath, relPath));
1982
+ stats.filesScanned++;
1983
+ allSymbols = allSymbols.concat(extractSymbolsWithTsCompiler(fullPath, relPath, stats.parseErrors));
1899
1984
  }
1900
1985
  }
1901
1986
  }
1902
1987
  }
1903
1988
  catch (err) {
1904
1989
  logWarn("get_ast_hologram", `Error walking dir ${dir}: ${err}`);
1990
+ stats.parseErrors.push(`walk ${rel || "."}: ${err}`);
1905
1991
  }
1906
1992
  return allSymbols;
1907
1993
  }
@@ -1910,18 +1996,32 @@ async function handleGetAstHologram(args) {
1910
1996
  const extensions = args?.extensions || ["ts", "tsx", "js", "jsx"];
1911
1997
  const workspaceRoot = process.cwd();
1912
1998
  const { resolved } = safeResolvePath(workspaceRoot, dirPath);
1913
- const symbols = walkDirForHologram(resolved, dirPath, extensions);
1999
+ const stats = { filesScanned: 0, parseErrors: [] };
2000
+ const symbols = walkDirForHologram(resolved, dirPath, extensions, stats);
1914
2001
  const byFile = {};
1915
2002
  symbols.forEach((s) => {
1916
2003
  if (!byFile[s.file])
1917
2004
  byFile[s.file] = [];
1918
2005
  byFile[s.file].push({ kind: s.kind, name: s.name, line: s.line });
1919
2006
  });
2007
+ // An empty map is reported as a failure, not as an answer: a silent "0 symbols" is
2008
+ // indistinguishable from a working scan of an empty dir, which is how a parse bug
2009
+ // stayed invisible here before. Say when nothing could be read.
2010
+ const warning = stats.filesScanned === 0
2011
+ ? `No files matching [${extensions.join(", ")}] were found under "${dirPath || "."}" — check the path and extensions.`
2012
+ : symbols.length === 0
2013
+ ? `Scanned ${stats.filesScanned} file(s) but extracted 0 symbols — this is very likely an extraction failure, not an empty codebase. Do not treat this as "the directory has no exports".`
2014
+ : undefined;
1920
2015
  const hologram = {
1921
2016
  workspace: path.basename(workspaceRoot),
1922
2017
  scannedDir: dirPath || ".",
1923
2018
  totalSymbols: symbols.length,
2019
+ filesScanned: stats.filesScanned,
1924
2020
  totalFiles: Object.keys(byFile).length,
2021
+ ...(warning ? { warning } : {}),
2022
+ ...(stats.parseErrors.length > 0
2023
+ ? { parseErrors: stats.parseErrors.slice(0, 10), parseErrorCount: stats.parseErrors.length }
2024
+ : {}),
1925
2025
  map: byFile,
1926
2026
  };
1927
2027
  return { content: [{ type: "text", text: JSON.stringify(hologram, null, 2) }] };
@@ -2324,7 +2424,7 @@ async function handleCompressContext(args) {
2324
2424
  async function handleSmarterCache(args) {
2325
2425
  const query = args?.query;
2326
2426
  const context = args?.context || "";
2327
- const threshold = typeof args?.threshold === "number" ? args.threshold : 0.75;
2427
+ const threshold = typeof args?.threshold === "number" ? args.threshold : DEFAULT_MEMORY_SIMILARITY_FLOOR;
2328
2428
  if (!query)
2329
2429
  throw new Error("query is required");
2330
2430
  const fullQuery = context ? `${query}\n\nContext: ${context}` : query;