@nxuss/lemma 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +114 -5
  2. package/bin/checkpoint.js +49 -0
  3. package/bin/init.js +21 -2
  4. package/dist/cjs/mcp/prompts.d.ts +1 -2
  5. package/dist/cjs/mcp/prompts.d.ts.map +1 -1
  6. package/dist/cjs/mcp/prompts.js +29 -126
  7. package/dist/cjs/mcp/prompts.js.map +1 -1
  8. package/dist/cjs/mcp/resources.d.ts.map +1 -1
  9. package/dist/cjs/mcp/resources.js +4 -4
  10. package/dist/cjs/mcp/resources.js.map +1 -1
  11. package/dist/cjs/mcp/tool-profiles.d.ts +55 -0
  12. package/dist/cjs/mcp/tool-profiles.d.ts.map +1 -0
  13. package/dist/cjs/mcp/tool-profiles.js +204 -0
  14. package/dist/cjs/mcp/tool-profiles.js.map +1 -0
  15. package/dist/cjs/mcp/tools.d.ts +12 -0
  16. package/dist/cjs/mcp/tools.d.ts.map +1 -1
  17. package/dist/cjs/mcp/tools.js +175 -26
  18. package/dist/cjs/mcp/tools.js.map +1 -1
  19. package/dist/cjs/subconscious/TheBrainV2.d.ts +36 -2
  20. package/dist/cjs/subconscious/TheBrainV2.d.ts.map +1 -1
  21. package/dist/cjs/subconscious/TheBrainV2.js +104 -23
  22. package/dist/cjs/subconscious/TheBrainV2.js.map +1 -1
  23. package/dist/cjs/utils/AutoContextBundle.d.ts.map +1 -1
  24. package/dist/cjs/utils/AutoContextBundle.js +17 -20
  25. package/dist/cjs/utils/AutoContextBundle.js.map +1 -1
  26. package/dist/cjs/utils/ConversationCheckpoint.d.ts +13 -0
  27. package/dist/cjs/utils/ConversationCheckpoint.d.ts.map +1 -1
  28. package/dist/cjs/utils/ConversationCheckpoint.js +58 -0
  29. package/dist/cjs/utils/ConversationCheckpoint.js.map +1 -1
  30. package/dist/cjs/utils/TestAutofixInterceptor.d.ts.map +1 -1
  31. package/dist/cjs/utils/TestAutofixInterceptor.js +15 -26
  32. package/dist/cjs/utils/TestAutofixInterceptor.js.map +1 -1
  33. package/dist/esm/api/dashboardRoutes.d.ts +3 -3
  34. package/dist/esm/api/dashboardRoutes.js +1 -1
  35. package/dist/esm/api/server.d.ts +4 -4
  36. package/dist/esm/api/server.js +3 -3
  37. package/dist/esm/autopilot/AutopilotWatcher.js +3 -3
  38. package/dist/esm/autopilot/Notifier.d.ts +1 -1
  39. package/dist/esm/cli/lemma-proxy.js +14 -14
  40. package/dist/esm/cli/stack.d.ts +4 -4
  41. package/dist/esm/cli/stack.js +5 -5
  42. package/dist/esm/cloud/KeyManager.d.ts +1 -1
  43. package/dist/esm/cloud/KeyManager.js +1 -1
  44. package/dist/esm/cloud/TenantCache.d.ts +1 -1
  45. package/dist/esm/cloud/TenantCache.js +1 -1
  46. package/dist/esm/cloud/index.d.ts +3 -3
  47. package/dist/esm/cloud/index.js +3 -3
  48. package/dist/esm/cloud/server.d.ts +3 -3
  49. package/dist/esm/cloud/server.js +5 -5
  50. package/dist/esm/config/index.d.ts +1 -1
  51. package/dist/esm/config/index.js +1 -1
  52. package/dist/esm/consensus/index.d.ts +2 -2
  53. package/dist/esm/consensus/index.js +2 -2
  54. package/dist/esm/core/AgentRegistry.d.ts +1 -1
  55. package/dist/esm/core/AgentRegistry.js +1 -1
  56. package/dist/esm/core/DashboardBroadcaster.d.ts +3 -3
  57. package/dist/esm/core/DashboardBroadcaster.js +2 -2
  58. package/dist/esm/core/DashboardWebSocketServer.d.ts +1 -1
  59. package/dist/esm/core/DashboardWebSocketServer.js +1 -1
  60. package/dist/esm/core/GhostAlchemist.js +1 -1
  61. package/dist/esm/core/GhostListener.js +2 -2
  62. package/dist/esm/core/OrchestrationEngine.d.ts +3 -3
  63. package/dist/esm/core/OrchestrationEngine.js +2 -2
  64. package/dist/esm/core/SubconsciousEngine.d.ts +1 -1
  65. package/dist/esm/core/SubconsciousEngine.js +2 -2
  66. package/dist/esm/core/WebSocketServer.d.ts +2 -2
  67. package/dist/esm/core/WebSocketServer.js +1 -1
  68. package/dist/esm/core/index.d.ts +6 -6
  69. package/dist/esm/core/index.js +6 -6
  70. package/dist/esm/core/router.d.ts +3 -3
  71. package/dist/esm/core/router.js +1 -1
  72. package/dist/esm/db/database.js +1 -1
  73. package/dist/esm/embed.d.ts +2 -2
  74. package/dist/esm/embed.js +2 -2
  75. package/dist/esm/examples/basic-usage.js +1 -1
  76. package/dist/esm/index.d.ts +14 -14
  77. package/dist/esm/index.js +14 -14
  78. package/dist/esm/infra/analyzers/DriftDetector.d.ts +1 -1
  79. package/dist/esm/infra/analyzers/InfraAnalyzer.d.ts +1 -1
  80. package/dist/esm/infra/analyzers/MonolithAnalyzer.d.ts +1 -1
  81. package/dist/esm/infra/analyzers/index.d.ts +3 -3
  82. package/dist/esm/infra/analyzers/index.js +3 -3
  83. package/dist/esm/infra/cli.js +2 -2
  84. package/dist/esm/infra/config.d.ts +1 -1
  85. package/dist/esm/infra/index.d.ts +5 -5
  86. package/dist/esm/infra/index.js +5 -5
  87. package/dist/esm/infra/mcp-tools.js +5 -5
  88. package/dist/esm/infra/providers/InfraProvider.d.ts +1 -1
  89. package/dist/esm/infra/providers/JenkinsProvider.d.ts +2 -2
  90. package/dist/esm/infra/providers/OKDProvider.d.ts +2 -2
  91. package/dist/esm/infra/providers/TerraformProvider.d.ts +2 -2
  92. package/dist/esm/infra/providers/index.d.ts +5 -5
  93. package/dist/esm/infra/providers/index.js +3 -3
  94. package/dist/esm/mcp/index.js +5 -5
  95. package/dist/esm/mcp/prompts.d.ts +1 -2
  96. package/dist/esm/mcp/prompts.d.ts.map +1 -1
  97. package/dist/esm/mcp/prompts.js +28 -125
  98. package/dist/esm/mcp/prompts.js.map +1 -1
  99. package/dist/esm/mcp/resources.d.ts.map +1 -1
  100. package/dist/esm/mcp/resources.js +5 -5
  101. package/dist/esm/mcp/resources.js.map +1 -1
  102. package/dist/esm/mcp/tool-profiles.d.ts +55 -0
  103. package/dist/esm/mcp/tool-profiles.d.ts.map +1 -0
  104. package/dist/esm/mcp/tool-profiles.js +196 -0
  105. package/dist/esm/mcp/tool-profiles.js.map +1 -0
  106. package/dist/esm/mcp/tools.d.ts +12 -0
  107. package/dist/esm/mcp/tools.d.ts.map +1 -1
  108. package/dist/esm/mcp/tools.js +209 -63
  109. package/dist/esm/mcp/tools.js.map +1 -1
  110. package/dist/esm/observability/IdeContextSync.d.ts +1 -1
  111. package/dist/esm/observability/IdeContextSync.js +5 -5
  112. package/dist/esm/pr-review/PRReviewEngine.d.ts +1 -1
  113. package/dist/esm/pr-review/PRReviewEngine.js +4 -4
  114. package/dist/esm/pr-review/ReviewStore.d.ts +1 -1
  115. package/dist/esm/pr-review/adapters/AzureDevOpsAdapter.d.ts +2 -2
  116. package/dist/esm/pr-review/adapters/AzureDevOpsAdapter.js +1 -1
  117. package/dist/esm/pr-review/adapters/GitHubAdapter.d.ts +2 -2
  118. package/dist/esm/pr-review/adapters/GitHubAdapter.js +1 -1
  119. package/dist/esm/pr-review/adapters/GitLabAdapter.d.ts +2 -2
  120. package/dist/esm/pr-review/adapters/GitLabAdapter.js +1 -1
  121. package/dist/esm/pr-review/adapters/GitPlatformAdapter.d.ts +1 -1
  122. package/dist/esm/pr-review/adapters/index.d.ts +4 -4
  123. package/dist/esm/pr-review/adapters/index.js +3 -3
  124. package/dist/esm/pr-review/analyzers/QualityGrader.d.ts +1 -1
  125. package/dist/esm/pr-review/analyzers/RegressionDetector.d.ts +1 -1
  126. package/dist/esm/pr-review/analyzers/SecurityScanner.d.ts +1 -1
  127. package/dist/esm/pr-review/analyzers/StaticAnalyzer.d.ts +1 -1
  128. package/dist/esm/pr-review/analyzers/index.d.ts +8 -8
  129. package/dist/esm/pr-review/analyzers/index.js +4 -4
  130. package/dist/esm/pr-review/bridge/BrainBridge.d.ts +1 -1
  131. package/dist/esm/pr-review/bridge/BrainBridge.js +1 -1
  132. package/dist/esm/pr-review/bridge/index.d.ts +1 -1
  133. package/dist/esm/pr-review/bridge/index.js +1 -1
  134. package/dist/esm/pr-review/index.d.ts +4 -4
  135. package/dist/esm/pr-review/index.js +2 -2
  136. package/dist/esm/pr-review/reviewRunner.d.ts +2 -2
  137. package/dist/esm/pr-review/reviewRunner.js +6 -6
  138. package/dist/esm/pr-review/webhook/WebhookServer.js +1 -1
  139. package/dist/esm/pr-review/webhook/index.d.ts +1 -1
  140. package/dist/esm/pr-review/webhook/index.js +1 -1
  141. package/dist/esm/protocol/flows.d.ts +1 -1
  142. package/dist/esm/protocol/flows.js +1 -1
  143. package/dist/esm/protocol/index.d.ts +4 -4
  144. package/dist/esm/protocol/index.js +4 -4
  145. package/dist/esm/protocol/utils.d.ts +1 -1
  146. package/dist/esm/protocol/utils.js +2 -2
  147. package/dist/esm/protocol/validators.d.ts +1 -1
  148. package/dist/esm/protocol/validators.js +1 -1
  149. package/dist/esm/proxy/ContextInjector.d.ts +1 -1
  150. package/dist/esm/proxy/Gatekeeper.d.ts +1 -1
  151. package/dist/esm/security/SecurityMiddleware.d.ts +1 -1
  152. package/dist/esm/security/SecurityMiddleware.js +3 -3
  153. package/dist/esm/security/index.d.ts +4 -4
  154. package/dist/esm/security/index.js +4 -4
  155. package/dist/esm/speculative/SpeculativeCache.d.ts +2 -2
  156. package/dist/esm/speculative/SpeculativeEngine.d.ts +2 -2
  157. package/dist/esm/speculative/SpeculativeEngine.js +3 -3
  158. package/dist/esm/speculative/WorkerPool.d.ts +1 -1
  159. package/dist/esm/speculative/index.d.ts +4 -4
  160. package/dist/esm/speculative/index.js +4 -4
  161. package/dist/esm/subconscious/SemanticCache.d.ts +2 -2
  162. package/dist/esm/subconscious/SemanticCache.js +2 -2
  163. package/dist/esm/subconscious/SubconsciousEngine.d.ts +1 -1
  164. package/dist/esm/subconscious/SubconsciousEngine.js +3 -3
  165. package/dist/esm/subconscious/TheBrainV2.d.ts +36 -2
  166. package/dist/esm/subconscious/TheBrainV2.d.ts.map +1 -1
  167. package/dist/esm/subconscious/TheBrainV2.js +103 -23
  168. package/dist/esm/subconscious/TheBrainV2.js.map +1 -1
  169. package/dist/esm/subconscious/index.d.ts +4 -4
  170. package/dist/esm/subconscious/index.js +4 -4
  171. package/dist/esm/utils/AutoContextBundle.d.ts +1 -1
  172. package/dist/esm/utils/AutoContextBundle.d.ts.map +1 -1
  173. package/dist/esm/utils/AutoContextBundle.js +20 -23
  174. package/dist/esm/utils/AutoContextBundle.js.map +1 -1
  175. package/dist/esm/utils/BulkFileDigest.js +2 -2
  176. package/dist/esm/utils/ClipboardWatcher.js +2 -2
  177. package/dist/esm/utils/ContextCollapser.js +1 -1
  178. package/dist/esm/utils/ConversationCheckpoint.d.ts +13 -0
  179. package/dist/esm/utils/ConversationCheckpoint.d.ts.map +1 -1
  180. package/dist/esm/utils/ConversationCheckpoint.js +57 -0
  181. package/dist/esm/utils/ConversationCheckpoint.js.map +1 -1
  182. package/dist/esm/utils/ExecutiveReport.js +1 -1
  183. package/dist/esm/utils/StructuralSearch.d.ts +1 -1
  184. package/dist/esm/utils/SurgicalASTInsert.js +1 -1
  185. package/dist/esm/utils/SymbolSurgicalContext.js +1 -1
  186. package/dist/esm/utils/TestAutofixInterceptor.d.ts.map +1 -1
  187. package/dist/esm/utils/TestAutofixInterceptor.js +15 -26
  188. package/dist/esm/utils/TestAutofixInterceptor.js.map +1 -1
  189. package/dist/esm/utils/WorkspaceSearch.js +0 -0
  190. package/dist/esm/utils/logger.js +1 -1
  191. package/package.json +9 -10
  192. package/sdks/crewai/README.md +178 -0
  193. package/sdks/crewai/dist/index.d.ts +130 -0
  194. package/sdks/crewai/dist/index.js +308 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Tool surface control.
3
+ *
4
+ * Every tool schema Lemma advertises is injected into the model's system prompt and
5
+ * re-sent on every single request of the session. At 76 tools that is ~11.4k tokens
6
+ * per turn, charged before Lemma has saved anything — and it is charged even for tools
7
+ * the project can never use (Terraform schemas in a repo with no `.tf` files).
8
+ *
9
+ * So the advertised list is narrowed to what this project can actually use. Nothing is
10
+ * removed: every handler stays registered, and `lemma_toolbox` exposes the rest by name
11
+ * on demand — a one-time ~1.2k token catalog instead of ~8k every turn.
12
+ *
13
+ * The list is resolved once at startup and never mutated mid-session. It lives in the
14
+ * cached prefix of every request; changing it (e.g. lazily "unlocking" tools via
15
+ * notifications/tools/list_changed) invalidates that cache and re-charges the whole
16
+ * context at full price, which costs far more than the schemas it would defer.
17
+ */
18
+ export type ToolProfile = "auto" | "core" | "full";
19
+ /**
20
+ * Tools that pay for their schema in an ordinary coding session: reading, searching,
21
+ * patching, AST context, exact-hash caching, and the ledger. Everything else is
22
+ * situational and reachable through `lemma_toolbox`.
23
+ */
24
+ export declare const CORE_TOOL_NAMES: string[];
25
+ /**
26
+ * Code review is a mode the user enters, not a property of the repo — and it cannot be
27
+ * detected at startup. Gating these on `.git` advertised them in every git repo, which is
28
+ * every repo: ~670 tokens per turn for tools most sessions never touch. They stay
29
+ * registered and `lemma_toolbox` finds them by query the moment a review actually starts.
30
+ */
31
+ export declare const REVIEW_TOOL_NAMES: string[];
32
+ export interface ResolvedToolSurface<T extends {
33
+ name: string;
34
+ }> {
35
+ profile: ToolProfile;
36
+ /** Schemas advertised in tools/list. */
37
+ advertised: T[];
38
+ /** Registered and callable, but not advertised — reachable through `lemma_toolbox`. */
39
+ hidden: T[];
40
+ /** Situational groups that matched this project. */
41
+ activeGroups: string[];
42
+ }
43
+ export declare function resolveToolSurface<T extends {
44
+ name: string;
45
+ description?: string;
46
+ }>(allDefinitions: T[], cwd?: string): ResolvedToolSurface<T>;
47
+ /**
48
+ * One-line-per-tool catalog of everything not advertised, so the model can find and
49
+ * invoke a hidden tool without its schema having been resident all session.
50
+ */
51
+ export declare function buildToolboxCatalog(hidden: Array<{
52
+ name: string;
53
+ description?: string;
54
+ }>, query?: string): string;
55
+ //# sourceMappingURL=tool-profiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-profiles.d.ts","sourceRoot":"","sources":["../../../src/mcp/tool-profiles.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,EA0BnC,CAAC;AA4DF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,UAAoE,CAAC;AAkBnG,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;IAC7D,OAAO,EAAE,WAAW,CAAC;IACrB,wCAAwC;IACxC,UAAU,EAAE,CAAC,EAAE,CAAC;IAChB,uFAAuF;IACvF,MAAM,EAAE,CAAC,EAAE,CAAC;IACZ,oDAAoD;IACpD,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EACjF,cAAc,EAAE,CAAC,EAAE,EACnB,GAAG,GAAE,MAAsB,GAC1B,mBAAmB,CAAC,CAAC,CAAC,CAmCxB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EACrD,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CAuDR"}
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.REVIEW_TOOL_NAMES = exports.CORE_TOOL_NAMES = void 0;
7
+ exports.resolveToolSurface = resolveToolSurface;
8
+ exports.buildToolboxCatalog = buildToolboxCatalog;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ /**
12
+ * Tools that pay for their schema in an ordinary coding session: reading, searching,
13
+ * patching, AST context, exact-hash caching, and the ledger. Everything else is
14
+ * situational and reachable through `lemma_toolbox`.
15
+ */
16
+ exports.CORE_TOOL_NAMES = [
17
+ // Workspace read & navigation
18
+ "read_workspace_file",
19
+ "list_workspace_dir",
20
+ "search_workspace",
21
+ "smart_file_slice",
22
+ "run_workspace_command",
23
+ "output_region",
24
+ // Write path — patch, then validate before it lands
25
+ "apply_workspace_patch",
26
+ "write_workspace_file",
27
+ "create_workspace_file",
28
+ "surgical_ast_insert",
29
+ "validate_patch_sandbox",
30
+ // Structure without reading whole files
31
+ "get_ast_hologram",
32
+ "get_symbol_surgical_context",
33
+ "get_project_onboarding",
34
+ // Avoid re-deriving what was already derived
35
+ "state_hash_cache",
36
+ "smarter_cache",
37
+ "search_memory",
38
+ "store_memory",
39
+ // Accounting & routing
40
+ "token_receipt",
41
+ "get_routing_advice",
42
+ ];
43
+ const exists = (cwd, ...rel) => rel.some((r) => {
44
+ try {
45
+ return fs_1.default.existsSync(path_1.default.join(cwd, r));
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ });
51
+ /** Shallow scan for an extension — deep walks at startup would cost more than they save. */
52
+ function hasExtension(cwd, ext, depth = 2) {
53
+ const walk = (dir, left) => {
54
+ let entries;
55
+ try {
56
+ entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
57
+ }
58
+ catch {
59
+ return false;
60
+ }
61
+ for (const entry of entries) {
62
+ if (entry.name.startsWith(".") || entry.name === "node_modules")
63
+ continue;
64
+ if (entry.isFile() && entry.name.endsWith(ext))
65
+ return true;
66
+ if (entry.isDirectory() && left > 0 && walk(path_1.default.join(dir, entry.name), left - 1))
67
+ return true;
68
+ }
69
+ return false;
70
+ };
71
+ return walk(cwd, depth);
72
+ }
73
+ const TOOL_GROUPS = [
74
+ {
75
+ name: "infra",
76
+ tools: [
77
+ "infra_discover", "infra_connect", "infra_status", "infra_health_check",
78
+ "infra_diagnose", "infra_analyze_monolith", "infra_suggest_split",
79
+ "jenkins_generate_pipeline", "jenkins_trigger_build", "jenkins_build_status", "jenkins_logs",
80
+ "okd_generate_manifests", "okd_apply", "okd_status", "okd_logs", "okd_events",
81
+ "okd_describe", "okd_route_url",
82
+ "terraform_plan", "terraform_apply", "terraform_state_list", "terraform_drift",
83
+ ],
84
+ applies: (cwd) =>
85
+ // A provider already connected, or the repo actually contains infra to act on.
86
+ exists(process.env.HOME || "", ".lemma-cache/infra/config.json") ||
87
+ exists(cwd, "Jenkinsfile", "kustomization.yaml", ".openshift", "openshift") ||
88
+ hasExtension(cwd, ".tf", 2),
89
+ },
90
+ ];
91
+ /**
92
+ * Code review is a mode the user enters, not a property of the repo — and it cannot be
93
+ * detected at startup. Gating these on `.git` advertised them in every git repo, which is
94
+ * every repo: ~670 tokens per turn for tools most sessions never touch. They stay
95
+ * registered and `lemma_toolbox` finds them by query the moment a review actually starts.
96
+ */
97
+ exports.REVIEW_TOOL_NAMES = ["review_diff", "review_pr", "pr_status", "generate_pr_workflow"];
98
+ function readConfiguredProfile(cwd) {
99
+ try {
100
+ const configPath = path_1.default.join(cwd, "lemma.config.json");
101
+ if (fs_1.default.existsSync(configPath)) {
102
+ const config = JSON.parse(fs_1.default.readFileSync(configPath, "utf8"));
103
+ const profile = config?.mcp?.toolProfile;
104
+ if (profile === "core" || profile === "full" || profile === "auto")
105
+ return profile;
106
+ }
107
+ }
108
+ catch {
109
+ // Unreadable config must never change which tools work — fall through to the default.
110
+ }
111
+ const env = process.env.LEMMA_TOOL_PROFILE;
112
+ if (env === "core" || env === "full" || env === "auto")
113
+ return env;
114
+ return "auto";
115
+ }
116
+ function resolveToolSurface(allDefinitions, cwd = process.cwd()) {
117
+ const profile = readConfiguredProfile(cwd);
118
+ if (profile === "full") {
119
+ return { profile, advertised: allDefinitions, hidden: [], activeGroups: ["*"] };
120
+ }
121
+ // Always advertised: it is the only route back to a withheld schema, so hiding it
122
+ // would turn a deferred tool into a lost one.
123
+ const allowed = new Set([...exports.CORE_TOOL_NAMES, "lemma_toolbox"]);
124
+ const activeGroups = [];
125
+ if (profile === "auto") {
126
+ for (const group of TOOL_GROUPS) {
127
+ let matched = false;
128
+ try {
129
+ matched = group.applies(cwd);
130
+ }
131
+ catch {
132
+ // Detection must never be the reason a tool disappears; on error, include it.
133
+ matched = true;
134
+ }
135
+ if (matched) {
136
+ activeGroups.push(group.name);
137
+ group.tools.forEach((t) => allowed.add(t));
138
+ }
139
+ }
140
+ }
141
+ const advertised = [];
142
+ const hidden = [];
143
+ for (const def of allDefinitions) {
144
+ (allowed.has(def.name) ? advertised : hidden).push(def);
145
+ }
146
+ return { profile, advertised, hidden, activeGroups };
147
+ }
148
+ /**
149
+ * One-line-per-tool catalog of everything not advertised, so the model can find and
150
+ * invoke a hidden tool without its schema having been resident all session.
151
+ */
152
+ function buildToolboxCatalog(hidden, query) {
153
+ const q = (query || "").trim().toLowerCase();
154
+ const terms = q ? q.split(/\s+/).filter(Boolean) : [];
155
+ let matches = hidden;
156
+ if (terms.length) {
157
+ // Plain substring matching floods the result: "pr" hits scrub_privacy, precrime_static
158
+ // and compress_context, so a 3-word query returns the whole catalog and costs more
159
+ // than the schemas it was meant to replace. Match on word boundaries and rank by how
160
+ // many distinct terms a tool hits, so the tools the user meant come first.
161
+ const scored = hidden
162
+ .map((t) => {
163
+ const haystack = `${t.name} ${t.description || ""}`.toLowerCase();
164
+ const words = new Set(haystack.split(/[^a-z0-9]+/).filter(Boolean));
165
+ let score = 0;
166
+ for (const term of terms) {
167
+ if (words.has(term))
168
+ score += 2; // whole word — the strong signal
169
+ else if (term.length >= 4 && haystack.includes(term))
170
+ score += 1; // prefix/partial
171
+ }
172
+ return { tool: t, score };
173
+ })
174
+ .filter((s) => s.score > 0)
175
+ .sort((a, b) => b.score - a.score);
176
+ // A whole-word hit is a real match; partials only get shown when nothing matched a
177
+ // whole word, so a weak substring never crowds out the tool the user asked for.
178
+ const hasWordHit = scored.some((s) => s.score >= 2);
179
+ matches = scored.filter((s) => (hasWordHit ? s.score >= 2 : true)).map((s) => s.tool);
180
+ }
181
+ const CATALOG_LIMIT = 15;
182
+ const truncated = Math.max(0, matches.length - CATALOG_LIMIT);
183
+ const shown = matches.slice(0, CATALOG_LIMIT);
184
+ if (matches.length === 0) {
185
+ return terms.length
186
+ ? `No hidden Lemma tool matches "${query}". ${hidden.length} tools available; call lemma_toolbox with no query to list all.`
187
+ : "No hidden tools — every Lemma tool is already advertised in this session.";
188
+ }
189
+ const lines = shown.map((t) => {
190
+ // One line per tool: enough to choose, not enough to be a second schema listing.
191
+ const desc = (t.description || "").split(/\.\s|\n/)[0].slice(0, 100);
192
+ return `- ${t.name}: ${desc}`;
193
+ });
194
+ return [
195
+ `${matches.length} Lemma tool(s) available but not advertised (schemas withheld to keep per-turn context small).`,
196
+ `Invoke one with: lemma_toolbox { action: "call", tool: "<name>", args: { ... } }`,
197
+ "",
198
+ ...lines,
199
+ ...(truncated > 0 ? [`… and ${truncated} more — narrow the query to see them.`] : []),
200
+ "",
201
+ `Call lemma_toolbox { action: "schema", tool: "<name>" } for a tool's full input schema before calling it.`,
202
+ ].join("\n");
203
+ }
204
+ //# sourceMappingURL=tool-profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-profiles.js","sourceRoot":"","sources":["../../../src/mcp/tool-profiles.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,18 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
2
  /** Serialized size of every tool schema sent to the client, for cost accounting. */
3
3
  export declare function getToolSchemaChars(): number;
4
+ /** Every tool name with a registered handler, advertised or not. */
5
+ export declare function getRegisteredToolNames(): string[];
6
+ /** Tool names whose schemas this session sends to the client. */
7
+ export declare function getAdvertisedToolNames(): string[];
8
+ /** Which tools this session advertises, and why — surfaced by `token_budget`. */
9
+ export declare function getToolSurfaceSummary(): {
10
+ profile: string;
11
+ advertised: number;
12
+ hidden: number;
13
+ total: number;
14
+ activeGroups: string[];
15
+ };
4
16
  export interface ToolCallEvent {
5
17
  tool: string;
6
18
  args: Record<string, any>;
@@ -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;AA4/BnE,oFAAoF;AACpF,wBAAgB,kBAAkB,IAAI,MAAM,CAM3C;AA6ED,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;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"}
@@ -37,6 +37,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.getToolSchemaChars = getToolSchemaChars;
40
+ exports.getRegisteredToolNames = getRegisteredToolNames;
41
+ exports.getAdvertisedToolNames = getAdvertisedToolNames;
42
+ exports.getToolSurfaceSummary = getToolSurfaceSummary;
40
43
  exports.setupToolsHandlers = setupToolsHandlers;
41
44
  const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
42
45
  const fs_1 = __importDefault(require("fs"));
@@ -71,6 +74,7 @@ const WormholeCompressor_1 = require("../utils/WormholeCompressor");
71
74
  const ExecutiveReport_1 = require("../utils/ExecutiveReport");
72
75
  const SurgicalASTInsert_1 = require("../utils/SurgicalASTInsert");
73
76
  const mcp_tools_1 = require("../infra/mcp-tools");
77
+ const tool_profiles_1 = require("./tool-profiles");
74
78
  const StateHashCache_1 = require("../utils/StateHashCache");
75
79
  const TokenReceipt_1 = require("../utils/TokenReceipt");
76
80
  const CommandOutputDistiller_1 = require("../utils/CommandOutputDistiller");
@@ -102,6 +106,8 @@ const FREE_TOOLS = new Set([
102
106
  "token_budget", "squeeze_prompt", "turbosqueeze",
103
107
  // Utility — just enough to function
104
108
  "read_workspace_file", "scrub_privacy", "summarize_long_text",
109
+ // Discovery of withheld schemas — gating this would hide tools with no way back
110
+ "lemma_toolbox",
105
111
  // Phase 1 zero-cost analysis tools
106
112
  "file_fingerprint", "git_blame_heat", "type_coverage_report", "import_tree_context",
107
113
  // Phase 2 smart reading & dedup tools
@@ -1030,8 +1036,32 @@ const toolDefinitions = [
1030
1036
  ];
1031
1037
  // ── Infrastructure Tool Definitions ──────────────────────────────────────────
1032
1038
  const _infraToolDefs = (0, mcp_tools_1.getInfraToolDefinitions)();
1033
- const allToolDefinitions = [...toolDefinitions, ..._infraToolDefs];
1034
- const toolDefinitionsArray = _infraToolDefs.length > 0 ? allToolDefinitions : toolDefinitions;
1039
+ // `lemma_toolbox` keeps every non-advertised tool reachable. It is defined here rather
1040
+ // than in the main array because it must never itself be hidden.
1041
+ const TOOLBOX_TOOL_DEFINITION = {
1042
+ name: "lemma_toolbox",
1043
+ description: "Discover and invoke Lemma tools whose schemas are not loaded this session. Use action='list' (optionally with a query) to see what exists, 'schema' to read one tool's inputs, and 'call' to run it. Every Lemma capability is reachable here.",
1044
+ inputSchema: {
1045
+ type: "object",
1046
+ properties: {
1047
+ action: { type: "string", enum: ["list", "schema", "call"], description: "list | schema | call", default: "list" },
1048
+ query: { type: "string", description: "Filter terms for action='list' (e.g. 'terraform drift')" },
1049
+ tool: { type: "string", description: "Tool name, for action='schema' or 'call'" },
1050
+ args: { type: "object", description: "Arguments passed through to the tool, for action='call'" },
1051
+ },
1052
+ required: ["action"],
1053
+ },
1054
+ };
1055
+ const allToolDefinitions = [
1056
+ ...toolDefinitions,
1057
+ ..._infraToolDefs,
1058
+ TOOLBOX_TOOL_DEFINITION,
1059
+ ];
1060
+ // Narrow what gets advertised to what this project can actually use. Hidden tools stay
1061
+ // registered in `toolHandlers` and callable — both directly and through `lemma_toolbox`.
1062
+ const _toolSurface = (0, tool_profiles_1.resolveToolSurface)(allToolDefinitions, process.cwd());
1063
+ const toolDefinitionsArray = _toolSurface.advertised;
1064
+ const hiddenToolDefinitions = _toolSurface.hidden;
1035
1065
  /** Serialized size of every tool schema sent to the client, for cost accounting. */
1036
1066
  function getToolSchemaChars() {
1037
1067
  try {
@@ -1041,6 +1071,24 @@ function getToolSchemaChars() {
1041
1071
  return 0;
1042
1072
  }
1043
1073
  }
1074
+ /** Every tool name with a registered handler, advertised or not. */
1075
+ function getRegisteredToolNames() {
1076
+ return Object.keys(toolHandlers);
1077
+ }
1078
+ /** Tool names whose schemas this session sends to the client. */
1079
+ function getAdvertisedToolNames() {
1080
+ return toolDefinitionsArray.map((d) => d.name);
1081
+ }
1082
+ /** Which tools this session advertises, and why — surfaced by `token_budget`. */
1083
+ function getToolSurfaceSummary() {
1084
+ return {
1085
+ profile: _toolSurface.profile,
1086
+ advertised: toolDefinitionsArray.length,
1087
+ hidden: hiddenToolDefinitions.length,
1088
+ total: allToolDefinitions.length,
1089
+ activeGroups: _toolSurface.activeGroups,
1090
+ };
1091
+ }
1044
1092
  const toolHandlers = {
1045
1093
  scrub_privacy: handleScrubPrivacy,
1046
1094
  search_memory: handleSearchMemory,
@@ -1113,8 +1161,44 @@ const toolHandlers = {
1113
1161
  conversation_checkpoint: handleConversationCheckpoint,
1114
1162
  test_autofix_interceptor: handleTestAutofixInterceptor,
1115
1163
  prompt_pattern_cache: handlePromptPatternCache,
1164
+ lemma_toolbox: handleToolbox,
1116
1165
  ...(0, mcp_tools_1.getInfraToolHandlers)(),
1117
1166
  };
1167
+ /**
1168
+ * Reaches the tools whose schemas were withheld from the session listing.
1169
+ *
1170
+ * `call` proxies through this server rather than relying on the client to emit a name it
1171
+ * never saw advertised — some clients validate outgoing tool names against tools/list, so
1172
+ * the proxy is what makes hiding a schema safe rather than a silent capability loss.
1173
+ */
1174
+ async function handleToolbox(args) {
1175
+ const action = args?.action || "list";
1176
+ if (action === "list") {
1177
+ return {
1178
+ content: [{ type: "text", text: (0, tool_profiles_1.buildToolboxCatalog)(hiddenToolDefinitions, args?.query) }],
1179
+ };
1180
+ }
1181
+ const toolName = args?.tool;
1182
+ if (!toolName)
1183
+ throw new Error("`tool` is required for action '" + action + "'");
1184
+ if (action === "schema") {
1185
+ const def = allToolDefinitions.find((d) => d.name === toolName);
1186
+ if (!def)
1187
+ throw new Error(`Unknown tool: ${toolName}`);
1188
+ return { content: [{ type: "text", text: JSON.stringify(def, null, 2) }] };
1189
+ }
1190
+ if (action === "call") {
1191
+ const handler = toolHandlers[toolName];
1192
+ if (!handler)
1193
+ throw new Error(`Unknown tool: ${toolName}`);
1194
+ // The proxy must not become a way around the license gate the direct path enforces.
1195
+ if (!isProUser() && !FREE_TOOLS.has(toolName)) {
1196
+ return { content: [{ type: "text", text: PRO_GATE_MESSAGE }] };
1197
+ }
1198
+ return handler((args?.args || {}));
1199
+ }
1200
+ throw new Error(`Unknown action: ${action}. Use 'list', 'schema', or 'call'.`);
1201
+ }
1118
1202
  function setupToolsHandlers(server, onToolCall) {
1119
1203
  server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
1120
1204
  tools: toolDefinitionsArray,
@@ -1191,7 +1275,7 @@ async function handleSearchMemory(args) {
1191
1275
  throw new Error("Query is required");
1192
1276
  try {
1193
1277
  const brain = (0, TheBrainV2_1.getBrain)();
1194
- const results = brain.search(query, limit);
1278
+ const results = brain.search(query, limit, 0, { projectId: (0, TheBrainV2_1.deriveProjectId)() });
1195
1279
  if (results.length === 0) {
1196
1280
  return { content: [{ type: "text", text: "No relevant memories found in Lemma's Brain." }] };
1197
1281
  }
@@ -1676,30 +1760,88 @@ async function handleSqueezePrompt(args) {
1676
1760
  return { content: [{ type: "text", text: `Error squeezing prompt: ${err.message}` }] };
1677
1761
  }
1678
1762
  }
1763
+ /**
1764
+ * Onboarding built from the filesystem alone.
1765
+ *
1766
+ * The proxy produces a richer map, but this tool is in the core surface and CLAUDE.md tells
1767
+ * the model to call it first — so when the proxy is down, the first call of every session
1768
+ * used to return an error and the session started with no context at all. A local answer is
1769
+ * strictly better than that, and it never fails.
1770
+ */
1771
+ function buildLocalOnboarding(cwd) {
1772
+ const lines = [];
1773
+ const projectName = path_1.default.basename(cwd);
1774
+ lines.push(`# ${projectName}`, "");
1775
+ let pkg = null;
1776
+ try {
1777
+ pkg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(cwd, "package.json"), "utf8"));
1778
+ }
1779
+ catch { /* not a node project */ }
1780
+ if (pkg) {
1781
+ if (pkg.description)
1782
+ lines.push(pkg.description, "");
1783
+ lines.push(`**Version:** ${pkg.version || "unknown"}`);
1784
+ const deps = Object.keys(pkg.dependencies || {});
1785
+ if (deps.length)
1786
+ lines.push(`**Dependencies:** ${deps.slice(0, 12).join(", ")}${deps.length > 12 ? ` (+${deps.length - 12})` : ""}`);
1787
+ const scripts = Object.keys(pkg.scripts || {});
1788
+ if (scripts.length)
1789
+ lines.push(`**Scripts:** ${scripts.slice(0, 12).join(", ")}`);
1790
+ }
1791
+ // Current branch straight from .git/HEAD — no subprocess, works on a detached HEAD too.
1792
+ try {
1793
+ const head = fs_1.default.readFileSync(path_1.default.join(cwd, ".git", "HEAD"), "utf8").trim();
1794
+ const branch = head.startsWith("ref: ") ? head.replace("ref: refs/heads/", "") : `detached @ ${head.slice(0, 8)}`;
1795
+ lines.push(`**Branch:** ${branch}`);
1796
+ }
1797
+ catch { /* not a git repo */ }
1798
+ try {
1799
+ const entries = fs_1.default
1800
+ .readdirSync(cwd, { withFileTypes: true })
1801
+ .filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules")
1802
+ .map((e) => e.name);
1803
+ if (entries.length)
1804
+ lines.push(`**Top-level directories:** ${entries.slice(0, 15).join(", ")}`);
1805
+ }
1806
+ catch { /* unreadable cwd */ }
1807
+ try {
1808
+ const memories = (0, TheBrainV2_1.getBrain)().getEntriesForProject((0, TheBrainV2_1.deriveProjectId)(cwd));
1809
+ lines.push("", `**Brain memories for this project:** ${memories.length}`);
1810
+ if (memories.length) {
1811
+ const recent = memories
1812
+ .slice()
1813
+ .sort((a, b) => (a.timestamp < b.timestamp ? 1 : -1))
1814
+ .slice(0, 5);
1815
+ lines.push("", "Most recent:");
1816
+ for (const m of recent)
1817
+ lines.push(`- ${m.query.slice(0, 100)}`);
1818
+ lines.push("", "Use `search_memory` to retrieve any of these in full.");
1819
+ }
1820
+ }
1821
+ catch { /* brain unavailable */ }
1822
+ return lines.join("\n");
1823
+ }
1679
1824
  async function handleGetProjectOnboarding(_args) {
1825
+ const cwd = process.cwd();
1680
1826
  try {
1681
1827
  const port = (0, utils_1.getProxyPort)();
1682
- const response = await axios_1.default.get(`http://localhost:${port}/api/project/onboarding`);
1683
- return {
1684
- content: [
1685
- {
1686
- type: "text",
1687
- text: response.data.markdown || "# Onboarding mental model could not be generated.",
1688
- },
1689
- ],
1690
- };
1828
+ const response = await axios_1.default.get(`http://localhost:${port}/api/project/onboarding`, { timeout: 2000 });
1829
+ if (response.data?.markdown) {
1830
+ return { content: [{ type: "text", text: response.data.markdown }] };
1831
+ }
1691
1832
  }
1692
1833
  catch (error) {
1693
- (0, utils_1.logError)("get_project_onboarding", error);
1694
- return {
1695
- content: [
1696
- {
1697
- type: "text",
1698
- text: `Failed to fetch codebase onboarding mental model: ${error.message}. Is Lemma proxy running?`,
1699
- },
1700
- ],
1701
- };
1834
+ // Proxy down or slow — fall through. Not worth surfacing as an error to the model.
1835
+ (0, utils_1.logError)("get_project_onboarding (falling back to local)", error);
1702
1836
  }
1837
+ return {
1838
+ content: [
1839
+ {
1840
+ type: "text",
1841
+ text: `${buildLocalOnboarding(cwd)}\n\n---\n*Generated locally. Start the Lemma proxy (\`lemma start\`) for the full indexed architecture map.*`,
1842
+ },
1843
+ ],
1844
+ };
1703
1845
  }
1704
1846
  function extractSymbolsWithTsCompiler(filePath, relPath) {
1705
1847
  try {
@@ -1927,7 +2069,7 @@ async function handleQueryHybridConsensus(args) {
1927
2069
  const fullQuery = context ? `${query}\n\nContext: ${context}` : query;
1928
2070
  try {
1929
2071
  const brain = (0, TheBrainV2_1.getBrain)();
1930
- const results = brain.search(fullQuery, 3);
2072
+ const results = brain.search(fullQuery, 3, 0, { projectId: (0, TheBrainV2_1.deriveProjectId)() });
1931
2073
  const topHit = results[0];
1932
2074
  if (topHit && topHit.similarity >= threshold) {
1933
2075
  const savedTokens = Math.floor((topHit.response?.length || 500) / 4);
@@ -1985,7 +2127,7 @@ async function handleGetTelepathicHints(args) {
1985
2127
  const telepathicQuery = `${basename} ${dirContext} ${ext} patterns solutions architecture`;
1986
2128
  try {
1987
2129
  const brain = (0, TheBrainV2_1.getBrain)();
1988
- const results = brain.search(telepathicQuery, limit);
2130
+ const results = brain.search(telepathicQuery, limit, 0, { projectId: (0, TheBrainV2_1.deriveProjectId)() });
1989
2131
  if (results.length === 0) {
1990
2132
  return {
1991
2133
  content: [
@@ -2231,7 +2373,7 @@ async function handleSmarterCache(args) {
2231
2373
  const fullQuery = context ? `${query}\n\nContext: ${context}` : query;
2232
2374
  try {
2233
2375
  const brain = (0, TheBrainV2_1.getBrain)();
2234
- const results = brain.search(fullQuery, 5);
2376
+ const results = brain.search(fullQuery, 5, 0, { projectId: (0, TheBrainV2_1.deriveProjectId)() });
2235
2377
  const topHit = results[0];
2236
2378
  if (topHit && topHit.similarity >= threshold) {
2237
2379
  const responseText = topHit.response;
@@ -2384,6 +2526,12 @@ async function handleTokenBudget(_args) {
2384
2526
  astNavigation: true,
2385
2527
  privacyScrubbing: true,
2386
2528
  },
2529
+ // The fixed cost of being connected, re-sent on every request of the session.
2530
+ perTurnOverhead: {
2531
+ ...getToolSurfaceSummary(),
2532
+ schemaTokens: Math.round(getToolSchemaChars() / 4),
2533
+ note: "Hidden tools stay callable via lemma_toolbox. Set mcp.toolProfile='full' in lemma.config.json to advertise all of them.",
2534
+ },
2387
2535
  recommendations: toolCallCount === 0
2388
2536
  ? ["No se han usado tools de Lemma todavía. Asegúrate de que el agente esté usando el prompt lemma-turbomode."]
2389
2537
  : ["Sigue usando turbosqueeze antes de cada code block grande.", "Usa smarter_cache antes de razonar."],
@@ -2997,7 +3145,8 @@ async function handleSemanticDedupGuard(args) {
2997
3145
  throw new Error("query and response are required");
2998
3146
  try {
2999
3147
  const brain = (0, TheBrainV2_1.getBrain)();
3000
- const results = brain.search(query, 3);
3148
+ // A dedup check is not a question the user asked — it must not register as a cache hit.
3149
+ const results = brain.search(query, 3, 0, { countStats: false, projectId: (0, TheBrainV2_1.deriveProjectId)() });
3001
3150
  const topHit = results[0];
3002
3151
  if (!topHit) {
3003
3152
  return {
@@ -4103,7 +4252,7 @@ async function handleLocalSemanticAutofix(args) {
4103
4252
  throw new Error("errorMessage is required");
4104
4253
  try {
4105
4254
  const brain = (0, TheBrainV2_1.getBrain)();
4106
- const results = brain.search(errorMessage, 3, 0.70);
4255
+ const results = brain.search(errorMessage, 3, 0.70, { projectId: (0, TheBrainV2_1.deriveProjectId)() });
4107
4256
  const topHit = results[0];
4108
4257
  if (topHit) {
4109
4258
  const responseText = topHit.response;