@nxuss/lemma 1.7.0 → 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.
- package/dist/cjs/mcp/tools.d.ts.map +1 -1
- package/dist/cjs/mcp/tools.js +115 -15
- package/dist/cjs/mcp/tools.js.map +1 -1
- package/dist/cjs/proxy/ComplexityRouter.d.ts +7 -0
- package/dist/cjs/proxy/ComplexityRouter.d.ts.map +1 -1
- package/dist/cjs/proxy/ComplexityRouter.js +18 -5
- package/dist/cjs/proxy/ComplexityRouter.js.map +1 -1
- package/dist/cjs/utils/TokenReceipt.d.ts +6 -0
- package/dist/cjs/utils/TokenReceipt.d.ts.map +1 -1
- package/dist/cjs/utils/TokenReceipt.js +9 -0
- package/dist/cjs/utils/TokenReceipt.js.map +1 -1
- package/dist/esm/api/dashboardRoutes.d.ts +3 -3
- package/dist/esm/api/dashboardRoutes.js +1 -1
- package/dist/esm/api/server.d.ts +4 -4
- package/dist/esm/api/server.js +3 -3
- package/dist/esm/autopilot/AutopilotWatcher.js +3 -3
- package/dist/esm/autopilot/Notifier.d.ts +1 -1
- package/dist/esm/cli/lemma-proxy.js +14 -14
- package/dist/esm/cli/stack.d.ts +4 -4
- package/dist/esm/cli/stack.js +5 -5
- package/dist/esm/cloud/KeyManager.d.ts +1 -1
- package/dist/esm/cloud/KeyManager.js +1 -1
- package/dist/esm/cloud/TenantCache.d.ts +1 -1
- package/dist/esm/cloud/TenantCache.js +1 -1
- package/dist/esm/cloud/index.d.ts +3 -3
- package/dist/esm/cloud/index.js +3 -3
- package/dist/esm/cloud/server.d.ts +3 -3
- package/dist/esm/cloud/server.js +5 -5
- package/dist/esm/config/index.d.ts +1 -1
- package/dist/esm/config/index.js +1 -1
- package/dist/esm/consensus/index.d.ts +2 -2
- package/dist/esm/consensus/index.js +2 -2
- package/dist/esm/core/AgentRegistry.d.ts +1 -1
- package/dist/esm/core/AgentRegistry.js +1 -1
- package/dist/esm/core/DashboardBroadcaster.d.ts +3 -3
- package/dist/esm/core/DashboardBroadcaster.js +2 -2
- package/dist/esm/core/DashboardWebSocketServer.d.ts +1 -1
- package/dist/esm/core/DashboardWebSocketServer.js +1 -1
- package/dist/esm/core/GhostAlchemist.js +1 -1
- package/dist/esm/core/GhostListener.js +2 -2
- package/dist/esm/core/OrchestrationEngine.d.ts +3 -3
- package/dist/esm/core/OrchestrationEngine.js +2 -2
- package/dist/esm/core/SubconsciousEngine.d.ts +1 -1
- package/dist/esm/core/SubconsciousEngine.js +2 -2
- package/dist/esm/core/WebSocketServer.d.ts +2 -2
- package/dist/esm/core/WebSocketServer.js +1 -1
- package/dist/esm/core/index.d.ts +6 -6
- package/dist/esm/core/index.js +6 -6
- package/dist/esm/core/router.d.ts +3 -3
- package/dist/esm/core/router.js +1 -1
- package/dist/esm/db/database.js +1 -1
- package/dist/esm/embed.d.ts +2 -2
- package/dist/esm/embed.js +2 -2
- package/dist/esm/examples/basic-usage.js +1 -1
- package/dist/esm/index.d.ts +14 -14
- package/dist/esm/index.js +14 -14
- package/dist/esm/infra/analyzers/DriftDetector.d.ts +1 -1
- package/dist/esm/infra/analyzers/InfraAnalyzer.d.ts +1 -1
- package/dist/esm/infra/analyzers/MonolithAnalyzer.d.ts +1 -1
- package/dist/esm/infra/analyzers/index.d.ts +3 -3
- package/dist/esm/infra/analyzers/index.js +3 -3
- package/dist/esm/infra/cli.js +2 -2
- package/dist/esm/infra/config.d.ts +1 -1
- package/dist/esm/infra/index.d.ts +5 -5
- package/dist/esm/infra/index.js +5 -5
- package/dist/esm/infra/mcp-tools.js +5 -5
- package/dist/esm/infra/providers/InfraProvider.d.ts +1 -1
- package/dist/esm/infra/providers/JenkinsProvider.d.ts +2 -2
- package/dist/esm/infra/providers/OKDProvider.d.ts +2 -2
- package/dist/esm/infra/providers/TerraformProvider.d.ts +2 -2
- package/dist/esm/infra/providers/index.d.ts +5 -5
- package/dist/esm/infra/providers/index.js +3 -3
- package/dist/esm/mcp/index.js +5 -5
- package/dist/esm/mcp/resources.js +2 -2
- package/dist/esm/mcp/tools.d.ts.map +1 -1
- package/dist/esm/mcp/tools.js +153 -53
- package/dist/esm/mcp/tools.js.map +1 -1
- package/dist/esm/observability/IdeContextSync.d.ts +1 -1
- package/dist/esm/observability/IdeContextSync.js +5 -5
- package/dist/esm/pr-review/PRReviewEngine.d.ts +1 -1
- package/dist/esm/pr-review/PRReviewEngine.js +4 -4
- package/dist/esm/pr-review/ReviewStore.d.ts +1 -1
- package/dist/esm/pr-review/adapters/AzureDevOpsAdapter.d.ts +2 -2
- package/dist/esm/pr-review/adapters/AzureDevOpsAdapter.js +1 -1
- package/dist/esm/pr-review/adapters/GitHubAdapter.d.ts +2 -2
- package/dist/esm/pr-review/adapters/GitHubAdapter.js +1 -1
- package/dist/esm/pr-review/adapters/GitLabAdapter.d.ts +2 -2
- package/dist/esm/pr-review/adapters/GitLabAdapter.js +1 -1
- package/dist/esm/pr-review/adapters/GitPlatformAdapter.d.ts +1 -1
- package/dist/esm/pr-review/adapters/index.d.ts +4 -4
- package/dist/esm/pr-review/adapters/index.js +3 -3
- package/dist/esm/pr-review/analyzers/QualityGrader.d.ts +1 -1
- package/dist/esm/pr-review/analyzers/RegressionDetector.d.ts +1 -1
- package/dist/esm/pr-review/analyzers/SecurityScanner.d.ts +1 -1
- package/dist/esm/pr-review/analyzers/StaticAnalyzer.d.ts +1 -1
- package/dist/esm/pr-review/analyzers/index.d.ts +8 -8
- package/dist/esm/pr-review/analyzers/index.js +4 -4
- package/dist/esm/pr-review/bridge/BrainBridge.d.ts +1 -1
- package/dist/esm/pr-review/bridge/BrainBridge.js +1 -1
- package/dist/esm/pr-review/bridge/index.d.ts +1 -1
- package/dist/esm/pr-review/bridge/index.js +1 -1
- package/dist/esm/pr-review/index.d.ts +4 -4
- package/dist/esm/pr-review/index.js +2 -2
- package/dist/esm/pr-review/reviewRunner.d.ts +2 -2
- package/dist/esm/pr-review/reviewRunner.js +6 -6
- package/dist/esm/pr-review/webhook/WebhookServer.js +1 -1
- package/dist/esm/pr-review/webhook/index.d.ts +1 -1
- package/dist/esm/pr-review/webhook/index.js +1 -1
- package/dist/esm/protocol/flows.d.ts +1 -1
- package/dist/esm/protocol/flows.js +1 -1
- package/dist/esm/protocol/index.d.ts +4 -4
- package/dist/esm/protocol/index.js +4 -4
- package/dist/esm/protocol/utils.d.ts +1 -1
- package/dist/esm/protocol/utils.js +2 -2
- package/dist/esm/protocol/validators.d.ts +1 -1
- package/dist/esm/protocol/validators.js +1 -1
- package/dist/esm/proxy/ComplexityRouter.d.ts +7 -0
- package/dist/esm/proxy/ComplexityRouter.d.ts.map +1 -1
- package/dist/esm/proxy/ComplexityRouter.js +18 -5
- package/dist/esm/proxy/ComplexityRouter.js.map +1 -1
- package/dist/esm/proxy/ContextInjector.d.ts +1 -1
- package/dist/esm/proxy/Gatekeeper.d.ts +1 -1
- package/dist/esm/security/SecurityMiddleware.d.ts +1 -1
- package/dist/esm/security/SecurityMiddleware.js +3 -3
- package/dist/esm/security/index.d.ts +4 -4
- package/dist/esm/security/index.js +4 -4
- package/dist/esm/speculative/SpeculativeCache.d.ts +2 -2
- package/dist/esm/speculative/SpeculativeEngine.d.ts +2 -2
- package/dist/esm/speculative/SpeculativeEngine.js +3 -3
- package/dist/esm/speculative/WorkerPool.d.ts +1 -1
- package/dist/esm/speculative/index.d.ts +4 -4
- package/dist/esm/speculative/index.js +4 -4
- package/dist/esm/subconscious/SemanticCache.d.ts +2 -2
- package/dist/esm/subconscious/SemanticCache.js +2 -2
- package/dist/esm/subconscious/SubconsciousEngine.d.ts +1 -1
- package/dist/esm/subconscious/SubconsciousEngine.js +3 -3
- package/dist/esm/subconscious/index.d.ts +4 -4
- package/dist/esm/subconscious/index.js +4 -4
- package/dist/esm/utils/AutoContextBundle.d.ts +1 -1
- package/dist/esm/utils/AutoContextBundle.js +4 -4
- package/dist/esm/utils/BulkFileDigest.js +2 -2
- package/dist/esm/utils/ClipboardWatcher.js +2 -2
- package/dist/esm/utils/ContextCollapser.js +1 -1
- package/dist/esm/utils/ExecutiveReport.js +1 -1
- package/dist/esm/utils/StructuralSearch.d.ts +1 -1
- package/dist/esm/utils/SurgicalASTInsert.js +1 -1
- package/dist/esm/utils/SymbolSurgicalContext.js +1 -1
- package/dist/esm/utils/TestAutofixInterceptor.js +1 -1
- package/dist/esm/utils/TokenReceipt.d.ts +6 -0
- package/dist/esm/utils/TokenReceipt.d.ts.map +1 -1
- package/dist/esm/utils/TokenReceipt.js +8 -0
- package/dist/esm/utils/TokenReceipt.js.map +1 -1
- package/dist/esm/utils/WorkspaceSearch.js +0 -0
- package/dist/esm/utils/logger.js +1 -1
- package/package.json +6 -9
- package/sdks/crewai/README.md +178 -0
- package/sdks/crewai/dist/index.d.ts +130 -0
- package/sdks/crewai/dist/index.js +308 -0
package/dist/esm/mcp/tools.js
CHANGED
|
@@ -5,40 +5,40 @@ import { execSync, spawnSync } from "child_process";
|
|
|
5
5
|
import os from "os";
|
|
6
6
|
import axios from "axios";
|
|
7
7
|
import * as ts from "typescript";
|
|
8
|
-
import { SemanticScrubber } from "../security/SemanticScrubber";
|
|
9
|
-
import { ComplexityRouter } from "../proxy/ComplexityRouter";
|
|
10
|
-
import { squeezeCode, squeezePrompt, pruneHistoryMessages, generateAstDiff } from "../utils/ContextSqueezer";
|
|
11
|
-
import { reportSavings, reportCost } from "../utils/reportSavings";
|
|
12
|
-
import { getProxyPort, safeResolvePath, logError, logWarn, estimateTokensFromResult, isCommandAllowed, loadAllowedCommands } from "./utils";
|
|
13
|
-
import { computeFingerprint } from "../utils/FileFingerprint";
|
|
14
|
-
import { analyzeBlameHeat } from "../utils/GitBlameHeat";
|
|
15
|
-
import { analyzeTypeCoverage } from "../utils/TypeCoverage";
|
|
16
|
-
import { buildImportTree } from "../utils/ImportTreeContext";
|
|
17
|
-
import { readTokenBudgeted } from "../utils/TokenBudgetedReader";
|
|
18
|
-
import { bulkFileDigest } from "../utils/BulkFileDigest";
|
|
19
|
-
import { autoContextBundle } from "../utils/AutoContextBundle";
|
|
20
|
-
import { checkRepeat, recordOutput } from "../utils/RepeatGuard";
|
|
21
|
-
import { saveCheckpoint, loadLatestCheckpoint, buildCheckpointSummary } from "../utils/ConversationCheckpoint";
|
|
22
|
-
import { runTestAutofix } from "../utils/TestAutofixInterceptor";
|
|
23
|
-
import { findPattern, storePattern, getPatternStats } from "../utils/PromptPatternCache";
|
|
24
|
-
import { runAnalysis, parseDiff } from "../pr-review/PRReviewEngine";
|
|
25
|
-
import { ReviewStore } from "../pr-review/ReviewStore";
|
|
26
|
-
import { runPRReview } from "../pr-review/reviewRunner";
|
|
27
|
-
import { getBrain, deriveProjectId } from "../subconscious/TheBrainV2";
|
|
28
|
-
import { collapseContext } from "../utils/ContextCollapser";
|
|
29
|
-
import { getSymbolSurgicalContext } from "../utils/SymbolSurgicalContext";
|
|
30
|
-
import { compressWormhole } from "../utils/WormholeCompressor";
|
|
31
|
-
import { generateExecutiveReport } from "../utils/ExecutiveReport";
|
|
32
|
-
import { surgicalASTInsert } from "../utils/SurgicalASTInsert";
|
|
33
|
-
import { getInfraToolDefinitions, getInfraToolHandlers, INFRA_TOOL_NAMES } from "../infra/mcp-tools";
|
|
34
|
-
import { resolveToolSurface, buildToolboxCatalog } from "./tool-profiles";
|
|
35
|
-
import { lookupStateHash, storeStateHash } from "../utils/StateHashCache";
|
|
36
|
-
import { recordReceiptEvent, getReceiptSummary } from "../utils/TokenReceipt";
|
|
37
|
-
import { distillCommandOutput, buildDistillFooter, readRegion } from "../utils/CommandOutputDistiller";
|
|
38
|
-
import { findMatch, reindentReplacement } from "../utils/PatchMatcher";
|
|
39
|
-
import { searchWorkspace, groupSearchResults, parseExtensionFilter } from "../utils/WorkspaceSearch";
|
|
40
|
-
import { checkAlreadySent, recordSent } from "../utils/ReadWorkspaceCache";
|
|
41
|
-
import { getCachedSearch, setCachedSearch } from "../utils/SearchCache";
|
|
8
|
+
import { SemanticScrubber } from "../security/SemanticScrubber.js";
|
|
9
|
+
import { ComplexityRouter } from "../proxy/ComplexityRouter.js";
|
|
10
|
+
import { squeezeCode, squeezePrompt, pruneHistoryMessages, generateAstDiff } from "../utils/ContextSqueezer.js";
|
|
11
|
+
import { reportSavings, reportCost } from "../utils/reportSavings.js";
|
|
12
|
+
import { getProxyPort, safeResolvePath, logError, logWarn, estimateTokensFromResult, isCommandAllowed, loadAllowedCommands } from "./utils.js";
|
|
13
|
+
import { computeFingerprint } from "../utils/FileFingerprint.js";
|
|
14
|
+
import { analyzeBlameHeat } from "../utils/GitBlameHeat.js";
|
|
15
|
+
import { analyzeTypeCoverage } from "../utils/TypeCoverage.js";
|
|
16
|
+
import { buildImportTree } from "../utils/ImportTreeContext.js";
|
|
17
|
+
import { readTokenBudgeted } from "../utils/TokenBudgetedReader.js";
|
|
18
|
+
import { bulkFileDigest } from "../utils/BulkFileDigest.js";
|
|
19
|
+
import { autoContextBundle } from "../utils/AutoContextBundle.js";
|
|
20
|
+
import { checkRepeat, recordOutput } from "../utils/RepeatGuard.js";
|
|
21
|
+
import { saveCheckpoint, loadLatestCheckpoint, buildCheckpointSummary } from "../utils/ConversationCheckpoint.js";
|
|
22
|
+
import { runTestAutofix } from "../utils/TestAutofixInterceptor.js";
|
|
23
|
+
import { findPattern, storePattern, getPatternStats } from "../utils/PromptPatternCache.js";
|
|
24
|
+
import { runAnalysis, parseDiff } from "../pr-review/PRReviewEngine.js";
|
|
25
|
+
import { ReviewStore } from "../pr-review/ReviewStore.js";
|
|
26
|
+
import { runPRReview } from "../pr-review/reviewRunner.js";
|
|
27
|
+
import { getBrain, deriveProjectId } from "../subconscious/TheBrainV2.js";
|
|
28
|
+
import { collapseContext } from "../utils/ContextCollapser.js";
|
|
29
|
+
import { getSymbolSurgicalContext } from "../utils/SymbolSurgicalContext.js";
|
|
30
|
+
import { compressWormhole } from "../utils/WormholeCompressor.js";
|
|
31
|
+
import { generateExecutiveReport } from "../utils/ExecutiveReport.js";
|
|
32
|
+
import { surgicalASTInsert } from "../utils/SurgicalASTInsert.js";
|
|
33
|
+
import { getInfraToolDefinitions, getInfraToolHandlers, INFRA_TOOL_NAMES } from "../infra/mcp-tools.js";
|
|
34
|
+
import { resolveToolSurface, buildToolboxCatalog } from "./tool-profiles.js";
|
|
35
|
+
import { lookupStateHash, storeStateHash } from "../utils/StateHashCache.js";
|
|
36
|
+
import { recordReceiptEvent, getReceiptSummary, getLedgerLength } from "../utils/TokenReceipt.js";
|
|
37
|
+
import { distillCommandOutput, buildDistillFooter, readRegion } from "../utils/CommandOutputDistiller.js";
|
|
38
|
+
import { findMatch, reindentReplacement } from "../utils/PatchMatcher.js";
|
|
39
|
+
import { searchWorkspace, groupSearchResults, parseExtensionFilter } from "../utils/WorkspaceSearch.js";
|
|
40
|
+
import { checkAlreadySent, recordSent } from "../utils/ReadWorkspaceCache.js";
|
|
41
|
+
import { getCachedSearch, setCachedSearch } from "../utils/SearchCache.js";
|
|
42
42
|
// ── Pro License Gate ──────────────────────────────────────────────────────────
|
|
43
43
|
const LICENSE_FILE = path.join(os.homedir(), '.lemma-cache', 'license.json');
|
|
44
44
|
let _proStatusCache = null;
|
|
@@ -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,
|
|
1294
|
+
const results = brain.search(query, limit, minSimilarity, { projectId: deriveProjectId() });
|
|
1236
1295
|
if (results.length === 0) {
|
|
1237
|
-
|
|
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
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
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}` }] };
|
|
@@ -1314,7 +1394,7 @@ async function handleGetRoutingAdvice(args) {
|
|
|
1314
1394
|
async function handleAutoHeal(args) {
|
|
1315
1395
|
const apply = !!args?.apply;
|
|
1316
1396
|
try {
|
|
1317
|
-
const { performAutoHeal: heal } = await import("../cli/lemma-proxy");
|
|
1397
|
+
const { performAutoHeal: heal } = await import("../cli/lemma-proxy.js");
|
|
1318
1398
|
const res = await heal(apply);
|
|
1319
1399
|
return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
|
|
1320
1400
|
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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) }] };
|
|
@@ -2264,7 +2364,7 @@ async function handleTurboSqueeze(args) {
|
|
|
2264
2364
|
const ratio = text.length > 0 ? ((1 - squeezed.length / text.length) * 100).toFixed(1) : "0";
|
|
2265
2365
|
const saved = Math.max(0, text.length - squeezed.length);
|
|
2266
2366
|
try {
|
|
2267
|
-
const { reportSavings } = await import("../utils/reportSavings");
|
|
2367
|
+
const { reportSavings } = await import("../utils/reportSavings.js");
|
|
2268
2368
|
reportSavings({ source: "contextSqueeze", charsBefore: text.length, charsAfter: squeezed.length, toolName: "turbosqueeze" });
|
|
2269
2369
|
}
|
|
2270
2370
|
catch { }
|
|
@@ -2309,7 +2409,7 @@ async function handleCompressContext(args) {
|
|
|
2309
2409
|
...recent,
|
|
2310
2410
|
];
|
|
2311
2411
|
try {
|
|
2312
|
-
const { reportSavings } = await import("../utils/reportSavings");
|
|
2412
|
+
const { reportSavings } = await import("../utils/reportSavings.js");
|
|
2313
2413
|
reportSavings({ source: "historyPrune", tokens: tokensSaved, toolName: "compress_context" });
|
|
2314
2414
|
}
|
|
2315
2415
|
catch { }
|
|
@@ -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 :
|
|
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;
|
|
@@ -2336,7 +2436,7 @@ async function handleSmarterCache(args) {
|
|
|
2336
2436
|
const responseText = topHit.response;
|
|
2337
2437
|
const tokensSaved = Math.floor(responseText.length / 4);
|
|
2338
2438
|
try {
|
|
2339
|
-
const { reportSavings } = await import("../utils/reportSavings");
|
|
2439
|
+
const { reportSavings } = await import("../utils/reportSavings.js");
|
|
2340
2440
|
reportSavings({ source: "cache", tokens: tokensSaved, toolName: "smarter_cache", query: query.substring(0, 100) });
|
|
2341
2441
|
}
|
|
2342
2442
|
catch { }
|