@usewhisper/mcp-server 2.10.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server.js +721 -144
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -3914,6 +3914,21 @@ function classifyWorkspaceHealth(args) {
|
|
|
3914
3914
|
if (ageHours > (args.max_staleness_hours ?? 168)) return "stale";
|
|
3915
3915
|
return "healthy";
|
|
3916
3916
|
}
|
|
3917
|
+
function resolveWorkspaceIdentity(args) {
|
|
3918
|
+
const rootPath = canonicalizeWorkspacePath(args?.path, args?.cwd);
|
|
3919
|
+
const derivedWorkspaceId = getWorkspaceIdForPath(rootPath);
|
|
3920
|
+
const requestedWorkspaceId = args?.workspace_id?.trim();
|
|
3921
|
+
if (requestedWorkspaceId && requestedWorkspaceId !== derivedWorkspaceId) {
|
|
3922
|
+
throw new Error(
|
|
3923
|
+
`workspace_id '${requestedWorkspaceId}' does not match canonical workspace '${derivedWorkspaceId}' for ${rootPath}.`
|
|
3924
|
+
);
|
|
3925
|
+
}
|
|
3926
|
+
return {
|
|
3927
|
+
workspace_id: derivedWorkspaceId,
|
|
3928
|
+
root_path: rootPath,
|
|
3929
|
+
identity_source: args?.path?.trim() ? "path_canonical" : "cwd_canonical"
|
|
3930
|
+
};
|
|
3931
|
+
}
|
|
3917
3932
|
function getWorkspaceId(workspaceId) {
|
|
3918
3933
|
if (workspaceId?.trim()) return workspaceId.trim();
|
|
3919
3934
|
const seed = `${canonicalizeWorkspacePath(process.cwd())}|${API_KEY.slice(0, 12) || "anon"}`;
|
|
@@ -3925,6 +3940,134 @@ function getWorkspaceIdForPath(path, workspaceId) {
|
|
|
3925
3940
|
const seed = `${canonicalizeWorkspacePath(path)}|${API_KEY.slice(0, 12) || "anon"}`;
|
|
3926
3941
|
return createHash("sha256").update(seed).digest("hex").slice(0, 20);
|
|
3927
3942
|
}
|
|
3943
|
+
function normalizeLoosePath(value) {
|
|
3944
|
+
if (!value || !String(value).trim()) return null;
|
|
3945
|
+
return canonicalizeWorkspacePath(String(value));
|
|
3946
|
+
}
|
|
3947
|
+
function normalizeRepoName(value) {
|
|
3948
|
+
if (!value || !String(value).trim()) return null;
|
|
3949
|
+
return String(value).trim().replace(/\.git$/i, "").toLowerCase();
|
|
3950
|
+
}
|
|
3951
|
+
function parseGitHubRemote(remote) {
|
|
3952
|
+
if (!remote) return null;
|
|
3953
|
+
const normalized = remote.trim().replace(/\.git$/i, "");
|
|
3954
|
+
const httpsMatch = normalized.match(/github\.com[/:]([^/:\s]+)\/([^/\s]+)$/i);
|
|
3955
|
+
if (httpsMatch) {
|
|
3956
|
+
return { owner: httpsMatch[1].toLowerCase(), repo: httpsMatch[2].toLowerCase() };
|
|
3957
|
+
}
|
|
3958
|
+
const sshMatch = normalized.match(/git@github\.com:([^/:\s]+)\/([^/\s]+)$/i);
|
|
3959
|
+
if (sshMatch) {
|
|
3960
|
+
return { owner: sshMatch[1].toLowerCase(), repo: sshMatch[2].toLowerCase() };
|
|
3961
|
+
}
|
|
3962
|
+
return null;
|
|
3963
|
+
}
|
|
3964
|
+
function getGitRemoteUrl(searchPath) {
|
|
3965
|
+
const root = searchPath || process.cwd();
|
|
3966
|
+
const result = spawnSync("git", ["-C", root, "config", "--get", "remote.origin.url"], { encoding: "utf-8" });
|
|
3967
|
+
if (result.status !== 0) return void 0;
|
|
3968
|
+
const out = String(result.stdout || "").trim();
|
|
3969
|
+
return out || void 0;
|
|
3970
|
+
}
|
|
3971
|
+
function getGitBranch(searchPath) {
|
|
3972
|
+
const root = searchPath || process.cwd();
|
|
3973
|
+
const result = spawnSync("git", ["-C", root, "rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf-8" });
|
|
3974
|
+
if (result.status !== 0) return void 0;
|
|
3975
|
+
const out = String(result.stdout || "").trim();
|
|
3976
|
+
return out || void 0;
|
|
3977
|
+
}
|
|
3978
|
+
function sourceStatusLooksReady(status) {
|
|
3979
|
+
const normalized = String(status || "").trim().toLowerCase();
|
|
3980
|
+
if (!normalized) return true;
|
|
3981
|
+
if (["ready", "indexed", "completed", "complete", "active", "synced", "success"].includes(normalized)) return true;
|
|
3982
|
+
if (["processing", "queued", "syncing", "pending", "creating", "indexing", "error", "failed"].includes(normalized)) {
|
|
3983
|
+
return false;
|
|
3984
|
+
}
|
|
3985
|
+
return !normalized.includes("error") && !normalized.includes("fail") && !normalized.includes("queue");
|
|
3986
|
+
}
|
|
3987
|
+
function sourceMatchesWorkspacePath(source, rootPath) {
|
|
3988
|
+
const config = source.config || {};
|
|
3989
|
+
const candidates = [
|
|
3990
|
+
config.path,
|
|
3991
|
+
config.root_path,
|
|
3992
|
+
config.workspace_path,
|
|
3993
|
+
config.workspacePath,
|
|
3994
|
+
config.local_path,
|
|
3995
|
+
config.file_path,
|
|
3996
|
+
config.directory
|
|
3997
|
+
].map((value) => normalizeLoosePath(typeof value === "string" ? value : null)).filter(Boolean);
|
|
3998
|
+
return candidates.some((candidate) => candidate === rootPath || rootPath.startsWith(`${candidate}/`) || candidate.startsWith(`${rootPath}/`));
|
|
3999
|
+
}
|
|
4000
|
+
function sourceMatchesWorkspaceRepo(source, repoIdentity, branch) {
|
|
4001
|
+
if (!repoIdentity) return false;
|
|
4002
|
+
const config = source.config || {};
|
|
4003
|
+
const owner = normalizeRepoName(config.owner || config.org || config.organization || null);
|
|
4004
|
+
const repo = normalizeRepoName(config.repo || config.repository || config.name || null);
|
|
4005
|
+
const sourceUrlRepo = parseGitHubRemote(typeof config.url === "string" ? config.url : null);
|
|
4006
|
+
const branchValue = normalizeRepoName(config.branch || config.ref || config.default_branch || null);
|
|
4007
|
+
const ownerMatches = owner === repoIdentity.owner || sourceUrlRepo?.owner === repoIdentity.owner;
|
|
4008
|
+
const repoMatches = repo === repoIdentity.repo || sourceUrlRepo?.repo === repoIdentity.repo;
|
|
4009
|
+
if (!ownerMatches || !repoMatches) return false;
|
|
4010
|
+
if (!branchValue || !branch) return true;
|
|
4011
|
+
return branchValue === normalizeRepoName(branch);
|
|
4012
|
+
}
|
|
4013
|
+
function classifyProjectRepoReadiness(args) {
|
|
4014
|
+
const remoteIdentity = parseGitHubRemote(args.remote_url || null);
|
|
4015
|
+
const matchedSources = args.sources.filter(
|
|
4016
|
+
(source) => sourceMatchesWorkspacePath(source, args.root_path) || sourceMatchesWorkspaceRepo(source, remoteIdentity, args.branch || void 0)
|
|
4017
|
+
);
|
|
4018
|
+
if (matchedSources.length === 0) {
|
|
4019
|
+
return {
|
|
4020
|
+
retrieval_readiness: "project_bound_no_repo_source",
|
|
4021
|
+
matched_sources: [],
|
|
4022
|
+
matched_source_ids: [],
|
|
4023
|
+
warnings: [`No verified local or GitHub source matches ${args.root_path}.`]
|
|
4024
|
+
};
|
|
4025
|
+
}
|
|
4026
|
+
const readySources = matchedSources.filter((source) => sourceStatusLooksReady(source.status));
|
|
4027
|
+
if (readySources.length === 0) {
|
|
4028
|
+
return {
|
|
4029
|
+
retrieval_readiness: "project_repo_source_stale",
|
|
4030
|
+
matched_sources: matchedSources,
|
|
4031
|
+
matched_source_ids: matchedSources.map((source) => source.id),
|
|
4032
|
+
warnings: [
|
|
4033
|
+
`Matching repo sources are present but not ready (${matchedSources.map((source) => `${source.name}:${source.status}`).join(", ")}).`
|
|
4034
|
+
]
|
|
4035
|
+
};
|
|
4036
|
+
}
|
|
4037
|
+
return {
|
|
4038
|
+
retrieval_readiness: "project_repo_source_ready",
|
|
4039
|
+
matched_sources: readySources,
|
|
4040
|
+
matched_source_ids: readySources.map((source) => source.id),
|
|
4041
|
+
warnings: []
|
|
4042
|
+
};
|
|
4043
|
+
}
|
|
4044
|
+
function classifyRepoGroundedQuery(args) {
|
|
4045
|
+
const query = args.query.toLowerCase();
|
|
4046
|
+
let score = 0;
|
|
4047
|
+
if (/(^|[\s"'])where is\b|which file|what file|show me|wiring|handler|middleware|implementation|route|endpoint|function|class|module|repo|workspace|code/.test(query)) {
|
|
4048
|
+
score += 1;
|
|
4049
|
+
}
|
|
4050
|
+
if (/\bauth\b|\burl\b|\bcookie\b|\bsession\b|\bapi\b/.test(query)) {
|
|
4051
|
+
score += 1;
|
|
4052
|
+
}
|
|
4053
|
+
if (/[A-Za-z0-9/_-]+\.[A-Za-z0-9]+/.test(args.query) || /\/[A-Za-z0-9._~!$&'()*+,;=:@%/-]{2,}/.test(args.query)) {
|
|
4054
|
+
score += 2;
|
|
4055
|
+
}
|
|
4056
|
+
if (/[A-Za-z_$][A-Za-z0-9_$-]*\(/.test(args.query) || /[A-Z][A-Za-z0-9]+/.test(args.query)) {
|
|
4057
|
+
score += 1;
|
|
4058
|
+
}
|
|
4059
|
+
if ((args.chunk_types || []).some((chunkType) => ["code", "function", "class", "config", "schema", "api_spec"].includes(chunkType))) {
|
|
4060
|
+
score += 2;
|
|
4061
|
+
}
|
|
4062
|
+
const changedTokens = new Set((args.changed_path_tokens || []).map((token) => token.toLowerCase()));
|
|
4063
|
+
if (changedTokens.size > 0) {
|
|
4064
|
+
const queryTokens = tokenizeQueryForLexicalRescue(args.query);
|
|
4065
|
+
if (queryTokens.some((token) => changedTokens.has(token.toLowerCase()))) {
|
|
4066
|
+
score += 1;
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
return score >= 2;
|
|
4070
|
+
}
|
|
3928
4071
|
function clamp012(value) {
|
|
3929
4072
|
if (Number.isNaN(value)) return 0;
|
|
3930
4073
|
if (value < 0) return 0;
|
|
@@ -4066,8 +4209,9 @@ function getWorkspaceWarnings(args) {
|
|
|
4066
4209
|
return [];
|
|
4067
4210
|
}
|
|
4068
4211
|
async function resolveWorkspaceTrust(args) {
|
|
4069
|
-
const
|
|
4070
|
-
const
|
|
4212
|
+
const identity = resolveWorkspaceIdentity({ path: args.path, workspace_id: args.workspace_id });
|
|
4213
|
+
const rootPath = identity.root_path;
|
|
4214
|
+
const workspaceId = identity.workspace_id;
|
|
4071
4215
|
const state = loadState();
|
|
4072
4216
|
const workspace = getWorkspaceState(state, workspaceId);
|
|
4073
4217
|
let mutated = false;
|
|
@@ -4125,6 +4269,7 @@ async function resolveWorkspaceTrust(args) {
|
|
|
4125
4269
|
return {
|
|
4126
4270
|
workspace_id: workspaceId,
|
|
4127
4271
|
root_path: rootPath,
|
|
4272
|
+
identity_source: identity.identity_source,
|
|
4128
4273
|
project_ref: projectRef,
|
|
4129
4274
|
project_id: projectId,
|
|
4130
4275
|
resolved_by: selected.resolved_by,
|
|
@@ -4156,6 +4301,98 @@ async function resolveProjectRef(explicit) {
|
|
|
4156
4301
|
return void 0;
|
|
4157
4302
|
}
|
|
4158
4303
|
}
|
|
4304
|
+
function collectGitChangedPathTokens(searchPath) {
|
|
4305
|
+
const root = searchPath || process.cwd();
|
|
4306
|
+
const result = spawnSync("git", ["-C", root, "status", "--porcelain"], { encoding: "utf-8" });
|
|
4307
|
+
if (result.status !== 0) return [];
|
|
4308
|
+
const lines = String(result.stdout || "").split("\n").map((line) => line.trim()).filter(Boolean);
|
|
4309
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
4310
|
+
for (const line of lines) {
|
|
4311
|
+
const filePath = line.slice(3).split(" -> ").at(-1)?.trim();
|
|
4312
|
+
if (!filePath) continue;
|
|
4313
|
+
for (const token of filePath.split(/[\\/._-]+/).filter((part) => part.length >= 3)) {
|
|
4314
|
+
tokens.add(token.toLowerCase());
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
return Array.from(tokens);
|
|
4318
|
+
}
|
|
4319
|
+
async function inspectProjectRepoSources(args) {
|
|
4320
|
+
if (!args.project_ref) {
|
|
4321
|
+
return {
|
|
4322
|
+
retrieval_readiness: "no_project",
|
|
4323
|
+
matched_sources: [],
|
|
4324
|
+
matched_source_ids: [],
|
|
4325
|
+
warnings: ["No Whisper project is bound to this workspace."]
|
|
4326
|
+
};
|
|
4327
|
+
}
|
|
4328
|
+
try {
|
|
4329
|
+
const sourceData = await whisper.listSources(args.project_ref);
|
|
4330
|
+
const classified = classifyProjectRepoReadiness({
|
|
4331
|
+
sources: sourceData.sources || [],
|
|
4332
|
+
root_path: args.root_path,
|
|
4333
|
+
remote_url: getGitRemoteUrl(args.root_path) || null,
|
|
4334
|
+
branch: getGitBranch(args.root_path) || null
|
|
4335
|
+
});
|
|
4336
|
+
if (classified.retrieval_readiness === "project_bound_no_repo_source") {
|
|
4337
|
+
return {
|
|
4338
|
+
...classified,
|
|
4339
|
+
warnings: [`Project ${args.project_ref} has no verified local or GitHub source for ${args.root_path}.`]
|
|
4340
|
+
};
|
|
4341
|
+
}
|
|
4342
|
+
if (classified.retrieval_readiness === "project_repo_source_stale") {
|
|
4343
|
+
return {
|
|
4344
|
+
...classified,
|
|
4345
|
+
warnings: [
|
|
4346
|
+
`Project ${args.project_ref} has matching repo sources, but none are ready (${classified.matched_sources.map((source) => `${source.name}:${source.status}`).join(", ")}).`
|
|
4347
|
+
]
|
|
4348
|
+
};
|
|
4349
|
+
}
|
|
4350
|
+
return classified;
|
|
4351
|
+
} catch (error) {
|
|
4352
|
+
return {
|
|
4353
|
+
retrieval_readiness: "project_unverified",
|
|
4354
|
+
matched_sources: [],
|
|
4355
|
+
matched_source_ids: [],
|
|
4356
|
+
warnings: [`Could not verify project sources for ${args.project_ref}: ${error.message}`]
|
|
4357
|
+
};
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
async function resolveRepoGroundingPreflight(args) {
|
|
4361
|
+
const trust = await resolveWorkspaceTrust({ path: args.path, workspace_id: args.workspace_id, project: args.project });
|
|
4362
|
+
const repoGrounded = classifyRepoGroundedQuery({
|
|
4363
|
+
query: args.query,
|
|
4364
|
+
chunk_types: args.chunk_types,
|
|
4365
|
+
changed_path_tokens: collectGitChangedPathTokens(trust.root_path)
|
|
4366
|
+
});
|
|
4367
|
+
const sourceVerification = await inspectProjectRepoSources({
|
|
4368
|
+
project_ref: trust.project_ref,
|
|
4369
|
+
root_path: trust.root_path
|
|
4370
|
+
});
|
|
4371
|
+
const warnings = [...trust.warnings, ...sourceVerification.warnings];
|
|
4372
|
+
let retrievalReadiness = sourceVerification.retrieval_readiness;
|
|
4373
|
+
let retrievalRoute = "none";
|
|
4374
|
+
if (repoGrounded) {
|
|
4375
|
+
retrievalRoute = sourceVerification.retrieval_readiness === "project_repo_source_ready" ? "project_repo" : "local_workspace_fallback";
|
|
4376
|
+
if (retrievalRoute === "local_workspace_fallback") {
|
|
4377
|
+
retrievalReadiness = "local_fallback";
|
|
4378
|
+
warnings.push("Using live local workspace retrieval because project-backed repo retrieval is unavailable or unverified.");
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4381
|
+
const recommendedNextCalls = [...trust.recommended_next_calls];
|
|
4382
|
+
if (sourceVerification.retrieval_readiness === "project_bound_no_repo_source" || sourceVerification.retrieval_readiness === "project_unverified") {
|
|
4383
|
+
recommendedNextCalls.push("context.list_sources", "index.local_scan_ingest");
|
|
4384
|
+
}
|
|
4385
|
+
return {
|
|
4386
|
+
repo_grounded: repoGrounded,
|
|
4387
|
+
trust_state: trust,
|
|
4388
|
+
retrieval_readiness: retrievalReadiness,
|
|
4389
|
+
retrieval_route: retrievalRoute,
|
|
4390
|
+
matched_sources: sourceVerification.matched_sources,
|
|
4391
|
+
matched_source_ids: sourceVerification.matched_source_ids,
|
|
4392
|
+
warnings: Array.from(new Set(warnings)),
|
|
4393
|
+
recommended_next_calls: Array.from(new Set(recommendedNextCalls))
|
|
4394
|
+
};
|
|
4395
|
+
}
|
|
4159
4396
|
async function ingestSessionWithSyncFallback(params) {
|
|
4160
4397
|
try {
|
|
4161
4398
|
return await whisper.ingestSession({
|
|
@@ -4182,7 +4419,7 @@ function resolveMcpScope(params) {
|
|
|
4182
4419
|
project: params?.project,
|
|
4183
4420
|
userId: params?.user_id?.trim() || defaultMcpUserId(),
|
|
4184
4421
|
sessionId: params?.session_id?.trim() || cachedMcpSessionId,
|
|
4185
|
-
workspacePath: process.env.WHISPER_WORKSPACE_PATH || process.cwd()
|
|
4422
|
+
workspacePath: canonicalizeWorkspacePath(params?.path || process.env.WHISPER_WORKSPACE_PATH || process.cwd())
|
|
4186
4423
|
};
|
|
4187
4424
|
}
|
|
4188
4425
|
async function prepareAutomaticQuery(params) {
|
|
@@ -4418,6 +4655,7 @@ async function queryWithDegradedFallback(params) {
|
|
|
4418
4655
|
include_graph: params.include_graph,
|
|
4419
4656
|
user_id: params.user_id,
|
|
4420
4657
|
session_id: params.session_id,
|
|
4658
|
+
source_ids: params.source_ids,
|
|
4421
4659
|
hybrid: true,
|
|
4422
4660
|
rerank: true
|
|
4423
4661
|
});
|
|
@@ -4432,6 +4670,7 @@ async function queryWithDegradedFallback(params) {
|
|
|
4432
4670
|
include_graph: false,
|
|
4433
4671
|
user_id: params.user_id,
|
|
4434
4672
|
session_id: params.session_id,
|
|
4673
|
+
source_ids: params.source_ids,
|
|
4435
4674
|
hybrid: false,
|
|
4436
4675
|
rerank: false,
|
|
4437
4676
|
vector_weight: 0,
|
|
@@ -4471,9 +4710,15 @@ function collectCodeFiles(rootPath, allowedExts, maxFiles) {
|
|
|
4471
4710
|
}
|
|
4472
4711
|
function tokenizeQueryForLexicalRescue(query) {
|
|
4473
4712
|
const stopWords = /* @__PURE__ */ new Set(["where", "what", "show", "find", "logic", "code", "file", "files", "handled", "handling"]);
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
)
|
|
4713
|
+
const rawTokens = query.toLowerCase().split(/[^a-z0-9/._:-]+/).map((token) => token.trim()).filter(Boolean);
|
|
4714
|
+
const expanded = /* @__PURE__ */ new Set();
|
|
4715
|
+
for (const token of rawTokens) {
|
|
4716
|
+
if (token.length >= 3 && !stopWords.has(token)) expanded.add(token);
|
|
4717
|
+
for (const part of token.split(/[/:._-]+/).filter((value) => value.length >= 3 && !stopWords.has(value))) {
|
|
4718
|
+
expanded.add(part);
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
return Array.from(expanded);
|
|
4477
4722
|
}
|
|
4478
4723
|
function buildLexicalRescueResults(args) {
|
|
4479
4724
|
const tokens = tokenizeQueryForLexicalRescue(args.query);
|
|
@@ -4499,16 +4744,17 @@ ${doc.raw_content}`.toLowerCase();
|
|
|
4499
4744
|
async function runSearchCodeSearch(args) {
|
|
4500
4745
|
const topK = args.top_k ?? 10;
|
|
4501
4746
|
const requestedThreshold = args.threshold ?? 0.2;
|
|
4747
|
+
const semanticTopK = Math.min(args.documents.length || topK, Math.max(topK * 3, topK + 5));
|
|
4502
4748
|
const semanticDocuments = args.documents.map((doc) => ({ id: doc.id, content: doc.content }));
|
|
4503
4749
|
const defaultResponse = await args.semantic_search({
|
|
4504
4750
|
query: args.query,
|
|
4505
4751
|
documents: semanticDocuments,
|
|
4506
|
-
top_k:
|
|
4752
|
+
top_k: semanticTopK,
|
|
4507
4753
|
threshold: requestedThreshold
|
|
4508
4754
|
});
|
|
4509
4755
|
if (defaultResponse.results?.length) {
|
|
4510
4756
|
return {
|
|
4511
|
-
results: defaultResponse.results.map((result) => ({ ...result, search_mode: "semantic" })),
|
|
4757
|
+
results: defaultResponse.results.slice(0, topK).map((result) => ({ ...result, search_mode: "semantic" })),
|
|
4512
4758
|
mode: "semantic",
|
|
4513
4759
|
threshold_used: requestedThreshold,
|
|
4514
4760
|
fallback_used: false,
|
|
@@ -4520,12 +4766,12 @@ async function runSearchCodeSearch(args) {
|
|
|
4520
4766
|
const adaptiveResponse = await args.semantic_search({
|
|
4521
4767
|
query: args.query,
|
|
4522
4768
|
documents: semanticDocuments,
|
|
4523
|
-
top_k:
|
|
4769
|
+
top_k: semanticTopK,
|
|
4524
4770
|
threshold: adaptiveThreshold
|
|
4525
4771
|
});
|
|
4526
4772
|
if (adaptiveResponse.results?.length) {
|
|
4527
4773
|
return {
|
|
4528
|
-
results: adaptiveResponse.results.map((result) => ({ ...result, search_mode: "adaptive_semantic" })),
|
|
4774
|
+
results: adaptiveResponse.results.slice(0, topK).map((result) => ({ ...result, search_mode: "adaptive_semantic" })),
|
|
4529
4775
|
mode: "adaptive_semantic",
|
|
4530
4776
|
threshold_used: adaptiveThreshold,
|
|
4531
4777
|
fallback_used: true,
|
|
@@ -4546,36 +4792,8 @@ async function runSearchCodeSearch(args) {
|
|
|
4546
4792
|
fallback_reason: lexicalResults.length ? "Semantic ranking returned no matches; lexical rescue over local file paths and content was used." : "Semantic ranking returned no matches and lexical rescue found no strong candidates."
|
|
4547
4793
|
};
|
|
4548
4794
|
}
|
|
4549
|
-
|
|
4550
|
-
const
|
|
4551
|
-
const workspace = await resolveWorkspaceTrust({ path: rootPath });
|
|
4552
|
-
const allowedExts = args.file_types ? new Set(args.file_types) : CODE_EXTENSIONS;
|
|
4553
|
-
const files = collectCodeFiles(rootPath, allowedExts, args.max_files ?? 150);
|
|
4554
|
-
const sharedWarnings = [...workspace.warnings];
|
|
4555
|
-
if (files.length === 0) {
|
|
4556
|
-
return {
|
|
4557
|
-
tool: "search_code",
|
|
4558
|
-
query: args.query,
|
|
4559
|
-
path: rootPath,
|
|
4560
|
-
results: [],
|
|
4561
|
-
count: 0,
|
|
4562
|
-
warnings: sharedWarnings,
|
|
4563
|
-
diagnostics: {
|
|
4564
|
-
workspace_id: workspace.workspace_id,
|
|
4565
|
-
root_path: workspace.root_path,
|
|
4566
|
-
project_ref: workspace.project_ref,
|
|
4567
|
-
project_id: workspace.project_id,
|
|
4568
|
-
index_health: workspace.health,
|
|
4569
|
-
grounded_to_workspace: workspace.grounded_to_workspace,
|
|
4570
|
-
threshold_requested: args.threshold ?? 0.2,
|
|
4571
|
-
threshold_used: null,
|
|
4572
|
-
fallback_used: false,
|
|
4573
|
-
fallback_reason: null,
|
|
4574
|
-
search_mode: "semantic",
|
|
4575
|
-
warnings: sharedWarnings
|
|
4576
|
-
}
|
|
4577
|
-
};
|
|
4578
|
-
}
|
|
4795
|
+
function buildLocalWorkspaceDocuments(rootPath, allowedExts, maxFiles) {
|
|
4796
|
+
const files = collectCodeFiles(rootPath, allowedExts, maxFiles);
|
|
4579
4797
|
const documents = [];
|
|
4580
4798
|
for (const filePath of files) {
|
|
4581
4799
|
try {
|
|
@@ -4587,6 +4805,38 @@ async function runSearchCodeTool(args) {
|
|
|
4587
4805
|
} catch {
|
|
4588
4806
|
}
|
|
4589
4807
|
}
|
|
4808
|
+
return documents;
|
|
4809
|
+
}
|
|
4810
|
+
function extractRelevantSnippet(rawContent, query) {
|
|
4811
|
+
const lines = rawContent.split("\n");
|
|
4812
|
+
const tokens = tokenizeQueryForLexicalRescue(query);
|
|
4813
|
+
let matchIndex = -1;
|
|
4814
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
4815
|
+
const lower = lines[index].toLowerCase();
|
|
4816
|
+
if (tokens.some((token) => lower.includes(token))) {
|
|
4817
|
+
matchIndex = index;
|
|
4818
|
+
break;
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
if (matchIndex < 0) {
|
|
4822
|
+
matchIndex = lines.findIndex((line) => line.trim().length > 0);
|
|
4823
|
+
}
|
|
4824
|
+
const start = Math.max(0, matchIndex < 0 ? 0 : matchIndex - 1);
|
|
4825
|
+
const end = Math.min(lines.length, start + 4);
|
|
4826
|
+
const snippet = lines.slice(start, end).join("\n").trim().slice(0, 500);
|
|
4827
|
+
return {
|
|
4828
|
+
snippet,
|
|
4829
|
+
line_start: start + 1,
|
|
4830
|
+
...end > start + 1 ? { line_end: end } : {}
|
|
4831
|
+
};
|
|
4832
|
+
}
|
|
4833
|
+
async function runLocalWorkspaceRetrieval(args) {
|
|
4834
|
+
const identity = resolveWorkspaceIdentity({ path: args.path });
|
|
4835
|
+
const workspace = await resolveWorkspaceTrust({ path: identity.root_path, project: args.project });
|
|
4836
|
+
const allowedExts = args.file_types ? new Set(args.file_types) : CODE_EXTENSIONS;
|
|
4837
|
+
const candidateFiles = collectCodeFiles(identity.root_path, allowedExts, args.max_files ?? 150);
|
|
4838
|
+
const documents = buildLocalWorkspaceDocuments(identity.root_path, allowedExts, args.max_files ?? 150);
|
|
4839
|
+
const sharedWarnings = [...workspace.warnings];
|
|
4590
4840
|
const searchResult = await runSearchCodeSearch({
|
|
4591
4841
|
query: args.query,
|
|
4592
4842
|
documents,
|
|
@@ -4602,29 +4852,140 @@ async function runSearchCodeTool(args) {
|
|
|
4602
4852
|
if (!workspace.grounded_to_workspace && workspace.health !== "unbound" && workspace.health !== "unindexed") {
|
|
4603
4853
|
sharedWarnings.push("Local code search is live, but project-backed retrieval may disagree until the workspace is re-indexed.");
|
|
4604
4854
|
}
|
|
4855
|
+
const documentMap = new Map(documents.map((document) => [document.id, document]));
|
|
4856
|
+
const localResults = searchResult.results.map((result) => {
|
|
4857
|
+
const document = documentMap.get(result.id);
|
|
4858
|
+
const snippet = extractRelevantSnippet(document?.raw_content || result.snippet || "", args.query);
|
|
4859
|
+
return {
|
|
4860
|
+
...result,
|
|
4861
|
+
snippet: snippet.snippet || result.snippet,
|
|
4862
|
+
raw_snippet: snippet.snippet || result.snippet,
|
|
4863
|
+
line_start: snippet.line_start,
|
|
4864
|
+
...snippet.line_end ? { line_end: snippet.line_end } : {},
|
|
4865
|
+
retrieval_method: result.search_mode === "lexical_rescue" ? "lexical" : "semantic"
|
|
4866
|
+
};
|
|
4867
|
+
});
|
|
4605
4868
|
return {
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
results: searchResult.results,
|
|
4610
|
-
count: searchResult.results.length,
|
|
4869
|
+
workspace,
|
|
4870
|
+
documents,
|
|
4871
|
+
results: localResults,
|
|
4611
4872
|
warnings: sharedWarnings,
|
|
4612
4873
|
diagnostics: {
|
|
4613
4874
|
workspace_id: workspace.workspace_id,
|
|
4614
4875
|
root_path: workspace.root_path,
|
|
4615
4876
|
project_ref: workspace.project_ref,
|
|
4616
4877
|
project_id: workspace.project_id,
|
|
4878
|
+
identity_source: workspace.identity_source,
|
|
4617
4879
|
index_health: workspace.health,
|
|
4618
|
-
grounded_to_workspace:
|
|
4880
|
+
grounded_to_workspace: true,
|
|
4619
4881
|
threshold_requested: args.threshold ?? 0.2,
|
|
4620
4882
|
threshold_used: searchResult.threshold_used,
|
|
4621
4883
|
fallback_used: searchResult.fallback_used,
|
|
4622
4884
|
fallback_reason: searchResult.fallback_reason,
|
|
4623
4885
|
search_mode: searchResult.mode,
|
|
4886
|
+
candidate_files_scanned: candidateFiles.length,
|
|
4887
|
+
semantic_candidate_count: documents.length,
|
|
4888
|
+
retrieval_route: "local_workspace",
|
|
4624
4889
|
warnings: sharedWarnings
|
|
4625
4890
|
}
|
|
4626
4891
|
};
|
|
4627
4892
|
}
|
|
4893
|
+
function isLikelyRepoBackedResult(result) {
|
|
4894
|
+
const metadata = result.metadata || {};
|
|
4895
|
+
const retrievalSource = String(result.retrieval_source || "").toLowerCase();
|
|
4896
|
+
if (retrievalSource.includes("memory")) return false;
|
|
4897
|
+
const pathCandidate = String(metadata.file_path || metadata.path || result.source || result.document || "");
|
|
4898
|
+
if (/[\\/]/.test(pathCandidate) || /\.[a-z0-9]+$/i.test(pathCandidate)) return true;
|
|
4899
|
+
const sourceType = String(metadata.source_type || metadata.connector_type || result.type || "").toLowerCase();
|
|
4900
|
+
return ["local", "github", "code", "file", "repo"].some((token) => sourceType.includes(token));
|
|
4901
|
+
}
|
|
4902
|
+
function filterProjectRepoResults(results, matchedSourceIds) {
|
|
4903
|
+
const scoped = results.filter((result) => {
|
|
4904
|
+
const sourceId = String(result.metadata?.source_id || result.metadata?.sourceId || result.metadata?.source || "");
|
|
4905
|
+
return matchedSourceIds.length === 0 || matchedSourceIds.includes(sourceId);
|
|
4906
|
+
});
|
|
4907
|
+
const repoResults = scoped.filter((result) => isLikelyRepoBackedResult(result));
|
|
4908
|
+
return repoResults.length > 0 ? repoResults : scoped;
|
|
4909
|
+
}
|
|
4910
|
+
function localHitsToEvidence(workspaceId, hits) {
|
|
4911
|
+
return hits.map(
|
|
4912
|
+
(hit) => toEvidenceRef(
|
|
4913
|
+
{
|
|
4914
|
+
id: hit.id,
|
|
4915
|
+
content: hit.raw_snippet,
|
|
4916
|
+
score: hit.score,
|
|
4917
|
+
retrieval_source: hit.retrieval_method,
|
|
4918
|
+
metadata: {
|
|
4919
|
+
file_path: hit.id,
|
|
4920
|
+
snippet: hit.raw_snippet,
|
|
4921
|
+
line_start: hit.line_start,
|
|
4922
|
+
...hit.line_end ? { line_end: hit.line_end } : {}
|
|
4923
|
+
}
|
|
4924
|
+
},
|
|
4925
|
+
workspaceId,
|
|
4926
|
+
hit.retrieval_method
|
|
4927
|
+
)
|
|
4928
|
+
);
|
|
4929
|
+
}
|
|
4930
|
+
function renderLocalWorkspaceContext(args) {
|
|
4931
|
+
const lines = args.hits.map((hit, index) => {
|
|
4932
|
+
const location = hit.line_end && hit.line_end !== hit.line_start ? `${hit.id}:${hit.line_start}-${hit.line_end}` : `${hit.id}:${hit.line_start}`;
|
|
4933
|
+
return `${index + 1}. [${location}, score: ${hit.score.toFixed(2)}, mode: ${hit.search_mode}] ${hit.raw_snippet || hit.snippet || hit.id}`;
|
|
4934
|
+
});
|
|
4935
|
+
const header = `Found ${args.hits.length} local workspace result(s) (route=${args.route}, workspace=${args.diagnostics.workspace_id}, project=${args.project_ref || "none"}, user=${args.scope.userId}, session=${args.scope.sessionId}):`;
|
|
4936
|
+
const warnings = args.warnings.length ? `
|
|
4937
|
+
|
|
4938
|
+
[warnings]
|
|
4939
|
+
${args.warnings.join("\n")}` : "";
|
|
4940
|
+
return `${header}
|
|
4941
|
+
|
|
4942
|
+
${lines.join("\n\n")}${warnings}`;
|
|
4943
|
+
}
|
|
4944
|
+
async function runSearchCodeTool(args) {
|
|
4945
|
+
const rootPath = canonicalizeWorkspacePath(args.path);
|
|
4946
|
+
const allowedExts = args.file_types ? new Set(args.file_types) : CODE_EXTENSIONS;
|
|
4947
|
+
const files = collectCodeFiles(rootPath, allowedExts, args.max_files ?? 150);
|
|
4948
|
+
if (files.length === 0) {
|
|
4949
|
+
const workspace = await resolveWorkspaceTrust({ path: rootPath });
|
|
4950
|
+
const sharedWarnings = [...workspace.warnings];
|
|
4951
|
+
return {
|
|
4952
|
+
tool: "search_code",
|
|
4953
|
+
query: args.query,
|
|
4954
|
+
path: rootPath,
|
|
4955
|
+
results: [],
|
|
4956
|
+
count: 0,
|
|
4957
|
+
warnings: sharedWarnings,
|
|
4958
|
+
diagnostics: {
|
|
4959
|
+
workspace_id: workspace.workspace_id,
|
|
4960
|
+
root_path: workspace.root_path,
|
|
4961
|
+
project_ref: workspace.project_ref,
|
|
4962
|
+
project_id: workspace.project_id,
|
|
4963
|
+
identity_source: workspace.identity_source,
|
|
4964
|
+
index_health: workspace.health,
|
|
4965
|
+
grounded_to_workspace: workspace.grounded_to_workspace,
|
|
4966
|
+
threshold_requested: args.threshold ?? 0.2,
|
|
4967
|
+
threshold_used: null,
|
|
4968
|
+
fallback_used: false,
|
|
4969
|
+
fallback_reason: null,
|
|
4970
|
+
search_mode: "semantic",
|
|
4971
|
+
candidate_files_scanned: 0,
|
|
4972
|
+
semantic_candidate_count: 0,
|
|
4973
|
+
retrieval_route: "local_workspace",
|
|
4974
|
+
warnings: sharedWarnings
|
|
4975
|
+
}
|
|
4976
|
+
};
|
|
4977
|
+
}
|
|
4978
|
+
const localRetrieval = await runLocalWorkspaceRetrieval(args);
|
|
4979
|
+
return {
|
|
4980
|
+
tool: "search_code",
|
|
4981
|
+
query: args.query,
|
|
4982
|
+
path: rootPath,
|
|
4983
|
+
results: localRetrieval.results,
|
|
4984
|
+
count: localRetrieval.results.length,
|
|
4985
|
+
warnings: localRetrieval.warnings,
|
|
4986
|
+
diagnostics: localRetrieval.diagnostics
|
|
4987
|
+
};
|
|
4988
|
+
}
|
|
4628
4989
|
function getLocalAllowlistRoots() {
|
|
4629
4990
|
const fromEnv = (process.env.WHISPER_LOCAL_ALLOWLIST || "").split(",").map((v) => v.trim()).filter(Boolean);
|
|
4630
4991
|
if (fromEnv.length > 0) return fromEnv;
|
|
@@ -4900,21 +5261,23 @@ server.tool(
|
|
|
4900
5261
|
},
|
|
4901
5262
|
async ({ path, workspace_id, project }) => {
|
|
4902
5263
|
try {
|
|
4903
|
-
const
|
|
4904
|
-
const workspaceId = getWorkspaceIdForPath(rootPath, workspace_id);
|
|
5264
|
+
const identity = resolveWorkspaceIdentity({ path, workspace_id });
|
|
4905
5265
|
const state = loadState();
|
|
4906
|
-
const existed = Boolean(state.workspaces[
|
|
4907
|
-
const trust = await resolveWorkspaceTrust({ path:
|
|
5266
|
+
const existed = Boolean(state.workspaces[identity.workspace_id]);
|
|
5267
|
+
const trust = await resolveWorkspaceTrust({ path: identity.root_path, workspace_id, project });
|
|
5268
|
+
const preflight = await resolveRepoGroundingPreflight({ query: "workspace status", path, workspace_id, project });
|
|
4908
5269
|
const payload = {
|
|
4909
|
-
workspace_id:
|
|
5270
|
+
workspace_id: identity.workspace_id,
|
|
4910
5271
|
root_path: trust.root_path,
|
|
5272
|
+
identity_source: trust.identity_source,
|
|
4911
5273
|
project_ref: trust.project_ref,
|
|
4912
5274
|
project_id: trust.project_id,
|
|
4913
5275
|
created: !existed,
|
|
4914
5276
|
resolved_by: trust.resolved_by,
|
|
4915
5277
|
health: trust.health,
|
|
4916
|
-
|
|
4917
|
-
|
|
5278
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5279
|
+
warnings: preflight.warnings,
|
|
5280
|
+
recommended_next_calls: preflight.recommended_next_calls,
|
|
4918
5281
|
index_state: {
|
|
4919
5282
|
last_indexed_at: trust.freshness.last_indexed_at,
|
|
4920
5283
|
last_indexed_commit: trust.last_indexed_commit,
|
|
@@ -4937,15 +5300,23 @@ server.tool(
|
|
|
4937
5300
|
async ({ workspace_id, path }) => {
|
|
4938
5301
|
try {
|
|
4939
5302
|
const trust = await resolveWorkspaceTrust({ path, workspace_id });
|
|
5303
|
+
const repoVerification = await inspectProjectRepoSources({ project_ref: trust.project_ref, root_path: trust.root_path });
|
|
4940
5304
|
const payload = {
|
|
4941
5305
|
workspace_id: trust.workspace_id,
|
|
4942
5306
|
root_path: trust.root_path,
|
|
5307
|
+
identity_source: trust.identity_source,
|
|
4943
5308
|
project_ref: trust.project_ref,
|
|
4944
5309
|
project_id: trust.project_id,
|
|
4945
5310
|
resolved_by: trust.resolved_by,
|
|
4946
5311
|
health: trust.health,
|
|
4947
|
-
|
|
4948
|
-
|
|
5312
|
+
retrieval_readiness: repoVerification.retrieval_readiness,
|
|
5313
|
+
warnings: Array.from(/* @__PURE__ */ new Set([...trust.warnings, ...repoVerification.warnings])),
|
|
5314
|
+
recommended_next_calls: Array.from(
|
|
5315
|
+
/* @__PURE__ */ new Set([
|
|
5316
|
+
...trust.recommended_next_calls,
|
|
5317
|
+
...repoVerification.retrieval_readiness === "project_bound_no_repo_source" ? ["context.list_sources", "index.local_scan_ingest"] : []
|
|
5318
|
+
])
|
|
5319
|
+
),
|
|
4949
5320
|
freshness: trust.freshness,
|
|
4950
5321
|
coverage: trust.coverage,
|
|
4951
5322
|
last_indexed_commit: trust.last_indexed_commit,
|
|
@@ -4970,8 +5341,9 @@ server.tool(
|
|
|
4970
5341
|
},
|
|
4971
5342
|
async ({ workspace_id, path, mode, max_files }) => {
|
|
4972
5343
|
try {
|
|
4973
|
-
const
|
|
4974
|
-
const
|
|
5344
|
+
const identity = resolveWorkspaceIdentity({ path, workspace_id });
|
|
5345
|
+
const rootPath = identity.root_path;
|
|
5346
|
+
const workspaceId = identity.workspace_id;
|
|
4975
5347
|
const state = loadState();
|
|
4976
5348
|
const workspace = getWorkspaceState(state, workspaceId);
|
|
4977
5349
|
const fileStats = countCodeFiles(rootPath, max_files);
|
|
@@ -5038,9 +5410,10 @@ server.tool(
|
|
|
5038
5410
|
);
|
|
5039
5411
|
server.tool(
|
|
5040
5412
|
"context.get_relevant",
|
|
5041
|
-
"
|
|
5413
|
+
"Default grounded retrieval step for workspace/project questions. Call this before answering when you need ranked evidence with file:line citations instead of relying on model memory.",
|
|
5042
5414
|
{
|
|
5043
5415
|
question: z.string().describe("Task/question to retrieve context for"),
|
|
5416
|
+
path: z.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
5044
5417
|
workspace_id: z.string().optional(),
|
|
5045
5418
|
project: z.string().optional(),
|
|
5046
5419
|
top_k: z.number().optional().default(12),
|
|
@@ -5049,51 +5422,115 @@ server.tool(
|
|
|
5049
5422
|
session_id: z.string().optional(),
|
|
5050
5423
|
user_id: z.string().optional()
|
|
5051
5424
|
},
|
|
5052
|
-
async ({ question, workspace_id, project, top_k, include_memories, include_graph, session_id, user_id }) => {
|
|
5425
|
+
async ({ question, path, workspace_id, project, top_k, include_memories, include_graph, session_id, user_id }) => {
|
|
5053
5426
|
try {
|
|
5054
|
-
const
|
|
5055
|
-
if (
|
|
5427
|
+
const preflight = await resolveRepoGroundingPreflight({ query: question, path, workspace_id, project });
|
|
5428
|
+
if (preflight.retrieval_route === "local_workspace_fallback") {
|
|
5429
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5430
|
+
query: question,
|
|
5431
|
+
path: preflight.trust_state.root_path,
|
|
5432
|
+
project: preflight.trust_state.project_ref || project,
|
|
5433
|
+
top_k
|
|
5434
|
+
});
|
|
5435
|
+
const evidence2 = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results);
|
|
5056
5436
|
const payload2 = {
|
|
5057
5437
|
question,
|
|
5058
|
-
workspace_id:
|
|
5059
|
-
|
|
5438
|
+
workspace_id: preflight.trust_state.workspace_id,
|
|
5439
|
+
root_path: preflight.trust_state.root_path,
|
|
5440
|
+
identity_source: preflight.trust_state.identity_source,
|
|
5441
|
+
trust_state: preflight.trust_state,
|
|
5442
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5443
|
+
retrieval_route: preflight.retrieval_route,
|
|
5444
|
+
grounded_to_workspace: true,
|
|
5445
|
+
total_results: evidence2.length,
|
|
5446
|
+
context: evidence2.map((item) => `[${renderCitation(item)}] ${item.snippet || "Relevant local workspace context found."}`).join("\n"),
|
|
5447
|
+
evidence: evidence2,
|
|
5448
|
+
used_context_ids: evidence2.map((item) => item.source_id),
|
|
5449
|
+
latency_ms: 0,
|
|
5450
|
+
warnings: Array.from(/* @__PURE__ */ new Set([...preflight.warnings, ...localRetrieval.warnings])),
|
|
5451
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5452
|
+
};
|
|
5453
|
+
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
5454
|
+
}
|
|
5455
|
+
if (!preflight.trust_state.project_ref) {
|
|
5456
|
+
const payload2 = {
|
|
5457
|
+
question,
|
|
5458
|
+
workspace_id: preflight.trust_state.workspace_id,
|
|
5459
|
+
root_path: preflight.trust_state.root_path,
|
|
5460
|
+
identity_source: preflight.trust_state.identity_source,
|
|
5461
|
+
trust_state: preflight.trust_state,
|
|
5462
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5463
|
+
retrieval_route: "none",
|
|
5060
5464
|
grounded_to_workspace: false,
|
|
5061
5465
|
total_results: 0,
|
|
5062
5466
|
context: "",
|
|
5063
5467
|
evidence: [],
|
|
5064
5468
|
used_context_ids: [],
|
|
5065
5469
|
latency_ms: 0,
|
|
5066
|
-
warnings:
|
|
5067
|
-
recommended_next_calls:
|
|
5470
|
+
warnings: preflight.warnings,
|
|
5471
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5068
5472
|
};
|
|
5069
5473
|
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
5070
5474
|
}
|
|
5071
5475
|
const queryResult = await queryWithDegradedFallback({
|
|
5072
|
-
project:
|
|
5476
|
+
project: preflight.trust_state.project_ref,
|
|
5073
5477
|
query: question,
|
|
5074
5478
|
top_k,
|
|
5075
|
-
include_memories,
|
|
5479
|
+
include_memories: preflight.repo_grounded ? false : include_memories,
|
|
5076
5480
|
include_graph,
|
|
5077
5481
|
session_id,
|
|
5078
|
-
user_id
|
|
5482
|
+
user_id,
|
|
5483
|
+
source_ids: preflight.repo_grounded ? preflight.matched_source_ids : void 0
|
|
5079
5484
|
});
|
|
5080
5485
|
const response = queryResult.response;
|
|
5081
|
-
const
|
|
5486
|
+
const rawResults = preflight.repo_grounded ? filterProjectRepoResults(response.results || [], preflight.matched_source_ids) : response.results || [];
|
|
5487
|
+
if (preflight.repo_grounded && rawResults.length === 0) {
|
|
5488
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5489
|
+
query: question,
|
|
5490
|
+
path: preflight.trust_state.root_path,
|
|
5491
|
+
project: preflight.trust_state.project_ref,
|
|
5492
|
+
top_k
|
|
5493
|
+
});
|
|
5494
|
+
const evidence2 = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results);
|
|
5495
|
+
const payload2 = {
|
|
5496
|
+
question,
|
|
5497
|
+
workspace_id: preflight.trust_state.workspace_id,
|
|
5498
|
+
root_path: preflight.trust_state.root_path,
|
|
5499
|
+
identity_source: preflight.trust_state.identity_source,
|
|
5500
|
+
trust_state: preflight.trust_state,
|
|
5501
|
+
retrieval_readiness: "local_fallback",
|
|
5502
|
+
retrieval_route: "local_workspace_fallback",
|
|
5503
|
+
grounded_to_workspace: true,
|
|
5504
|
+
total_results: evidence2.length,
|
|
5505
|
+
context: evidence2.map((item) => `[${renderCitation(item)}] ${item.snippet || "Relevant local workspace context found."}`).join("\n"),
|
|
5506
|
+
evidence: evidence2,
|
|
5507
|
+
used_context_ids: evidence2.map((item) => item.source_id),
|
|
5508
|
+
latency_ms: 0,
|
|
5509
|
+
warnings: Array.from(/* @__PURE__ */ new Set([...preflight.warnings, ...localRetrieval.warnings])),
|
|
5510
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5511
|
+
};
|
|
5512
|
+
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
5513
|
+
}
|
|
5514
|
+
const evidence = rawResults.map((r) => toEvidenceRef(r, preflight.trust_state.workspace_id, "semantic"));
|
|
5082
5515
|
const payload = {
|
|
5083
5516
|
question,
|
|
5084
|
-
workspace_id:
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5517
|
+
workspace_id: preflight.trust_state.workspace_id,
|
|
5518
|
+
root_path: preflight.trust_state.root_path,
|
|
5519
|
+
identity_source: preflight.trust_state.identity_source,
|
|
5520
|
+
trust_state: preflight.trust_state,
|
|
5521
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5522
|
+
retrieval_route: preflight.repo_grounded ? "project_repo" : "none",
|
|
5523
|
+
grounded_to_workspace: preflight.repo_grounded ? true : preflight.trust_state.grounded_to_workspace,
|
|
5524
|
+
total_results: rawResults.length || response.meta?.total || evidence.length,
|
|
5088
5525
|
context: response.context || "",
|
|
5089
5526
|
evidence,
|
|
5090
|
-
used_context_ids:
|
|
5527
|
+
used_context_ids: rawResults.map((r) => String(r.id)),
|
|
5091
5528
|
latency_ms: response.meta?.latency_ms || 0,
|
|
5092
5529
|
degraded_mode: queryResult.degraded_mode,
|
|
5093
5530
|
degraded_reason: queryResult.degraded_reason,
|
|
5094
5531
|
recommendation: queryResult.recommendation,
|
|
5095
|
-
warnings:
|
|
5096
|
-
recommended_next_calls:
|
|
5532
|
+
warnings: preflight.warnings,
|
|
5533
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5097
5534
|
};
|
|
5098
5535
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
5099
5536
|
} catch (error) {
|
|
@@ -5106,38 +5543,53 @@ server.tool(
|
|
|
5106
5543
|
"Verify whether a claim is supported by retrieved context. Returns supported/partial/unsupported with evidence.",
|
|
5107
5544
|
{
|
|
5108
5545
|
claim: z.string().describe("Claim to verify"),
|
|
5546
|
+
path: z.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
5109
5547
|
workspace_id: z.string().optional(),
|
|
5110
5548
|
project: z.string().optional(),
|
|
5111
5549
|
context_ids: z.array(z.string()).optional(),
|
|
5112
5550
|
strict: z.boolean().optional().default(true)
|
|
5113
5551
|
},
|
|
5114
|
-
async ({ claim, workspace_id, project, context_ids, strict }) => {
|
|
5552
|
+
async ({ claim, path, workspace_id, project, context_ids, strict }) => {
|
|
5115
5553
|
try {
|
|
5116
|
-
const
|
|
5117
|
-
|
|
5554
|
+
const preflight = await resolveRepoGroundingPreflight({ query: claim, path, workspace_id, project });
|
|
5555
|
+
let evidence = [];
|
|
5556
|
+
if (preflight.retrieval_route === "local_workspace_fallback") {
|
|
5557
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5558
|
+
query: claim,
|
|
5559
|
+
path: preflight.trust_state.root_path,
|
|
5560
|
+
project: preflight.trust_state.project_ref || project,
|
|
5561
|
+
top_k: strict ? 8 : 12
|
|
5562
|
+
});
|
|
5563
|
+
evidence = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results).filter((item) => !context_ids || context_ids.length === 0 || context_ids.includes(item.source_id));
|
|
5564
|
+
} else if (preflight.trust_state.project_ref) {
|
|
5565
|
+
const response = await whisper.query({
|
|
5566
|
+
project: preflight.trust_state.project_ref,
|
|
5567
|
+
query: claim,
|
|
5568
|
+
top_k: strict ? 8 : 12,
|
|
5569
|
+
include_memories: preflight.repo_grounded ? false : true,
|
|
5570
|
+
include_graph: true,
|
|
5571
|
+
...preflight.repo_grounded ? { source_ids: preflight.matched_source_ids } : {}
|
|
5572
|
+
});
|
|
5573
|
+
const filtered = (preflight.repo_grounded ? filterProjectRepoResults(response.results || [], preflight.matched_source_ids) : response.results || []).filter(
|
|
5574
|
+
(r) => !context_ids || context_ids.length === 0 || context_ids.includes(String(r.id))
|
|
5575
|
+
);
|
|
5576
|
+
evidence = filtered.map((r) => toEvidenceRef(r, preflight.trust_state.workspace_id, "semantic"));
|
|
5577
|
+
}
|
|
5578
|
+
if (evidence.length === 0) {
|
|
5118
5579
|
const payload2 = {
|
|
5119
5580
|
verdict: "unsupported",
|
|
5120
5581
|
confidence: 0,
|
|
5121
5582
|
evidence: [],
|
|
5122
|
-
trust_state:
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5583
|
+
trust_state: preflight.trust_state,
|
|
5584
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5585
|
+
retrieval_route: preflight.retrieval_route,
|
|
5586
|
+
warnings: preflight.warnings,
|
|
5587
|
+
recommended_next_calls: preflight.recommended_next_calls,
|
|
5588
|
+
missing_requirements: preflight.warnings.length ? preflight.warnings : ["No repo-grounded evidence was available for verification."],
|
|
5589
|
+
explanation: "Verifier did not find sufficient repo-grounded evidence."
|
|
5127
5590
|
};
|
|
5128
5591
|
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
5129
5592
|
}
|
|
5130
|
-
const response = await whisper.query({
|
|
5131
|
-
project: trust.project_ref,
|
|
5132
|
-
query: claim,
|
|
5133
|
-
top_k: strict ? 8 : 12,
|
|
5134
|
-
include_memories: true,
|
|
5135
|
-
include_graph: true
|
|
5136
|
-
});
|
|
5137
|
-
const filtered = (response.results || []).filter(
|
|
5138
|
-
(r) => !context_ids || context_ids.length === 0 || context_ids.includes(String(r.id))
|
|
5139
|
-
);
|
|
5140
|
-
const evidence = filtered.map((r) => toEvidenceRef(r, trust.workspace_id, "semantic"));
|
|
5141
5593
|
const directEvidence = evidence.filter((e) => e.score >= (strict ? 0.7 : 0.6));
|
|
5142
5594
|
const weakEvidence = evidence.filter((e) => e.score >= (strict ? 0.45 : 0.35));
|
|
5143
5595
|
let verdict = "unsupported";
|
|
@@ -5147,9 +5599,11 @@ server.tool(
|
|
|
5147
5599
|
verdict,
|
|
5148
5600
|
confidence: evidence.length ? Math.max(...evidence.map((e) => e.score)) : 0,
|
|
5149
5601
|
evidence: verdict === "supported" ? directEvidence : weakEvidence,
|
|
5150
|
-
trust_state:
|
|
5151
|
-
|
|
5152
|
-
|
|
5602
|
+
trust_state: preflight.trust_state,
|
|
5603
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5604
|
+
retrieval_route: preflight.retrieval_route,
|
|
5605
|
+
warnings: preflight.warnings,
|
|
5606
|
+
recommended_next_calls: preflight.recommended_next_calls,
|
|
5153
5607
|
missing_requirements: verdict === "supported" ? [] : verdict === "partial" ? ["No direct evidence spans met strict threshold."] : ["No sufficient supporting evidence found for the claim."],
|
|
5154
5608
|
explanation: verdict === "supported" ? "At least one direct evidence span supports the claim." : verdict === "partial" ? "Some related evidence exists, but direct support is incomplete." : "Retrieved context did not contain sufficient support."
|
|
5155
5609
|
};
|
|
@@ -5164,6 +5618,7 @@ server.tool(
|
|
|
5164
5618
|
"Answer a question only when evidence requirements are met. Fails closed with an abstain payload when not verifiable.",
|
|
5165
5619
|
{
|
|
5166
5620
|
question: z.string(),
|
|
5621
|
+
path: z.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
5167
5622
|
workspace_id: z.string().optional(),
|
|
5168
5623
|
project: z.string().optional(),
|
|
5169
5624
|
constraints: z.object({
|
|
@@ -5178,38 +5633,51 @@ server.tool(
|
|
|
5178
5633
|
include_recent_decisions: z.boolean().optional().default(true)
|
|
5179
5634
|
}).optional()
|
|
5180
5635
|
},
|
|
5181
|
-
async ({ question, workspace_id, project, constraints, retrieval }) => {
|
|
5636
|
+
async ({ question, path, workspace_id, project, constraints, retrieval }) => {
|
|
5182
5637
|
try {
|
|
5183
5638
|
const requireCitations = constraints?.require_citations ?? true;
|
|
5184
5639
|
const minEvidenceItems = constraints?.min_evidence_items ?? 2;
|
|
5185
5640
|
const minConfidence = constraints?.min_confidence ?? 0.65;
|
|
5186
5641
|
const maxStalenessHours = constraints?.max_staleness_hours ?? 168;
|
|
5187
5642
|
const topK = retrieval?.top_k ?? 12;
|
|
5188
|
-
const
|
|
5189
|
-
|
|
5190
|
-
|
|
5643
|
+
const preflight = await resolveRepoGroundingPreflight({ query: question, path, workspace_id, project });
|
|
5644
|
+
let evidence = [];
|
|
5645
|
+
if (preflight.retrieval_route === "local_workspace_fallback") {
|
|
5646
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5647
|
+
query: question,
|
|
5648
|
+
path: preflight.trust_state.root_path,
|
|
5649
|
+
project: preflight.trust_state.project_ref || project,
|
|
5650
|
+
top_k: topK
|
|
5651
|
+
});
|
|
5652
|
+
evidence = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results);
|
|
5653
|
+
} else if (preflight.trust_state.project_ref) {
|
|
5654
|
+
const response = await whisper.query({
|
|
5655
|
+
project: preflight.trust_state.project_ref,
|
|
5656
|
+
query: question,
|
|
5657
|
+
top_k: topK,
|
|
5658
|
+
include_memories: preflight.repo_grounded ? false : true,
|
|
5659
|
+
include_graph: true,
|
|
5660
|
+
...preflight.repo_grounded ? { source_ids: preflight.matched_source_ids } : {}
|
|
5661
|
+
});
|
|
5662
|
+
const filtered = preflight.repo_grounded ? filterProjectRepoResults(response.results || [], preflight.matched_source_ids) : response.results || [];
|
|
5663
|
+
evidence = filtered.map((r) => toEvidenceRef(r, preflight.trust_state.workspace_id, "semantic"));
|
|
5664
|
+
}
|
|
5665
|
+
if (evidence.length === 0) {
|
|
5666
|
+
const reason = preflight.trust_state.health === "stale" || preflight.trust_state.health === "drifted" ? "stale_index" : "no_retrieval_hits";
|
|
5191
5667
|
const abstain = buildAbstain({
|
|
5192
5668
|
reason,
|
|
5193
|
-
message:
|
|
5669
|
+
message: preflight.warnings[0] || "Workspace trust requirements were not met.",
|
|
5194
5670
|
closest_evidence: [],
|
|
5195
5671
|
claims_evaluated: 1,
|
|
5196
5672
|
evidence_items_found: 0,
|
|
5197
5673
|
min_required: minEvidenceItems,
|
|
5198
|
-
index_fresh:
|
|
5199
|
-
warnings:
|
|
5200
|
-
trust_state:
|
|
5201
|
-
recommended_next_calls:
|
|
5674
|
+
index_fresh: preflight.trust_state.health === "healthy",
|
|
5675
|
+
warnings: preflight.warnings,
|
|
5676
|
+
trust_state: preflight.trust_state,
|
|
5677
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5202
5678
|
});
|
|
5203
5679
|
return { content: [{ type: "text", text: JSON.stringify(abstain, null, 2) }] };
|
|
5204
5680
|
}
|
|
5205
|
-
const response = await whisper.query({
|
|
5206
|
-
project: trust.project_ref,
|
|
5207
|
-
query: question,
|
|
5208
|
-
top_k: topK,
|
|
5209
|
-
include_memories: true,
|
|
5210
|
-
include_graph: true
|
|
5211
|
-
});
|
|
5212
|
-
const evidence = (response.results || []).map((r) => toEvidenceRef(r, trust.workspace_id, "semantic"));
|
|
5213
5681
|
const sorted = evidence.sort((a, b) => b.score - a.score);
|
|
5214
5682
|
const confidence = sorted.length ? sorted[0].score : 0;
|
|
5215
5683
|
if (sorted.length === 0) {
|
|
@@ -5220,10 +5688,10 @@ server.tool(
|
|
|
5220
5688
|
claims_evaluated: 1,
|
|
5221
5689
|
evidence_items_found: 0,
|
|
5222
5690
|
min_required: minEvidenceItems,
|
|
5223
|
-
index_fresh:
|
|
5224
|
-
warnings:
|
|
5225
|
-
trust_state:
|
|
5226
|
-
recommended_next_calls:
|
|
5691
|
+
index_fresh: preflight.trust_state.health === "healthy",
|
|
5692
|
+
warnings: preflight.warnings,
|
|
5693
|
+
trust_state: preflight.trust_state,
|
|
5694
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5227
5695
|
});
|
|
5228
5696
|
return { content: [{ type: "text", text: JSON.stringify(abstain, null, 2) }] };
|
|
5229
5697
|
}
|
|
@@ -5238,9 +5706,9 @@ server.tool(
|
|
|
5238
5706
|
evidence_items_found: supportedEvidence.length,
|
|
5239
5707
|
min_required: minEvidenceItems,
|
|
5240
5708
|
index_fresh: true,
|
|
5241
|
-
warnings:
|
|
5242
|
-
trust_state:
|
|
5243
|
-
recommended_next_calls:
|
|
5709
|
+
warnings: preflight.warnings,
|
|
5710
|
+
trust_state: preflight.trust_state,
|
|
5711
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5244
5712
|
});
|
|
5245
5713
|
return { content: [{ type: "text", text: JSON.stringify(abstain, null, 2) }] };
|
|
5246
5714
|
}
|
|
@@ -5253,9 +5721,11 @@ server.tool(
|
|
|
5253
5721
|
answer: answerLines.join("\n"),
|
|
5254
5722
|
citations,
|
|
5255
5723
|
confidence,
|
|
5256
|
-
trust_state:
|
|
5257
|
-
|
|
5258
|
-
|
|
5724
|
+
trust_state: preflight.trust_state,
|
|
5725
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5726
|
+
retrieval_route: preflight.retrieval_route,
|
|
5727
|
+
warnings: preflight.warnings,
|
|
5728
|
+
recommended_next_calls: preflight.recommended_next_calls,
|
|
5259
5729
|
verification: {
|
|
5260
5730
|
verdict,
|
|
5261
5731
|
supported_claims: verdict === "supported" ? 1 : 0,
|
|
@@ -5271,10 +5741,11 @@ server.tool(
|
|
|
5271
5741
|
);
|
|
5272
5742
|
server.tool(
|
|
5273
5743
|
"context.query",
|
|
5274
|
-
"
|
|
5744
|
+
"Use this when answering from project knowledge rather than general model memory. Retrieves packed context for a query using hybrid vector+keyword search with optional memory/graph expansion.",
|
|
5275
5745
|
{
|
|
5276
5746
|
project: z.string().optional().describe("Project name or slug (optional if WHISPER_PROJECT is set)"),
|
|
5277
5747
|
query: z.string().describe("What are you looking for?"),
|
|
5748
|
+
path: z.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
5278
5749
|
top_k: z.number().optional().default(10).describe("Number of results"),
|
|
5279
5750
|
chunk_types: z.array(z.string()).optional().describe("Filter: code, function, class, documentation, api_spec, schema, config, text"),
|
|
5280
5751
|
include_memories: z.boolean().optional().describe("Include relevant memories. Omit to use automatic runtime defaults."),
|
|
@@ -5283,14 +5754,105 @@ server.tool(
|
|
|
5283
5754
|
session_id: z.string().optional().describe("Session ID for memory scoping"),
|
|
5284
5755
|
max_tokens: z.number().optional().describe("Max tokens for packed context")
|
|
5285
5756
|
},
|
|
5286
|
-
async ({ project, query, top_k, chunk_types, include_memories, include_graph, user_id, session_id, max_tokens }) => {
|
|
5757
|
+
async ({ project, query, path, top_k, chunk_types, include_memories, include_graph, user_id, session_id, max_tokens }) => {
|
|
5287
5758
|
try {
|
|
5288
|
-
const
|
|
5289
|
-
|
|
5759
|
+
const preflight = await resolveRepoGroundingPreflight({ query, path, project, chunk_types });
|
|
5760
|
+
const resolvedProject = preflight.trust_state.project_ref || await resolveProjectRef(project);
|
|
5761
|
+
if (!resolvedProject && !preflight.repo_grounded) {
|
|
5290
5762
|
return { content: [{ type: "text", text: "Error: No project resolved. Set WHISPER_PROJECT or pass project." }] };
|
|
5291
5763
|
}
|
|
5292
|
-
const scope = resolveMcpScope({ project: resolvedProject, user_id, session_id });
|
|
5293
|
-
|
|
5764
|
+
const scope = resolveMcpScope({ project: resolvedProject, user_id, session_id, path: preflight.trust_state.root_path });
|
|
5765
|
+
if (preflight.repo_grounded && preflight.retrieval_route === "local_workspace_fallback") {
|
|
5766
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5767
|
+
query,
|
|
5768
|
+
path: preflight.trust_state.root_path,
|
|
5769
|
+
project: resolvedProject || project,
|
|
5770
|
+
top_k
|
|
5771
|
+
});
|
|
5772
|
+
if (localRetrieval.results.length > 0) {
|
|
5773
|
+
return {
|
|
5774
|
+
content: [{
|
|
5775
|
+
type: "text",
|
|
5776
|
+
text: renderLocalWorkspaceContext({
|
|
5777
|
+
query,
|
|
5778
|
+
project_ref: resolvedProject || null,
|
|
5779
|
+
scope,
|
|
5780
|
+
route: "local_workspace_fallback",
|
|
5781
|
+
hits: localRetrieval.results,
|
|
5782
|
+
diagnostics: localRetrieval.diagnostics,
|
|
5783
|
+
warnings: Array.from(/* @__PURE__ */ new Set([...preflight.warnings, ...localRetrieval.warnings]))
|
|
5784
|
+
})
|
|
5785
|
+
}]
|
|
5786
|
+
};
|
|
5787
|
+
}
|
|
5788
|
+
}
|
|
5789
|
+
if (preflight.repo_grounded && resolvedProject) {
|
|
5790
|
+
const queryResult2 = await queryWithDegradedFallback({
|
|
5791
|
+
project: resolvedProject,
|
|
5792
|
+
query,
|
|
5793
|
+
top_k,
|
|
5794
|
+
include_memories: false,
|
|
5795
|
+
include_graph,
|
|
5796
|
+
user_id: user_id || scope.userId,
|
|
5797
|
+
session_id: session_id || scope.sessionId,
|
|
5798
|
+
source_ids: preflight.matched_source_ids
|
|
5799
|
+
});
|
|
5800
|
+
const repoResults = filterProjectRepoResults(queryResult2.response.results || [], preflight.matched_source_ids);
|
|
5801
|
+
if (repoResults.length > 0) {
|
|
5802
|
+
const scopedResponse = { ...queryResult2.response, results: repoResults };
|
|
5803
|
+
const header2 = `Found ${repoResults.length} repo-grounded result(s) (${scopedResponse.meta.latency_ms}ms${scopedResponse.meta.cache_hit ? ", cached" : ""}, route=project_repo, workspace=${preflight.trust_state.workspace_id}, project=${resolvedProject}, user=${scope.userId}, session=${scope.sessionId}):
|
|
5804
|
+
|
|
5805
|
+
`;
|
|
5806
|
+
const suffix2 = [
|
|
5807
|
+
`[diagnostics] identity_source=${preflight.trust_state.identity_source} retrieval_readiness=${preflight.retrieval_readiness} retrieval_route=project_repo`,
|
|
5808
|
+
queryResult2.degraded_mode ? `[degraded_mode=true] ${queryResult2.degraded_reason}
|
|
5809
|
+
Recommendation: ${queryResult2.recommendation}` : "",
|
|
5810
|
+
preflight.warnings.length ? `[warnings]
|
|
5811
|
+
${preflight.warnings.join("\n")}` : ""
|
|
5812
|
+
].filter(Boolean).join("\n\n");
|
|
5813
|
+
return { content: [{ type: "text", text: `${header2}${scopedResponse.context}${suffix2 ? `
|
|
5814
|
+
|
|
5815
|
+
${suffix2}` : ""}` }] };
|
|
5816
|
+
}
|
|
5817
|
+
}
|
|
5818
|
+
if (preflight.repo_grounded) {
|
|
5819
|
+
if (resolvedProject && include_memories !== false) {
|
|
5820
|
+
const memoryRescue = await runContextQueryMemoryRescue({
|
|
5821
|
+
project: resolvedProject,
|
|
5822
|
+
query,
|
|
5823
|
+
user_id: user_id ? scope.userId : void 0,
|
|
5824
|
+
session_id: session_id ? scope.sessionId : void 0,
|
|
5825
|
+
top_k
|
|
5826
|
+
});
|
|
5827
|
+
if (memoryRescue.results.length && memoryRescue.rescue_mode) {
|
|
5828
|
+
return {
|
|
5829
|
+
content: [{
|
|
5830
|
+
type: "text",
|
|
5831
|
+
text: `${renderContextQueryMemoryRescue({
|
|
5832
|
+
project: resolvedProject,
|
|
5833
|
+
query,
|
|
5834
|
+
scope,
|
|
5835
|
+
results: memoryRescue.results,
|
|
5836
|
+
rescue_mode: memoryRescue.rescue_mode
|
|
5837
|
+
})}
|
|
5838
|
+
|
|
5839
|
+
[diagnostics]
|
|
5840
|
+
retrieval_route=memory_only retrieval_readiness=${preflight.retrieval_readiness} workspace=${preflight.trust_state.workspace_id}`
|
|
5841
|
+
}]
|
|
5842
|
+
};
|
|
5843
|
+
}
|
|
5844
|
+
}
|
|
5845
|
+
return {
|
|
5846
|
+
content: [{
|
|
5847
|
+
type: "text",
|
|
5848
|
+
text: `No relevant repo-grounded context found.
|
|
5849
|
+
|
|
5850
|
+
[diagnostics]
|
|
5851
|
+
workspace=${preflight.trust_state.workspace_id} identity_source=${preflight.trust_state.identity_source} retrieval_readiness=${preflight.retrieval_readiness} retrieval_route=${preflight.retrieval_route}`
|
|
5852
|
+
}]
|
|
5853
|
+
};
|
|
5854
|
+
}
|
|
5855
|
+
const automaticMode = !preflight.repo_grounded && include_memories !== false && include_graph !== true && !(chunk_types && chunk_types.length > 0) && max_tokens === void 0 && runtimeClient;
|
|
5294
5856
|
if (automaticMode) {
|
|
5295
5857
|
try {
|
|
5296
5858
|
const prepared = await prepareAutomaticQuery({
|
|
@@ -5298,7 +5860,8 @@ server.tool(
|
|
|
5298
5860
|
query,
|
|
5299
5861
|
top_k,
|
|
5300
5862
|
user_id,
|
|
5301
|
-
session_id
|
|
5863
|
+
session_id,
|
|
5864
|
+
path: preflight.trust_state.root_path
|
|
5302
5865
|
});
|
|
5303
5866
|
if (!prepared.items.length) {
|
|
5304
5867
|
const memoryRescue = include_memories !== false ? await runContextQueryMemoryRescue({
|
|
@@ -5351,8 +5914,8 @@ ${prepared.context}${warnings}`
|
|
|
5351
5914
|
top_k,
|
|
5352
5915
|
include_memories: include_memories === true,
|
|
5353
5916
|
include_graph,
|
|
5354
|
-
user_id: user_id ||
|
|
5355
|
-
session_id: session_id ||
|
|
5917
|
+
user_id: user_id || scope.userId,
|
|
5918
|
+
session_id: session_id || scope.sessionId
|
|
5356
5919
|
});
|
|
5357
5920
|
const response2 = queryResult2.response;
|
|
5358
5921
|
if (response2.results.length === 0) {
|
|
@@ -5404,8 +5967,8 @@ ${automaticWarning}${suffix2}` }] };
|
|
|
5404
5967
|
top_k,
|
|
5405
5968
|
include_memories: include_memories === true,
|
|
5406
5969
|
include_graph,
|
|
5407
|
-
user_id: user_id ||
|
|
5408
|
-
session_id: session_id ||
|
|
5970
|
+
user_id: user_id || scope.userId,
|
|
5971
|
+
session_id: session_id || scope.sessionId
|
|
5409
5972
|
});
|
|
5410
5973
|
const response = queryResult.response;
|
|
5411
5974
|
if (response.results.length === 0) {
|
|
@@ -5483,7 +6046,7 @@ server.tool(
|
|
|
5483
6046
|
);
|
|
5484
6047
|
server.tool(
|
|
5485
6048
|
"memory.search",
|
|
5486
|
-
"
|
|
6049
|
+
"Call this before answering questions about user history (preferences, prior decisions, past tasks, or 'what did we discuss/search'). Returns memory context you would not otherwise know.",
|
|
5487
6050
|
{
|
|
5488
6051
|
project: z.string().optional().describe("Project name or slug"),
|
|
5489
6052
|
query: z.string().describe("What to search for"),
|
|
@@ -6462,6 +7025,7 @@ var CODE_EXTENSIONS = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx", "py", "
|
|
|
6462
7025
|
function extractSignature(filePath, content) {
|
|
6463
7026
|
const lines = content.split("\n");
|
|
6464
7027
|
const signature = /* @__PURE__ */ new Set([`// File: ${filePath}`]);
|
|
7028
|
+
signature.add(`relative_path:${filePath.replace(/\\/g, "/").toLowerCase()}`);
|
|
6465
7029
|
for (const segment of filePath.split(/[\\/._-]+/).filter(Boolean)) {
|
|
6466
7030
|
signature.add(`path:${segment}`);
|
|
6467
7031
|
}
|
|
@@ -6477,29 +7041,39 @@ function extractSignature(filePath, content) {
|
|
|
6477
7041
|
if (routeMatches) {
|
|
6478
7042
|
for (const match of routeMatches.slice(0, 3)) signature.add(`route:${match.slice(0, 120)}`);
|
|
6479
7043
|
}
|
|
7044
|
+
const envKeyMatches = trimmed.match(/[A-Z][A-Z0-9_]{2,}/g);
|
|
7045
|
+
if (envKeyMatches) {
|
|
7046
|
+
for (const match of envKeyMatches.slice(0, 3)) signature.add(`env:${match}`);
|
|
7047
|
+
}
|
|
6480
7048
|
if (/^(import|from|require|use |pub use )/.test(trimmed)) {
|
|
6481
7049
|
signature.add(trimmed.slice(0, 120));
|
|
6482
7050
|
continue;
|
|
6483
7051
|
}
|
|
6484
7052
|
if (/^(export|async function|function|class|interface|type |const |let |def |pub fn |fn |struct |impl |enum )/.test(trimmed)) {
|
|
6485
7053
|
signature.add(trimmed.slice(0, 120));
|
|
7054
|
+
const exportMatch = trimmed.match(/export\s+(?:const|function|class|type|interface)\s+([A-Za-z0-9_$]+)/);
|
|
7055
|
+
if (exportMatch?.[1]) signature.add(`export:${exportMatch[1]}`);
|
|
6486
7056
|
continue;
|
|
6487
7057
|
}
|
|
6488
7058
|
if (trimmed.startsWith("@") || trimmed.startsWith("#[")) {
|
|
6489
7059
|
signature.add(trimmed.slice(0, 80));
|
|
6490
7060
|
}
|
|
6491
7061
|
}
|
|
6492
|
-
for (
|
|
6493
|
-
const trimmed =
|
|
7062
|
+
for (let index = 60; index < lines.length; index += 1) {
|
|
7063
|
+
const trimmed = lines[index].trim();
|
|
6494
7064
|
if (/^(export (default |async )?function|export (default )?class|export const|export type|export interface|async function|function |class |def |pub fn |fn )/.test(trimmed)) {
|
|
6495
7065
|
signature.add(trimmed.slice(0, 120));
|
|
7066
|
+
const surrounding = lines.slice(index, Math.min(lines.length, index + 3)).map((line) => line.trim()).filter(Boolean);
|
|
7067
|
+
for (const line of surrounding) signature.add(line.slice(0, 120));
|
|
6496
7068
|
continue;
|
|
6497
7069
|
}
|
|
6498
7070
|
if (/^[A-Za-z0-9_$]+\s*[:=]\s*["'`][^"'`]{3,}["'`]/.test(trimmed)) {
|
|
6499
7071
|
signature.add(trimmed.slice(0, 120));
|
|
6500
7072
|
}
|
|
7073
|
+
const exportMatch = trimmed.match(/export\s+(?:const|function|class|type|interface)\s+([A-Za-z0-9_$]+)/);
|
|
7074
|
+
if (exportMatch?.[1]) signature.add(`export:${exportMatch[1]}`);
|
|
6501
7075
|
}
|
|
6502
|
-
return Array.from(signature).join("\n").slice(0,
|
|
7076
|
+
return Array.from(signature).join("\n").slice(0, 3200);
|
|
6503
7077
|
}
|
|
6504
7078
|
server.tool(
|
|
6505
7079
|
"code.search_semantic",
|
|
@@ -6766,7 +7340,7 @@ server.tool(
|
|
|
6766
7340
|
);
|
|
6767
7341
|
server.tool(
|
|
6768
7342
|
"search",
|
|
6769
|
-
"
|
|
7343
|
+
"Primary retrieval alias. Call this whenever the user asks to find or recall context: use `query` for semantic retrieval, `id` for exact memory fetch, or both for hybrid recall.",
|
|
6770
7344
|
{
|
|
6771
7345
|
project: z.string().optional().describe("Project name or slug"),
|
|
6772
7346
|
query: z.string().optional().describe("Semantic retrieval query"),
|
|
@@ -6996,7 +7570,7 @@ server.tool(
|
|
|
6996
7570
|
);
|
|
6997
7571
|
server.tool(
|
|
6998
7572
|
"index",
|
|
6999
|
-
"
|
|
7573
|
+
"Administrative indexing tool. Call this when retrieval is stale/missing or the user asks to connect new data. Use action='source' to add GitHub/web/pdf/local/slack/video, action='workspace' to refresh local workspace metadata.",
|
|
7000
7574
|
{
|
|
7001
7575
|
action: z.enum(["source", "workspace"]).default("source"),
|
|
7002
7576
|
project: z.string().optional(),
|
|
@@ -7077,7 +7651,7 @@ server.tool(
|
|
|
7077
7651
|
);
|
|
7078
7652
|
server.tool(
|
|
7079
7653
|
"remember",
|
|
7080
|
-
"
|
|
7654
|
+
"Call this whenever the user states a durable preference, decision, instruction, or personal/project fact that should persist across sessions. Save proactively without waiting for an explicit 'remember this'.",
|
|
7081
7655
|
{
|
|
7082
7656
|
project: z.string().optional(),
|
|
7083
7657
|
content: z.string().describe("Memory content"),
|
|
@@ -7156,7 +7730,7 @@ server.tool(
|
|
|
7156
7730
|
);
|
|
7157
7731
|
server.tool(
|
|
7158
7732
|
"learn",
|
|
7159
|
-
"Unified
|
|
7733
|
+
"Unified ingestion entrypoint. Call this when the user asks to import knowledge: mode='conversation' for chat logs, mode='text' for raw text, mode='source' for external sources to index. Prefer this over legacy compatibility tools.",
|
|
7160
7734
|
{
|
|
7161
7735
|
mode: z.enum(["conversation", "text", "source"]).describe("What kind of learning to perform"),
|
|
7162
7736
|
project: z.string().optional(),
|
|
@@ -7289,6 +7863,8 @@ if (process.argv[1] && /server\.(mjs|cjs|js|ts)$/.test(process.argv[1])) {
|
|
|
7289
7863
|
export {
|
|
7290
7864
|
canonicalizeWorkspacePath,
|
|
7291
7865
|
chooseWorkspaceProjectSource,
|
|
7866
|
+
classifyProjectRepoReadiness,
|
|
7867
|
+
classifyRepoGroundedQuery,
|
|
7292
7868
|
classifyWorkspaceHealth,
|
|
7293
7869
|
createMcpServer,
|
|
7294
7870
|
createWhisperMcpClient,
|
|
@@ -7296,5 +7872,6 @@ export {
|
|
|
7296
7872
|
extractSignature,
|
|
7297
7873
|
renderScopedMcpConfig,
|
|
7298
7874
|
resolveForgetQueryCandidates,
|
|
7875
|
+
resolveWorkspaceIdentity,
|
|
7299
7876
|
runSearchCodeSearch
|
|
7300
7877
|
};
|