@usewhisper/mcp-server 2.10.0 → 2.12.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 +849 -156
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -3799,6 +3799,7 @@ var API_KEY = process.env.WHISPER_API_KEY || "";
|
|
|
3799
3799
|
var DEFAULT_PROJECT = process.env.WHISPER_PROJECT || "";
|
|
3800
3800
|
var BASE_URL = process.env.WHISPER_BASE_URL;
|
|
3801
3801
|
var RUNTIME_MODE = (process.env.WHISPER_MCP_MODE || "remote").toLowerCase();
|
|
3802
|
+
var MCP_RETRIEVAL_PRECISION_V1_DEFAULT = /^true$/i.test(process.env.MCP_RETRIEVAL_PRECISION_V1_DEFAULT || "false");
|
|
3802
3803
|
var CLI_ARGS = process.argv.slice(2);
|
|
3803
3804
|
var IS_MANAGEMENT_ONLY = CLI_ARGS.includes("--print-tool-map") || CLI_ARGS[0] === "scope";
|
|
3804
3805
|
function createWhisperMcpClient(options) {
|
|
@@ -3833,6 +3834,16 @@ var server = new McpServer({
|
|
|
3833
3834
|
function createMcpServer() {
|
|
3834
3835
|
return server;
|
|
3835
3836
|
}
|
|
3837
|
+
var WORKSPACE_HEALTH_VALUES = ["unbound", "unindexed", "stale", "drifted", "healthy"];
|
|
3838
|
+
var RETRIEVAL_READINESS_VALUES = [
|
|
3839
|
+
"no_project",
|
|
3840
|
+
"project_unverified",
|
|
3841
|
+
"project_bound_no_repo_source",
|
|
3842
|
+
"project_repo_source_stale",
|
|
3843
|
+
"project_repo_source_ready",
|
|
3844
|
+
"local_fallback"
|
|
3845
|
+
];
|
|
3846
|
+
var RETRIEVAL_ROUTE_VALUES = ["project_repo", "local_workspace_fallback", "memory_only", "none"];
|
|
3836
3847
|
var STATE_DIR = join(homedir(), ".whisper-mcp");
|
|
3837
3848
|
var STATE_PATH = join(STATE_DIR, "state.json");
|
|
3838
3849
|
var AUDIT_LOG_PATH = join(STATE_DIR, "forget-audit.log");
|
|
@@ -3874,6 +3885,55 @@ var ALIAS_TOOL_MAP = [
|
|
|
3874
3885
|
{ alias: "learn", target: "context.add_text | context.add_source | context.add_document" },
|
|
3875
3886
|
{ alias: "share_context", target: "context.share" }
|
|
3876
3887
|
];
|
|
3888
|
+
var MCP_RETRIEVAL_PROFILE_VALUES = ["legacy", "precision_v1"];
|
|
3889
|
+
var RECOMMENDED_NEXT_CALL_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
3890
|
+
"index.workspace_resolve",
|
|
3891
|
+
"context.list_projects",
|
|
3892
|
+
"index.workspace_status",
|
|
3893
|
+
"index.workspace_run",
|
|
3894
|
+
"search_code",
|
|
3895
|
+
"grep",
|
|
3896
|
+
"context.list_sources",
|
|
3897
|
+
"index.local_scan_ingest",
|
|
3898
|
+
"context.get_relevant"
|
|
3899
|
+
]);
|
|
3900
|
+
var UNTRUSTED_CODEBASE_HEALTH = /* @__PURE__ */ new Set(["unbound", "unindexed"]);
|
|
3901
|
+
var UNTRUSTED_CODEBASE_PROJECT_READINESS = /* @__PURE__ */ new Set([
|
|
3902
|
+
"project_unverified",
|
|
3903
|
+
"project_bound_no_repo_source"
|
|
3904
|
+
]);
|
|
3905
|
+
function sanitizeRecommendedNextCalls(nextCalls) {
|
|
3906
|
+
return Array.from(
|
|
3907
|
+
new Set(
|
|
3908
|
+
nextCalls.map((entry) => String(entry || "").trim()).filter((entry) => RECOMMENDED_NEXT_CALL_ALLOWLIST.has(entry))
|
|
3909
|
+
)
|
|
3910
|
+
);
|
|
3911
|
+
}
|
|
3912
|
+
function resolveMcpRetrievalProfile(requested) {
|
|
3913
|
+
if (requested && MCP_RETRIEVAL_PROFILE_VALUES.includes(requested)) {
|
|
3914
|
+
return requested;
|
|
3915
|
+
}
|
|
3916
|
+
return MCP_RETRIEVAL_PRECISION_V1_DEFAULT ? "precision_v1" : "legacy";
|
|
3917
|
+
}
|
|
3918
|
+
function shouldAbstainForCodebaseTrust(args) {
|
|
3919
|
+
if (args.retrievalProfile !== "precision_v1" || !args.codebaseIntent) return false;
|
|
3920
|
+
return UNTRUSTED_CODEBASE_HEALTH.has(args.preflight.trust_state.health) || UNTRUSTED_CODEBASE_PROJECT_READINESS.has(args.preflight.project_retrieval_readiness);
|
|
3921
|
+
}
|
|
3922
|
+
function buildCodebaseTrustAbstainPayload(args) {
|
|
3923
|
+
return {
|
|
3924
|
+
status: "abstained",
|
|
3925
|
+
reason: "stale_index",
|
|
3926
|
+
message: args.preflight.warnings[0] || "Codebase retrieval is not trusted yet for this workspace/project. Resolve index trust before answering.",
|
|
3927
|
+
query: args.query,
|
|
3928
|
+
trust_state: args.preflight.trust_state,
|
|
3929
|
+
retrieval_readiness: args.preflight.retrieval_readiness,
|
|
3930
|
+
project_retrieval_readiness: args.preflight.project_retrieval_readiness,
|
|
3931
|
+
retrieval_route: args.preflight.retrieval_route,
|
|
3932
|
+
retrieval_profile: args.retrievalProfile,
|
|
3933
|
+
warnings: args.preflight.warnings,
|
|
3934
|
+
recommended_next_calls: sanitizeRecommendedNextCalls(args.preflight.recommended_next_calls)
|
|
3935
|
+
};
|
|
3936
|
+
}
|
|
3877
3937
|
function ensureStateDir() {
|
|
3878
3938
|
if (!existsSync(STATE_DIR)) {
|
|
3879
3939
|
mkdirSync(STATE_DIR, { recursive: true });
|
|
@@ -3914,6 +3974,21 @@ function classifyWorkspaceHealth(args) {
|
|
|
3914
3974
|
if (ageHours > (args.max_staleness_hours ?? 168)) return "stale";
|
|
3915
3975
|
return "healthy";
|
|
3916
3976
|
}
|
|
3977
|
+
function resolveWorkspaceIdentity(args) {
|
|
3978
|
+
const rootPath = canonicalizeWorkspacePath(args?.path, args?.cwd);
|
|
3979
|
+
const derivedWorkspaceId = getWorkspaceIdForPath(rootPath);
|
|
3980
|
+
const requestedWorkspaceId = args?.workspace_id?.trim();
|
|
3981
|
+
if (requestedWorkspaceId && requestedWorkspaceId !== derivedWorkspaceId) {
|
|
3982
|
+
throw new Error(
|
|
3983
|
+
`workspace_id '${requestedWorkspaceId}' does not match canonical workspace '${derivedWorkspaceId}' for ${rootPath}.`
|
|
3984
|
+
);
|
|
3985
|
+
}
|
|
3986
|
+
return {
|
|
3987
|
+
workspace_id: derivedWorkspaceId,
|
|
3988
|
+
root_path: rootPath,
|
|
3989
|
+
identity_source: args?.path?.trim() ? "path_canonical" : "cwd_canonical"
|
|
3990
|
+
};
|
|
3991
|
+
}
|
|
3917
3992
|
function getWorkspaceId(workspaceId) {
|
|
3918
3993
|
if (workspaceId?.trim()) return workspaceId.trim();
|
|
3919
3994
|
const seed = `${canonicalizeWorkspacePath(process.cwd())}|${API_KEY.slice(0, 12) || "anon"}`;
|
|
@@ -3925,6 +4000,143 @@ function getWorkspaceIdForPath(path, workspaceId) {
|
|
|
3925
4000
|
const seed = `${canonicalizeWorkspacePath(path)}|${API_KEY.slice(0, 12) || "anon"}`;
|
|
3926
4001
|
return createHash("sha256").update(seed).digest("hex").slice(0, 20);
|
|
3927
4002
|
}
|
|
4003
|
+
function normalizeLoosePath(value) {
|
|
4004
|
+
if (!value || !String(value).trim()) return null;
|
|
4005
|
+
return canonicalizeWorkspacePath(String(value));
|
|
4006
|
+
}
|
|
4007
|
+
function normalizeRepoName(value) {
|
|
4008
|
+
if (!value || !String(value).trim()) return null;
|
|
4009
|
+
return String(value).trim().replace(/\.git$/i, "").toLowerCase();
|
|
4010
|
+
}
|
|
4011
|
+
function parseGitHubRemote(remote) {
|
|
4012
|
+
if (!remote) return null;
|
|
4013
|
+
const normalized = remote.trim().replace(/\.git$/i, "");
|
|
4014
|
+
const httpsMatch = normalized.match(/github\.com[/:]([^/:\s]+)\/([^/\s]+)$/i);
|
|
4015
|
+
if (httpsMatch) {
|
|
4016
|
+
return { owner: httpsMatch[1].toLowerCase(), repo: httpsMatch[2].toLowerCase() };
|
|
4017
|
+
}
|
|
4018
|
+
const sshMatch = normalized.match(/git@github\.com:([^/:\s]+)\/([^/\s]+)$/i);
|
|
4019
|
+
if (sshMatch) {
|
|
4020
|
+
return { owner: sshMatch[1].toLowerCase(), repo: sshMatch[2].toLowerCase() };
|
|
4021
|
+
}
|
|
4022
|
+
return null;
|
|
4023
|
+
}
|
|
4024
|
+
function getGitRemoteUrl(searchPath) {
|
|
4025
|
+
const root = searchPath || process.cwd();
|
|
4026
|
+
const result = spawnSync("git", ["-C", root, "config", "--get", "remote.origin.url"], { encoding: "utf-8" });
|
|
4027
|
+
if (result.status !== 0) return void 0;
|
|
4028
|
+
const out = String(result.stdout || "").trim();
|
|
4029
|
+
return out || void 0;
|
|
4030
|
+
}
|
|
4031
|
+
function getGitBranch(searchPath) {
|
|
4032
|
+
const root = searchPath || process.cwd();
|
|
4033
|
+
const result = spawnSync("git", ["-C", root, "rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf-8" });
|
|
4034
|
+
if (result.status !== 0) return void 0;
|
|
4035
|
+
const out = String(result.stdout || "").trim();
|
|
4036
|
+
return out || void 0;
|
|
4037
|
+
}
|
|
4038
|
+
function sourceStatusLooksReady(status) {
|
|
4039
|
+
const normalized = String(status || "").trim().toLowerCase();
|
|
4040
|
+
if (!normalized) return true;
|
|
4041
|
+
if (["ready", "indexed", "completed", "complete", "active", "synced", "success"].includes(normalized)) return true;
|
|
4042
|
+
if (["processing", "queued", "syncing", "pending", "creating", "indexing", "error", "failed"].includes(normalized)) {
|
|
4043
|
+
return false;
|
|
4044
|
+
}
|
|
4045
|
+
return !normalized.includes("error") && !normalized.includes("fail") && !normalized.includes("queue");
|
|
4046
|
+
}
|
|
4047
|
+
function sourceMatchesWorkspacePath(source, rootPath) {
|
|
4048
|
+
const config = source.config || {};
|
|
4049
|
+
const candidates = [
|
|
4050
|
+
config.path,
|
|
4051
|
+
config.root_path,
|
|
4052
|
+
config.workspace_path,
|
|
4053
|
+
config.workspacePath,
|
|
4054
|
+
config.local_path,
|
|
4055
|
+
config.file_path,
|
|
4056
|
+
config.directory
|
|
4057
|
+
].map((value) => normalizeLoosePath(typeof value === "string" ? value : null)).filter(Boolean);
|
|
4058
|
+
return candidates.some((candidate) => candidate === rootPath || rootPath.startsWith(`${candidate}/`) || candidate.startsWith(`${rootPath}/`));
|
|
4059
|
+
}
|
|
4060
|
+
function sourceMatchesWorkspaceRepo(source, repoIdentity, branch) {
|
|
4061
|
+
if (!repoIdentity) return false;
|
|
4062
|
+
const config = source.config || {};
|
|
4063
|
+
const owner = normalizeRepoName(config.owner || config.org || config.organization || null);
|
|
4064
|
+
const repo = normalizeRepoName(config.repo || config.repository || config.name || null);
|
|
4065
|
+
const sourceUrlRepo = parseGitHubRemote(typeof config.url === "string" ? config.url : null);
|
|
4066
|
+
const branchValue = normalizeRepoName(config.branch || config.ref || config.default_branch || null);
|
|
4067
|
+
const ownerMatches = owner === repoIdentity.owner || sourceUrlRepo?.owner === repoIdentity.owner;
|
|
4068
|
+
const repoMatches = repo === repoIdentity.repo || sourceUrlRepo?.repo === repoIdentity.repo;
|
|
4069
|
+
if (!ownerMatches || !repoMatches) return false;
|
|
4070
|
+
if (!branchValue || !branch) return true;
|
|
4071
|
+
return branchValue === normalizeRepoName(branch);
|
|
4072
|
+
}
|
|
4073
|
+
function classifyProjectRepoReadiness(args) {
|
|
4074
|
+
const remoteIdentity = parseGitHubRemote(args.remote_url || null);
|
|
4075
|
+
const matchedSources = args.sources.filter(
|
|
4076
|
+
(source) => sourceMatchesWorkspacePath(source, args.root_path) || sourceMatchesWorkspaceRepo(source, remoteIdentity, args.branch || void 0)
|
|
4077
|
+
);
|
|
4078
|
+
if (matchedSources.length === 0) {
|
|
4079
|
+
return {
|
|
4080
|
+
retrieval_readiness: "project_bound_no_repo_source",
|
|
4081
|
+
matched_sources: [],
|
|
4082
|
+
matched_source_ids: [],
|
|
4083
|
+
warnings: [`No verified local or GitHub source matches ${args.root_path}.`]
|
|
4084
|
+
};
|
|
4085
|
+
}
|
|
4086
|
+
const readySources = matchedSources.filter((source) => sourceStatusLooksReady(source.status));
|
|
4087
|
+
if (readySources.length === 0) {
|
|
4088
|
+
return {
|
|
4089
|
+
retrieval_readiness: "project_repo_source_stale",
|
|
4090
|
+
matched_sources: matchedSources,
|
|
4091
|
+
matched_source_ids: matchedSources.map((source) => source.id),
|
|
4092
|
+
warnings: [
|
|
4093
|
+
`Matching repo sources are present but not ready (${matchedSources.map((source) => `${source.name}:${source.status}`).join(", ")}).`
|
|
4094
|
+
]
|
|
4095
|
+
};
|
|
4096
|
+
}
|
|
4097
|
+
return {
|
|
4098
|
+
retrieval_readiness: "project_repo_source_ready",
|
|
4099
|
+
matched_sources: readySources,
|
|
4100
|
+
matched_source_ids: readySources.map((source) => source.id),
|
|
4101
|
+
warnings: []
|
|
4102
|
+
};
|
|
4103
|
+
}
|
|
4104
|
+
function classifyRepoGroundedQuery(args) {
|
|
4105
|
+
const query = args.query.toLowerCase();
|
|
4106
|
+
let score = 0;
|
|
4107
|
+
if (/(^|[\s"'])where is\b|which file|what file|show me|wiring|handler|middleware|implementation|route|endpoint|function|class|module|repo|workspace|code|project/.test(query)) {
|
|
4108
|
+
score += 1;
|
|
4109
|
+
}
|
|
4110
|
+
if (/\bauth\b|\burl\b|\bcookie\b|\bsession\b|\bapi\b/.test(query)) {
|
|
4111
|
+
score += 1;
|
|
4112
|
+
}
|
|
4113
|
+
if (/\berror\b|\berrors\b|\bexception\b|\bexceptions\b/.test(query)) {
|
|
4114
|
+
score += 1;
|
|
4115
|
+
}
|
|
4116
|
+
if (/\bretry\b/.test(query)) {
|
|
4117
|
+
score += 1;
|
|
4118
|
+
}
|
|
4119
|
+
if (/\bflow\b|\blogic\b|\bstack\b|\btrace\b|\blogging\b|\bfailure\b|\bfailures\b|\bthrow\b|\bcatch\b|\bdebug\b/.test(query)) {
|
|
4120
|
+
score += 1;
|
|
4121
|
+
}
|
|
4122
|
+
if (/[A-Za-z0-9/_-]+\.[A-Za-z0-9]+/.test(args.query) || /\/[A-Za-z0-9._~!$&'()*+,;=:@%/-]{2,}/.test(args.query)) {
|
|
4123
|
+
score += 2;
|
|
4124
|
+
}
|
|
4125
|
+
if (/[A-Za-z_$][A-Za-z0-9_$-]*\(/.test(args.query) || /\b[A-Z][a-z0-9]+[A-Z][A-Za-z0-9]*\b/.test(args.query)) {
|
|
4126
|
+
score += 1;
|
|
4127
|
+
}
|
|
4128
|
+
if ((args.chunk_types || []).some((chunkType) => ["code", "function", "class", "config", "schema", "api_spec"].includes(chunkType))) {
|
|
4129
|
+
score += 2;
|
|
4130
|
+
}
|
|
4131
|
+
const changedTokens = new Set((args.changed_path_tokens || []).map((token) => token.toLowerCase()));
|
|
4132
|
+
if (changedTokens.size > 0) {
|
|
4133
|
+
const queryTokens = tokenizeQueryForLexicalRescue(args.query);
|
|
4134
|
+
if (queryTokens.some((token) => changedTokens.has(token.toLowerCase()))) {
|
|
4135
|
+
score += 1;
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
return score >= 2;
|
|
4139
|
+
}
|
|
3928
4140
|
function clamp012(value) {
|
|
3929
4141
|
if (Number.isNaN(value)) return 0;
|
|
3930
4142
|
if (value < 0) return 0;
|
|
@@ -4038,9 +4250,9 @@ async function resolveProjectDescriptor(projectRef) {
|
|
|
4038
4250
|
}
|
|
4039
4251
|
}
|
|
4040
4252
|
function getWorkspaceRecommendedNextCalls(health) {
|
|
4041
|
-
if (health === "unbound") return ["index.workspace_resolve", "context.list_projects", "index.workspace_status"];
|
|
4042
|
-
if (health === "unindexed") return ["index.workspace_status", "index.workspace_run", "search_code", "grep"];
|
|
4043
|
-
if (health === "stale" || health === "drifted") return ["index.workspace_status", "index.workspace_run", "grep", "search_code"];
|
|
4253
|
+
if (health === "unbound") return sanitizeRecommendedNextCalls(["index.workspace_resolve", "context.list_projects", "index.workspace_status"]);
|
|
4254
|
+
if (health === "unindexed") return sanitizeRecommendedNextCalls(["index.workspace_status", "index.workspace_run", "search_code", "grep"]);
|
|
4255
|
+
if (health === "stale" || health === "drifted") return sanitizeRecommendedNextCalls(["index.workspace_status", "index.workspace_run", "grep", "search_code"]);
|
|
4044
4256
|
return [];
|
|
4045
4257
|
}
|
|
4046
4258
|
function getWorkspaceWarnings(args) {
|
|
@@ -4066,8 +4278,9 @@ function getWorkspaceWarnings(args) {
|
|
|
4066
4278
|
return [];
|
|
4067
4279
|
}
|
|
4068
4280
|
async function resolveWorkspaceTrust(args) {
|
|
4069
|
-
const
|
|
4070
|
-
const
|
|
4281
|
+
const identity = resolveWorkspaceIdentity({ path: args.path, workspace_id: args.workspace_id });
|
|
4282
|
+
const rootPath = identity.root_path;
|
|
4283
|
+
const workspaceId = identity.workspace_id;
|
|
4071
4284
|
const state = loadState();
|
|
4072
4285
|
const workspace = getWorkspaceState(state, workspaceId);
|
|
4073
4286
|
let mutated = false;
|
|
@@ -4125,6 +4338,7 @@ async function resolveWorkspaceTrust(args) {
|
|
|
4125
4338
|
return {
|
|
4126
4339
|
workspace_id: workspaceId,
|
|
4127
4340
|
root_path: rootPath,
|
|
4341
|
+
identity_source: identity.identity_source,
|
|
4128
4342
|
project_ref: projectRef,
|
|
4129
4343
|
project_id: projectId,
|
|
4130
4344
|
resolved_by: selected.resolved_by,
|
|
@@ -4156,6 +4370,99 @@ async function resolveProjectRef(explicit) {
|
|
|
4156
4370
|
return void 0;
|
|
4157
4371
|
}
|
|
4158
4372
|
}
|
|
4373
|
+
function collectGitChangedPathTokens(searchPath) {
|
|
4374
|
+
const root = searchPath || process.cwd();
|
|
4375
|
+
const result = spawnSync("git", ["-C", root, "status", "--porcelain"], { encoding: "utf-8" });
|
|
4376
|
+
if (result.status !== 0) return [];
|
|
4377
|
+
const lines = String(result.stdout || "").split("\n").map((line) => line.trim()).filter(Boolean);
|
|
4378
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
4379
|
+
for (const line of lines) {
|
|
4380
|
+
const filePath = line.slice(3).split(" -> ").at(-1)?.trim();
|
|
4381
|
+
if (!filePath) continue;
|
|
4382
|
+
for (const token of filePath.split(/[\\/._-]+/).filter((part) => part.length >= 3)) {
|
|
4383
|
+
tokens.add(token.toLowerCase());
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
return Array.from(tokens);
|
|
4387
|
+
}
|
|
4388
|
+
async function inspectProjectRepoSources(args) {
|
|
4389
|
+
if (!args.project_ref) {
|
|
4390
|
+
return {
|
|
4391
|
+
retrieval_readiness: "no_project",
|
|
4392
|
+
matched_sources: [],
|
|
4393
|
+
matched_source_ids: [],
|
|
4394
|
+
warnings: ["No Whisper project is bound to this workspace."]
|
|
4395
|
+
};
|
|
4396
|
+
}
|
|
4397
|
+
try {
|
|
4398
|
+
const sourceData = await whisper.listSources(args.project_ref);
|
|
4399
|
+
const classified = classifyProjectRepoReadiness({
|
|
4400
|
+
sources: sourceData.sources || [],
|
|
4401
|
+
root_path: args.root_path,
|
|
4402
|
+
remote_url: getGitRemoteUrl(args.root_path) || null,
|
|
4403
|
+
branch: getGitBranch(args.root_path) || null
|
|
4404
|
+
});
|
|
4405
|
+
if (classified.retrieval_readiness === "project_bound_no_repo_source") {
|
|
4406
|
+
return {
|
|
4407
|
+
...classified,
|
|
4408
|
+
warnings: [`Project ${args.project_ref} has no verified local or GitHub source for ${args.root_path}.`]
|
|
4409
|
+
};
|
|
4410
|
+
}
|
|
4411
|
+
if (classified.retrieval_readiness === "project_repo_source_stale") {
|
|
4412
|
+
return {
|
|
4413
|
+
...classified,
|
|
4414
|
+
warnings: [
|
|
4415
|
+
`Project ${args.project_ref} has matching repo sources, but none are ready (${classified.matched_sources.map((source) => `${source.name}:${source.status}`).join(", ")}).`
|
|
4416
|
+
]
|
|
4417
|
+
};
|
|
4418
|
+
}
|
|
4419
|
+
return classified;
|
|
4420
|
+
} catch (error) {
|
|
4421
|
+
return {
|
|
4422
|
+
retrieval_readiness: "project_unverified",
|
|
4423
|
+
matched_sources: [],
|
|
4424
|
+
matched_source_ids: [],
|
|
4425
|
+
warnings: [`Could not verify project sources for ${args.project_ref}: ${error.message}`]
|
|
4426
|
+
};
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
async function resolveRepoGroundingPreflight(args) {
|
|
4430
|
+
const trust = await resolveWorkspaceTrust({ path: args.path, workspace_id: args.workspace_id, project: args.project });
|
|
4431
|
+
const repoGrounded = classifyRepoGroundedQuery({
|
|
4432
|
+
query: args.query,
|
|
4433
|
+
chunk_types: args.chunk_types,
|
|
4434
|
+
changed_path_tokens: collectGitChangedPathTokens(trust.root_path)
|
|
4435
|
+
});
|
|
4436
|
+
const sourceVerification = await inspectProjectRepoSources({
|
|
4437
|
+
project_ref: trust.project_ref,
|
|
4438
|
+
root_path: trust.root_path
|
|
4439
|
+
});
|
|
4440
|
+
const warnings = [...trust.warnings, ...sourceVerification.warnings];
|
|
4441
|
+
let retrievalReadiness = sourceVerification.retrieval_readiness;
|
|
4442
|
+
let retrievalRoute = "none";
|
|
4443
|
+
if (repoGrounded) {
|
|
4444
|
+
retrievalRoute = sourceVerification.retrieval_readiness === "project_repo_source_ready" ? "project_repo" : "local_workspace_fallback";
|
|
4445
|
+
if (retrievalRoute === "local_workspace_fallback") {
|
|
4446
|
+
retrievalReadiness = "local_fallback";
|
|
4447
|
+
warnings.push("Using live local workspace retrieval because project-backed repo retrieval is unavailable or unverified.");
|
|
4448
|
+
}
|
|
4449
|
+
}
|
|
4450
|
+
const recommendedNextCalls = [...trust.recommended_next_calls];
|
|
4451
|
+
if (sourceVerification.retrieval_readiness === "project_bound_no_repo_source" || sourceVerification.retrieval_readiness === "project_unverified") {
|
|
4452
|
+
recommendedNextCalls.push("context.list_sources", "index.local_scan_ingest");
|
|
4453
|
+
}
|
|
4454
|
+
return {
|
|
4455
|
+
repo_grounded: repoGrounded,
|
|
4456
|
+
trust_state: trust,
|
|
4457
|
+
project_retrieval_readiness: sourceVerification.retrieval_readiness,
|
|
4458
|
+
retrieval_readiness: retrievalReadiness,
|
|
4459
|
+
retrieval_route: retrievalRoute,
|
|
4460
|
+
matched_sources: sourceVerification.matched_sources,
|
|
4461
|
+
matched_source_ids: sourceVerification.matched_source_ids,
|
|
4462
|
+
warnings: Array.from(new Set(warnings)),
|
|
4463
|
+
recommended_next_calls: sanitizeRecommendedNextCalls(recommendedNextCalls)
|
|
4464
|
+
};
|
|
4465
|
+
}
|
|
4159
4466
|
async function ingestSessionWithSyncFallback(params) {
|
|
4160
4467
|
try {
|
|
4161
4468
|
return await whisper.ingestSession({
|
|
@@ -4182,7 +4489,7 @@ function resolveMcpScope(params) {
|
|
|
4182
4489
|
project: params?.project,
|
|
4183
4490
|
userId: params?.user_id?.trim() || defaultMcpUserId(),
|
|
4184
4491
|
sessionId: params?.session_id?.trim() || cachedMcpSessionId,
|
|
4185
|
-
workspacePath: process.env.WHISPER_WORKSPACE_PATH || process.cwd()
|
|
4492
|
+
workspacePath: canonicalizeWorkspacePath(params?.path || process.env.WHISPER_WORKSPACE_PATH || process.cwd())
|
|
4186
4493
|
};
|
|
4187
4494
|
}
|
|
4188
4495
|
async function prepareAutomaticQuery(params) {
|
|
@@ -4248,7 +4555,9 @@ function buildAbstain(args) {
|
|
|
4248
4555
|
closest_evidence: args.closest_evidence,
|
|
4249
4556
|
warnings: args.warnings || [],
|
|
4250
4557
|
trust_state: args.trust_state,
|
|
4251
|
-
recommended_next_calls:
|
|
4558
|
+
recommended_next_calls: sanitizeRecommendedNextCalls(
|
|
4559
|
+
args.recommended_next_calls || ["index.workspace_status", "index.workspace_run", "grep", "context.get_relevant"]
|
|
4560
|
+
),
|
|
4252
4561
|
diagnostics: {
|
|
4253
4562
|
claims_evaluated: args.claims_evaluated,
|
|
4254
4563
|
evidence_items_found: args.evidence_items_found,
|
|
@@ -4418,6 +4727,9 @@ async function queryWithDegradedFallback(params) {
|
|
|
4418
4727
|
include_graph: params.include_graph,
|
|
4419
4728
|
user_id: params.user_id,
|
|
4420
4729
|
session_id: params.session_id,
|
|
4730
|
+
source_ids: params.source_ids,
|
|
4731
|
+
retrieval_profile: params.retrieval_profile,
|
|
4732
|
+
include_parent_content: params.include_parent_content,
|
|
4421
4733
|
hybrid: true,
|
|
4422
4734
|
rerank: true
|
|
4423
4735
|
});
|
|
@@ -4432,6 +4744,9 @@ async function queryWithDegradedFallback(params) {
|
|
|
4432
4744
|
include_graph: false,
|
|
4433
4745
|
user_id: params.user_id,
|
|
4434
4746
|
session_id: params.session_id,
|
|
4747
|
+
source_ids: params.source_ids,
|
|
4748
|
+
retrieval_profile: params.retrieval_profile,
|
|
4749
|
+
include_parent_content: params.include_parent_content,
|
|
4435
4750
|
hybrid: false,
|
|
4436
4751
|
rerank: false,
|
|
4437
4752
|
vector_weight: 0,
|
|
@@ -4471,9 +4786,15 @@ function collectCodeFiles(rootPath, allowedExts, maxFiles) {
|
|
|
4471
4786
|
}
|
|
4472
4787
|
function tokenizeQueryForLexicalRescue(query) {
|
|
4473
4788
|
const stopWords = /* @__PURE__ */ new Set(["where", "what", "show", "find", "logic", "code", "file", "files", "handled", "handling"]);
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
)
|
|
4789
|
+
const rawTokens = query.toLowerCase().split(/[^a-z0-9/._:-]+/).map((token) => token.trim()).filter(Boolean);
|
|
4790
|
+
const expanded = /* @__PURE__ */ new Set();
|
|
4791
|
+
for (const token of rawTokens) {
|
|
4792
|
+
if (token.length >= 3 && !stopWords.has(token)) expanded.add(token);
|
|
4793
|
+
for (const part of token.split(/[/:._-]+/).filter((value) => value.length >= 3 && !stopWords.has(value))) {
|
|
4794
|
+
expanded.add(part);
|
|
4795
|
+
}
|
|
4796
|
+
}
|
|
4797
|
+
return Array.from(expanded);
|
|
4477
4798
|
}
|
|
4478
4799
|
function buildLexicalRescueResults(args) {
|
|
4479
4800
|
const tokens = tokenizeQueryForLexicalRescue(args.query);
|
|
@@ -4499,16 +4820,17 @@ ${doc.raw_content}`.toLowerCase();
|
|
|
4499
4820
|
async function runSearchCodeSearch(args) {
|
|
4500
4821
|
const topK = args.top_k ?? 10;
|
|
4501
4822
|
const requestedThreshold = args.threshold ?? 0.2;
|
|
4823
|
+
const semanticTopK = Math.min(args.documents.length || topK, Math.max(topK * 3, topK + 5));
|
|
4502
4824
|
const semanticDocuments = args.documents.map((doc) => ({ id: doc.id, content: doc.content }));
|
|
4503
4825
|
const defaultResponse = await args.semantic_search({
|
|
4504
4826
|
query: args.query,
|
|
4505
4827
|
documents: semanticDocuments,
|
|
4506
|
-
top_k:
|
|
4828
|
+
top_k: semanticTopK,
|
|
4507
4829
|
threshold: requestedThreshold
|
|
4508
4830
|
});
|
|
4509
4831
|
if (defaultResponse.results?.length) {
|
|
4510
4832
|
return {
|
|
4511
|
-
results: defaultResponse.results.map((result) => ({ ...result, search_mode: "semantic" })),
|
|
4833
|
+
results: defaultResponse.results.slice(0, topK).map((result) => ({ ...result, search_mode: "semantic" })),
|
|
4512
4834
|
mode: "semantic",
|
|
4513
4835
|
threshold_used: requestedThreshold,
|
|
4514
4836
|
fallback_used: false,
|
|
@@ -4520,12 +4842,12 @@ async function runSearchCodeSearch(args) {
|
|
|
4520
4842
|
const adaptiveResponse = await args.semantic_search({
|
|
4521
4843
|
query: args.query,
|
|
4522
4844
|
documents: semanticDocuments,
|
|
4523
|
-
top_k:
|
|
4845
|
+
top_k: semanticTopK,
|
|
4524
4846
|
threshold: adaptiveThreshold
|
|
4525
4847
|
});
|
|
4526
4848
|
if (adaptiveResponse.results?.length) {
|
|
4527
4849
|
return {
|
|
4528
|
-
results: adaptiveResponse.results.map((result) => ({ ...result, search_mode: "adaptive_semantic" })),
|
|
4850
|
+
results: adaptiveResponse.results.slice(0, topK).map((result) => ({ ...result, search_mode: "adaptive_semantic" })),
|
|
4529
4851
|
mode: "adaptive_semantic",
|
|
4530
4852
|
threshold_used: adaptiveThreshold,
|
|
4531
4853
|
fallback_used: true,
|
|
@@ -4546,36 +4868,8 @@ async function runSearchCodeSearch(args) {
|
|
|
4546
4868
|
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
4869
|
};
|
|
4548
4870
|
}
|
|
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
|
-
}
|
|
4871
|
+
function buildLocalWorkspaceDocuments(rootPath, allowedExts, maxFiles) {
|
|
4872
|
+
const files = collectCodeFiles(rootPath, allowedExts, maxFiles);
|
|
4579
4873
|
const documents = [];
|
|
4580
4874
|
for (const filePath of files) {
|
|
4581
4875
|
try {
|
|
@@ -4587,6 +4881,38 @@ async function runSearchCodeTool(args) {
|
|
|
4587
4881
|
} catch {
|
|
4588
4882
|
}
|
|
4589
4883
|
}
|
|
4884
|
+
return documents;
|
|
4885
|
+
}
|
|
4886
|
+
function extractRelevantSnippet(rawContent, query) {
|
|
4887
|
+
const lines = rawContent.split("\n");
|
|
4888
|
+
const tokens = tokenizeQueryForLexicalRescue(query);
|
|
4889
|
+
let matchIndex = -1;
|
|
4890
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
4891
|
+
const lower = lines[index].toLowerCase();
|
|
4892
|
+
if (tokens.some((token) => lower.includes(token))) {
|
|
4893
|
+
matchIndex = index;
|
|
4894
|
+
break;
|
|
4895
|
+
}
|
|
4896
|
+
}
|
|
4897
|
+
if (matchIndex < 0) {
|
|
4898
|
+
matchIndex = lines.findIndex((line) => line.trim().length > 0);
|
|
4899
|
+
}
|
|
4900
|
+
const start = Math.max(0, matchIndex < 0 ? 0 : matchIndex - 1);
|
|
4901
|
+
const end = Math.min(lines.length, start + 4);
|
|
4902
|
+
const snippet = lines.slice(start, end).join("\n").trim().slice(0, 500);
|
|
4903
|
+
return {
|
|
4904
|
+
snippet,
|
|
4905
|
+
line_start: start + 1,
|
|
4906
|
+
...end > start + 1 ? { line_end: end } : {}
|
|
4907
|
+
};
|
|
4908
|
+
}
|
|
4909
|
+
async function runLocalWorkspaceRetrieval(args) {
|
|
4910
|
+
const identity = resolveWorkspaceIdentity({ path: args.path });
|
|
4911
|
+
const workspace = await resolveWorkspaceTrust({ path: identity.root_path, project: args.project });
|
|
4912
|
+
const allowedExts = args.file_types ? new Set(args.file_types) : CODE_EXTENSIONS;
|
|
4913
|
+
const candidateFiles = collectCodeFiles(identity.root_path, allowedExts, args.max_files ?? 150);
|
|
4914
|
+
const documents = buildLocalWorkspaceDocuments(identity.root_path, allowedExts, args.max_files ?? 150);
|
|
4915
|
+
const sharedWarnings = [...workspace.warnings];
|
|
4590
4916
|
const searchResult = await runSearchCodeSearch({
|
|
4591
4917
|
query: args.query,
|
|
4592
4918
|
documents,
|
|
@@ -4602,29 +4928,140 @@ async function runSearchCodeTool(args) {
|
|
|
4602
4928
|
if (!workspace.grounded_to_workspace && workspace.health !== "unbound" && workspace.health !== "unindexed") {
|
|
4603
4929
|
sharedWarnings.push("Local code search is live, but project-backed retrieval may disagree until the workspace is re-indexed.");
|
|
4604
4930
|
}
|
|
4931
|
+
const documentMap = new Map(documents.map((document) => [document.id, document]));
|
|
4932
|
+
const localResults = searchResult.results.map((result) => {
|
|
4933
|
+
const document = documentMap.get(result.id);
|
|
4934
|
+
const snippet = extractRelevantSnippet(document?.raw_content || result.snippet || "", args.query);
|
|
4935
|
+
return {
|
|
4936
|
+
...result,
|
|
4937
|
+
snippet: snippet.snippet || result.snippet,
|
|
4938
|
+
raw_snippet: snippet.snippet || result.snippet,
|
|
4939
|
+
line_start: snippet.line_start,
|
|
4940
|
+
...snippet.line_end ? { line_end: snippet.line_end } : {},
|
|
4941
|
+
retrieval_method: result.search_mode === "lexical_rescue" ? "lexical" : "semantic"
|
|
4942
|
+
};
|
|
4943
|
+
});
|
|
4605
4944
|
return {
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
results: searchResult.results,
|
|
4610
|
-
count: searchResult.results.length,
|
|
4945
|
+
workspace,
|
|
4946
|
+
documents,
|
|
4947
|
+
results: localResults,
|
|
4611
4948
|
warnings: sharedWarnings,
|
|
4612
4949
|
diagnostics: {
|
|
4613
4950
|
workspace_id: workspace.workspace_id,
|
|
4614
4951
|
root_path: workspace.root_path,
|
|
4615
4952
|
project_ref: workspace.project_ref,
|
|
4616
4953
|
project_id: workspace.project_id,
|
|
4954
|
+
identity_source: workspace.identity_source,
|
|
4617
4955
|
index_health: workspace.health,
|
|
4618
|
-
grounded_to_workspace:
|
|
4956
|
+
grounded_to_workspace: true,
|
|
4619
4957
|
threshold_requested: args.threshold ?? 0.2,
|
|
4620
4958
|
threshold_used: searchResult.threshold_used,
|
|
4621
4959
|
fallback_used: searchResult.fallback_used,
|
|
4622
4960
|
fallback_reason: searchResult.fallback_reason,
|
|
4623
4961
|
search_mode: searchResult.mode,
|
|
4962
|
+
candidate_files_scanned: candidateFiles.length,
|
|
4963
|
+
semantic_candidate_count: documents.length,
|
|
4964
|
+
retrieval_route: "local_workspace",
|
|
4624
4965
|
warnings: sharedWarnings
|
|
4625
4966
|
}
|
|
4626
4967
|
};
|
|
4627
4968
|
}
|
|
4969
|
+
function isLikelyRepoBackedResult(result) {
|
|
4970
|
+
const metadata = result.metadata || {};
|
|
4971
|
+
const retrievalSource = String(result.retrieval_source || "").toLowerCase();
|
|
4972
|
+
if (retrievalSource.includes("memory")) return false;
|
|
4973
|
+
const pathCandidate = String(metadata.file_path || metadata.path || result.source || result.document || "");
|
|
4974
|
+
if (/[\\/]/.test(pathCandidate) || /\.[a-z0-9]+$/i.test(pathCandidate)) return true;
|
|
4975
|
+
const sourceType = String(metadata.source_type || metadata.connector_type || result.type || "").toLowerCase();
|
|
4976
|
+
return ["local", "github", "code", "file", "repo"].some((token) => sourceType.includes(token));
|
|
4977
|
+
}
|
|
4978
|
+
function filterProjectRepoResults(results, matchedSourceIds) {
|
|
4979
|
+
const scoped = results.filter((result) => {
|
|
4980
|
+
const sourceId = String(result.metadata?.source_id || result.metadata?.sourceId || result.metadata?.source || "");
|
|
4981
|
+
return matchedSourceIds.length === 0 || matchedSourceIds.includes(sourceId);
|
|
4982
|
+
});
|
|
4983
|
+
const repoResults = scoped.filter((result) => isLikelyRepoBackedResult(result));
|
|
4984
|
+
return repoResults.length > 0 ? repoResults : scoped;
|
|
4985
|
+
}
|
|
4986
|
+
function localHitsToEvidence(workspaceId, hits) {
|
|
4987
|
+
return hits.map(
|
|
4988
|
+
(hit) => toEvidenceRef(
|
|
4989
|
+
{
|
|
4990
|
+
id: hit.id,
|
|
4991
|
+
content: hit.raw_snippet,
|
|
4992
|
+
score: hit.score,
|
|
4993
|
+
retrieval_source: hit.retrieval_method,
|
|
4994
|
+
metadata: {
|
|
4995
|
+
file_path: hit.id,
|
|
4996
|
+
snippet: hit.raw_snippet,
|
|
4997
|
+
line_start: hit.line_start,
|
|
4998
|
+
...hit.line_end ? { line_end: hit.line_end } : {}
|
|
4999
|
+
}
|
|
5000
|
+
},
|
|
5001
|
+
workspaceId,
|
|
5002
|
+
hit.retrieval_method
|
|
5003
|
+
)
|
|
5004
|
+
);
|
|
5005
|
+
}
|
|
5006
|
+
function renderLocalWorkspaceContext(args) {
|
|
5007
|
+
const lines = args.hits.map((hit, index) => {
|
|
5008
|
+
const location = hit.line_end && hit.line_end !== hit.line_start ? `${hit.id}:${hit.line_start}-${hit.line_end}` : `${hit.id}:${hit.line_start}`;
|
|
5009
|
+
return `${index + 1}. [${location}, score: ${hit.score.toFixed(2)}, mode: ${hit.search_mode}] ${hit.raw_snippet || hit.snippet || hit.id}`;
|
|
5010
|
+
});
|
|
5011
|
+
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}):`;
|
|
5012
|
+
const warnings = args.warnings.length ? `
|
|
5013
|
+
|
|
5014
|
+
[warnings]
|
|
5015
|
+
${args.warnings.join("\n")}` : "";
|
|
5016
|
+
return `${header}
|
|
5017
|
+
|
|
5018
|
+
${lines.join("\n\n")}${warnings}`;
|
|
5019
|
+
}
|
|
5020
|
+
async function runSearchCodeTool(args) {
|
|
5021
|
+
const rootPath = canonicalizeWorkspacePath(args.path);
|
|
5022
|
+
const allowedExts = args.file_types ? new Set(args.file_types) : CODE_EXTENSIONS;
|
|
5023
|
+
const files = collectCodeFiles(rootPath, allowedExts, args.max_files ?? 150);
|
|
5024
|
+
if (files.length === 0) {
|
|
5025
|
+
const workspace = await resolveWorkspaceTrust({ path: rootPath });
|
|
5026
|
+
const sharedWarnings = [...workspace.warnings];
|
|
5027
|
+
return {
|
|
5028
|
+
tool: "search_code",
|
|
5029
|
+
query: args.query,
|
|
5030
|
+
path: rootPath,
|
|
5031
|
+
results: [],
|
|
5032
|
+
count: 0,
|
|
5033
|
+
warnings: sharedWarnings,
|
|
5034
|
+
diagnostics: {
|
|
5035
|
+
workspace_id: workspace.workspace_id,
|
|
5036
|
+
root_path: workspace.root_path,
|
|
5037
|
+
project_ref: workspace.project_ref,
|
|
5038
|
+
project_id: workspace.project_id,
|
|
5039
|
+
identity_source: workspace.identity_source,
|
|
5040
|
+
index_health: workspace.health,
|
|
5041
|
+
grounded_to_workspace: workspace.grounded_to_workspace,
|
|
5042
|
+
threshold_requested: args.threshold ?? 0.2,
|
|
5043
|
+
threshold_used: null,
|
|
5044
|
+
fallback_used: false,
|
|
5045
|
+
fallback_reason: null,
|
|
5046
|
+
search_mode: "semantic",
|
|
5047
|
+
candidate_files_scanned: 0,
|
|
5048
|
+
semantic_candidate_count: 0,
|
|
5049
|
+
retrieval_route: "local_workspace",
|
|
5050
|
+
warnings: sharedWarnings
|
|
5051
|
+
}
|
|
5052
|
+
};
|
|
5053
|
+
}
|
|
5054
|
+
const localRetrieval = await runLocalWorkspaceRetrieval(args);
|
|
5055
|
+
return {
|
|
5056
|
+
tool: "search_code",
|
|
5057
|
+
query: args.query,
|
|
5058
|
+
path: rootPath,
|
|
5059
|
+
results: localRetrieval.results,
|
|
5060
|
+
count: localRetrieval.results.length,
|
|
5061
|
+
warnings: localRetrieval.warnings,
|
|
5062
|
+
diagnostics: localRetrieval.diagnostics
|
|
5063
|
+
};
|
|
5064
|
+
}
|
|
4628
5065
|
function getLocalAllowlistRoots() {
|
|
4629
5066
|
const fromEnv = (process.env.WHISPER_LOCAL_ALLOWLIST || "").split(",").map((v) => v.trim()).filter(Boolean);
|
|
4630
5067
|
if (fromEnv.length > 0) return fromEnv;
|
|
@@ -4900,21 +5337,23 @@ server.tool(
|
|
|
4900
5337
|
},
|
|
4901
5338
|
async ({ path, workspace_id, project }) => {
|
|
4902
5339
|
try {
|
|
4903
|
-
const
|
|
4904
|
-
const workspaceId = getWorkspaceIdForPath(rootPath, workspace_id);
|
|
5340
|
+
const identity = resolveWorkspaceIdentity({ path, workspace_id });
|
|
4905
5341
|
const state = loadState();
|
|
4906
|
-
const existed = Boolean(state.workspaces[
|
|
4907
|
-
const trust = await resolveWorkspaceTrust({ path:
|
|
5342
|
+
const existed = Boolean(state.workspaces[identity.workspace_id]);
|
|
5343
|
+
const trust = await resolveWorkspaceTrust({ path: identity.root_path, workspace_id, project });
|
|
5344
|
+
const preflight = await resolveRepoGroundingPreflight({ query: "workspace status", path, workspace_id, project });
|
|
4908
5345
|
const payload = {
|
|
4909
|
-
workspace_id:
|
|
5346
|
+
workspace_id: identity.workspace_id,
|
|
4910
5347
|
root_path: trust.root_path,
|
|
5348
|
+
identity_source: trust.identity_source,
|
|
4911
5349
|
project_ref: trust.project_ref,
|
|
4912
5350
|
project_id: trust.project_id,
|
|
4913
5351
|
created: !existed,
|
|
4914
5352
|
resolved_by: trust.resolved_by,
|
|
4915
5353
|
health: trust.health,
|
|
4916
|
-
|
|
4917
|
-
|
|
5354
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5355
|
+
warnings: preflight.warnings,
|
|
5356
|
+
recommended_next_calls: preflight.recommended_next_calls,
|
|
4918
5357
|
index_state: {
|
|
4919
5358
|
last_indexed_at: trust.freshness.last_indexed_at,
|
|
4920
5359
|
last_indexed_commit: trust.last_indexed_commit,
|
|
@@ -4937,15 +5376,21 @@ server.tool(
|
|
|
4937
5376
|
async ({ workspace_id, path }) => {
|
|
4938
5377
|
try {
|
|
4939
5378
|
const trust = await resolveWorkspaceTrust({ path, workspace_id });
|
|
5379
|
+
const repoVerification = await inspectProjectRepoSources({ project_ref: trust.project_ref, root_path: trust.root_path });
|
|
4940
5380
|
const payload = {
|
|
4941
5381
|
workspace_id: trust.workspace_id,
|
|
4942
5382
|
root_path: trust.root_path,
|
|
5383
|
+
identity_source: trust.identity_source,
|
|
4943
5384
|
project_ref: trust.project_ref,
|
|
4944
5385
|
project_id: trust.project_id,
|
|
4945
5386
|
resolved_by: trust.resolved_by,
|
|
4946
5387
|
health: trust.health,
|
|
4947
|
-
|
|
4948
|
-
|
|
5388
|
+
retrieval_readiness: repoVerification.retrieval_readiness,
|
|
5389
|
+
warnings: Array.from(/* @__PURE__ */ new Set([...trust.warnings, ...repoVerification.warnings])),
|
|
5390
|
+
recommended_next_calls: sanitizeRecommendedNextCalls([
|
|
5391
|
+
...trust.recommended_next_calls,
|
|
5392
|
+
...repoVerification.retrieval_readiness === "project_bound_no_repo_source" ? ["context.list_sources", "index.local_scan_ingest"] : []
|
|
5393
|
+
]),
|
|
4949
5394
|
freshness: trust.freshness,
|
|
4950
5395
|
coverage: trust.coverage,
|
|
4951
5396
|
last_indexed_commit: trust.last_indexed_commit,
|
|
@@ -4970,8 +5415,9 @@ server.tool(
|
|
|
4970
5415
|
},
|
|
4971
5416
|
async ({ workspace_id, path, mode, max_files }) => {
|
|
4972
5417
|
try {
|
|
4973
|
-
const
|
|
4974
|
-
const
|
|
5418
|
+
const identity = resolveWorkspaceIdentity({ path, workspace_id });
|
|
5419
|
+
const rootPath = identity.root_path;
|
|
5420
|
+
const workspaceId = identity.workspace_id;
|
|
4975
5421
|
const state = loadState();
|
|
4976
5422
|
const workspace = getWorkspaceState(state, workspaceId);
|
|
4977
5423
|
const fileStats = countCodeFiles(rootPath, max_files);
|
|
@@ -5038,62 +5484,143 @@ server.tool(
|
|
|
5038
5484
|
);
|
|
5039
5485
|
server.tool(
|
|
5040
5486
|
"context.get_relevant",
|
|
5041
|
-
"
|
|
5487
|
+
"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
5488
|
{
|
|
5043
5489
|
question: z.string().describe("Task/question to retrieve context for"),
|
|
5490
|
+
path: z.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
5044
5491
|
workspace_id: z.string().optional(),
|
|
5045
5492
|
project: z.string().optional(),
|
|
5046
5493
|
top_k: z.number().optional().default(12),
|
|
5047
5494
|
include_memories: z.boolean().optional().default(true),
|
|
5048
5495
|
include_graph: z.boolean().optional().default(true),
|
|
5049
5496
|
session_id: z.string().optional(),
|
|
5050
|
-
user_id: z.string().optional()
|
|
5497
|
+
user_id: z.string().optional(),
|
|
5498
|
+
include_parent_content: z.boolean().optional().default(false),
|
|
5499
|
+
retrieval_profile: z.enum(MCP_RETRIEVAL_PROFILE_VALUES).optional()
|
|
5051
5500
|
},
|
|
5052
|
-
async ({ question, workspace_id, project, top_k, include_memories, include_graph, session_id, user_id }) => {
|
|
5501
|
+
async ({ question, path, workspace_id, project, top_k, include_memories, include_graph, session_id, user_id, include_parent_content, retrieval_profile }) => {
|
|
5053
5502
|
try {
|
|
5054
|
-
const
|
|
5055
|
-
|
|
5503
|
+
const preflight = await resolveRepoGroundingPreflight({ query: question, path, workspace_id, project });
|
|
5504
|
+
const retrievalProfile = resolveMcpRetrievalProfile(retrieval_profile);
|
|
5505
|
+
if (shouldAbstainForCodebaseTrust({
|
|
5506
|
+
retrievalProfile,
|
|
5507
|
+
codebaseIntent: preflight.repo_grounded,
|
|
5508
|
+
preflight
|
|
5509
|
+
})) {
|
|
5510
|
+
return toTextResult(buildCodebaseTrustAbstainPayload({
|
|
5511
|
+
query: question,
|
|
5512
|
+
preflight,
|
|
5513
|
+
retrievalProfile
|
|
5514
|
+
}));
|
|
5515
|
+
}
|
|
5516
|
+
if (preflight.retrieval_route === "local_workspace_fallback") {
|
|
5517
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5518
|
+
query: question,
|
|
5519
|
+
path: preflight.trust_state.root_path,
|
|
5520
|
+
project: preflight.trust_state.project_ref || project,
|
|
5521
|
+
top_k
|
|
5522
|
+
});
|
|
5523
|
+
const evidence2 = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results);
|
|
5524
|
+
const payload2 = {
|
|
5525
|
+
question,
|
|
5526
|
+
workspace_id: preflight.trust_state.workspace_id,
|
|
5527
|
+
root_path: preflight.trust_state.root_path,
|
|
5528
|
+
identity_source: preflight.trust_state.identity_source,
|
|
5529
|
+
trust_state: preflight.trust_state,
|
|
5530
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5531
|
+
retrieval_route: preflight.retrieval_route,
|
|
5532
|
+
grounded_to_workspace: true,
|
|
5533
|
+
total_results: evidence2.length,
|
|
5534
|
+
context: evidence2.map((item) => `[${renderCitation(item)}] ${item.snippet || "Relevant local workspace context found."}`).join("\n"),
|
|
5535
|
+
evidence: evidence2,
|
|
5536
|
+
used_context_ids: evidence2.map((item) => item.source_id),
|
|
5537
|
+
latency_ms: 0,
|
|
5538
|
+
warnings: Array.from(/* @__PURE__ */ new Set([...preflight.warnings, ...localRetrieval.warnings])),
|
|
5539
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5540
|
+
};
|
|
5541
|
+
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
5542
|
+
}
|
|
5543
|
+
if (!preflight.trust_state.project_ref) {
|
|
5056
5544
|
const payload2 = {
|
|
5057
5545
|
question,
|
|
5058
|
-
workspace_id:
|
|
5059
|
-
|
|
5546
|
+
workspace_id: preflight.trust_state.workspace_id,
|
|
5547
|
+
root_path: preflight.trust_state.root_path,
|
|
5548
|
+
identity_source: preflight.trust_state.identity_source,
|
|
5549
|
+
trust_state: preflight.trust_state,
|
|
5550
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5551
|
+
retrieval_route: "none",
|
|
5060
5552
|
grounded_to_workspace: false,
|
|
5061
5553
|
total_results: 0,
|
|
5062
5554
|
context: "",
|
|
5063
5555
|
evidence: [],
|
|
5064
5556
|
used_context_ids: [],
|
|
5065
5557
|
latency_ms: 0,
|
|
5066
|
-
warnings:
|
|
5067
|
-
recommended_next_calls:
|
|
5558
|
+
warnings: preflight.warnings,
|
|
5559
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5068
5560
|
};
|
|
5069
5561
|
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
5070
5562
|
}
|
|
5071
5563
|
const queryResult = await queryWithDegradedFallback({
|
|
5072
|
-
project:
|
|
5564
|
+
project: preflight.trust_state.project_ref,
|
|
5073
5565
|
query: question,
|
|
5074
5566
|
top_k,
|
|
5075
|
-
include_memories,
|
|
5567
|
+
include_memories: preflight.repo_grounded ? false : include_memories,
|
|
5076
5568
|
include_graph,
|
|
5077
5569
|
session_id,
|
|
5078
|
-
user_id
|
|
5570
|
+
user_id,
|
|
5571
|
+
source_ids: preflight.repo_grounded ? preflight.matched_source_ids : void 0,
|
|
5572
|
+
retrieval_profile: retrievalProfile,
|
|
5573
|
+
include_parent_content
|
|
5079
5574
|
});
|
|
5080
5575
|
const response = queryResult.response;
|
|
5081
|
-
const
|
|
5576
|
+
const rawResults = preflight.repo_grounded ? filterProjectRepoResults(response.results || [], preflight.matched_source_ids) : response.results || [];
|
|
5577
|
+
if (preflight.repo_grounded && rawResults.length === 0) {
|
|
5578
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5579
|
+
query: question,
|
|
5580
|
+
path: preflight.trust_state.root_path,
|
|
5581
|
+
project: preflight.trust_state.project_ref,
|
|
5582
|
+
top_k
|
|
5583
|
+
});
|
|
5584
|
+
const evidence2 = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results);
|
|
5585
|
+
const payload2 = {
|
|
5586
|
+
question,
|
|
5587
|
+
workspace_id: preflight.trust_state.workspace_id,
|
|
5588
|
+
root_path: preflight.trust_state.root_path,
|
|
5589
|
+
identity_source: preflight.trust_state.identity_source,
|
|
5590
|
+
trust_state: preflight.trust_state,
|
|
5591
|
+
retrieval_readiness: "local_fallback",
|
|
5592
|
+
retrieval_route: "local_workspace_fallback",
|
|
5593
|
+
grounded_to_workspace: true,
|
|
5594
|
+
total_results: evidence2.length,
|
|
5595
|
+
context: evidence2.map((item) => `[${renderCitation(item)}] ${item.snippet || "Relevant local workspace context found."}`).join("\n"),
|
|
5596
|
+
evidence: evidence2,
|
|
5597
|
+
used_context_ids: evidence2.map((item) => item.source_id),
|
|
5598
|
+
latency_ms: 0,
|
|
5599
|
+
warnings: Array.from(/* @__PURE__ */ new Set([...preflight.warnings, ...localRetrieval.warnings])),
|
|
5600
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5601
|
+
};
|
|
5602
|
+
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
5603
|
+
}
|
|
5604
|
+
const evidence = rawResults.map((r) => toEvidenceRef(r, preflight.trust_state.workspace_id, "semantic"));
|
|
5082
5605
|
const payload = {
|
|
5083
5606
|
question,
|
|
5084
|
-
workspace_id:
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5607
|
+
workspace_id: preflight.trust_state.workspace_id,
|
|
5608
|
+
root_path: preflight.trust_state.root_path,
|
|
5609
|
+
identity_source: preflight.trust_state.identity_source,
|
|
5610
|
+
trust_state: preflight.trust_state,
|
|
5611
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5612
|
+
retrieval_route: preflight.repo_grounded ? "project_repo" : "none",
|
|
5613
|
+
grounded_to_workspace: preflight.repo_grounded ? true : preflight.trust_state.grounded_to_workspace,
|
|
5614
|
+
total_results: rawResults.length || response.meta?.total || evidence.length,
|
|
5088
5615
|
context: response.context || "",
|
|
5089
5616
|
evidence,
|
|
5090
|
-
used_context_ids:
|
|
5617
|
+
used_context_ids: rawResults.map((r) => String(r.id)),
|
|
5091
5618
|
latency_ms: response.meta?.latency_ms || 0,
|
|
5092
5619
|
degraded_mode: queryResult.degraded_mode,
|
|
5093
5620
|
degraded_reason: queryResult.degraded_reason,
|
|
5094
5621
|
recommendation: queryResult.recommendation,
|
|
5095
|
-
warnings:
|
|
5096
|
-
recommended_next_calls:
|
|
5622
|
+
warnings: preflight.warnings,
|
|
5623
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5097
5624
|
};
|
|
5098
5625
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
5099
5626
|
} catch (error) {
|
|
@@ -5106,38 +5633,53 @@ server.tool(
|
|
|
5106
5633
|
"Verify whether a claim is supported by retrieved context. Returns supported/partial/unsupported with evidence.",
|
|
5107
5634
|
{
|
|
5108
5635
|
claim: z.string().describe("Claim to verify"),
|
|
5636
|
+
path: z.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
5109
5637
|
workspace_id: z.string().optional(),
|
|
5110
5638
|
project: z.string().optional(),
|
|
5111
5639
|
context_ids: z.array(z.string()).optional(),
|
|
5112
5640
|
strict: z.boolean().optional().default(true)
|
|
5113
5641
|
},
|
|
5114
|
-
async ({ claim, workspace_id, project, context_ids, strict }) => {
|
|
5642
|
+
async ({ claim, path, workspace_id, project, context_ids, strict }) => {
|
|
5115
5643
|
try {
|
|
5116
|
-
const
|
|
5117
|
-
|
|
5644
|
+
const preflight = await resolveRepoGroundingPreflight({ query: claim, path, workspace_id, project });
|
|
5645
|
+
let evidence = [];
|
|
5646
|
+
if (preflight.retrieval_route === "local_workspace_fallback") {
|
|
5647
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5648
|
+
query: claim,
|
|
5649
|
+
path: preflight.trust_state.root_path,
|
|
5650
|
+
project: preflight.trust_state.project_ref || project,
|
|
5651
|
+
top_k: strict ? 8 : 12
|
|
5652
|
+
});
|
|
5653
|
+
evidence = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results).filter((item) => !context_ids || context_ids.length === 0 || context_ids.includes(item.source_id));
|
|
5654
|
+
} else if (preflight.trust_state.project_ref) {
|
|
5655
|
+
const response = await whisper.query({
|
|
5656
|
+
project: preflight.trust_state.project_ref,
|
|
5657
|
+
query: claim,
|
|
5658
|
+
top_k: strict ? 8 : 12,
|
|
5659
|
+
include_memories: preflight.repo_grounded ? false : true,
|
|
5660
|
+
include_graph: true,
|
|
5661
|
+
...preflight.repo_grounded ? { source_ids: preflight.matched_source_ids } : {}
|
|
5662
|
+
});
|
|
5663
|
+
const filtered = (preflight.repo_grounded ? filterProjectRepoResults(response.results || [], preflight.matched_source_ids) : response.results || []).filter(
|
|
5664
|
+
(r) => !context_ids || context_ids.length === 0 || context_ids.includes(String(r.id))
|
|
5665
|
+
);
|
|
5666
|
+
evidence = filtered.map((r) => toEvidenceRef(r, preflight.trust_state.workspace_id, "semantic"));
|
|
5667
|
+
}
|
|
5668
|
+
if (evidence.length === 0) {
|
|
5118
5669
|
const payload2 = {
|
|
5119
5670
|
verdict: "unsupported",
|
|
5120
5671
|
confidence: 0,
|
|
5121
5672
|
evidence: [],
|
|
5122
|
-
trust_state:
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5673
|
+
trust_state: preflight.trust_state,
|
|
5674
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5675
|
+
retrieval_route: preflight.retrieval_route,
|
|
5676
|
+
warnings: preflight.warnings,
|
|
5677
|
+
recommended_next_calls: preflight.recommended_next_calls,
|
|
5678
|
+
missing_requirements: preflight.warnings.length ? preflight.warnings : ["No repo-grounded evidence was available for verification."],
|
|
5679
|
+
explanation: "Verifier did not find sufficient repo-grounded evidence."
|
|
5127
5680
|
};
|
|
5128
5681
|
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
5129
5682
|
}
|
|
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
5683
|
const directEvidence = evidence.filter((e) => e.score >= (strict ? 0.7 : 0.6));
|
|
5142
5684
|
const weakEvidence = evidence.filter((e) => e.score >= (strict ? 0.45 : 0.35));
|
|
5143
5685
|
let verdict = "unsupported";
|
|
@@ -5147,9 +5689,11 @@ server.tool(
|
|
|
5147
5689
|
verdict,
|
|
5148
5690
|
confidence: evidence.length ? Math.max(...evidence.map((e) => e.score)) : 0,
|
|
5149
5691
|
evidence: verdict === "supported" ? directEvidence : weakEvidence,
|
|
5150
|
-
trust_state:
|
|
5151
|
-
|
|
5152
|
-
|
|
5692
|
+
trust_state: preflight.trust_state,
|
|
5693
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5694
|
+
retrieval_route: preflight.retrieval_route,
|
|
5695
|
+
warnings: preflight.warnings,
|
|
5696
|
+
recommended_next_calls: preflight.recommended_next_calls,
|
|
5153
5697
|
missing_requirements: verdict === "supported" ? [] : verdict === "partial" ? ["No direct evidence spans met strict threshold."] : ["No sufficient supporting evidence found for the claim."],
|
|
5154
5698
|
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
5699
|
};
|
|
@@ -5164,6 +5708,7 @@ server.tool(
|
|
|
5164
5708
|
"Answer a question only when evidence requirements are met. Fails closed with an abstain payload when not verifiable.",
|
|
5165
5709
|
{
|
|
5166
5710
|
question: z.string(),
|
|
5711
|
+
path: z.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
5167
5712
|
workspace_id: z.string().optional(),
|
|
5168
5713
|
project: z.string().optional(),
|
|
5169
5714
|
constraints: z.object({
|
|
@@ -5178,38 +5723,51 @@ server.tool(
|
|
|
5178
5723
|
include_recent_decisions: z.boolean().optional().default(true)
|
|
5179
5724
|
}).optional()
|
|
5180
5725
|
},
|
|
5181
|
-
async ({ question, workspace_id, project, constraints, retrieval }) => {
|
|
5726
|
+
async ({ question, path, workspace_id, project, constraints, retrieval }) => {
|
|
5182
5727
|
try {
|
|
5183
5728
|
const requireCitations = constraints?.require_citations ?? true;
|
|
5184
5729
|
const minEvidenceItems = constraints?.min_evidence_items ?? 2;
|
|
5185
5730
|
const minConfidence = constraints?.min_confidence ?? 0.65;
|
|
5186
5731
|
const maxStalenessHours = constraints?.max_staleness_hours ?? 168;
|
|
5187
5732
|
const topK = retrieval?.top_k ?? 12;
|
|
5188
|
-
const
|
|
5189
|
-
|
|
5190
|
-
|
|
5733
|
+
const preflight = await resolveRepoGroundingPreflight({ query: question, path, workspace_id, project });
|
|
5734
|
+
let evidence = [];
|
|
5735
|
+
if (preflight.retrieval_route === "local_workspace_fallback") {
|
|
5736
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5737
|
+
query: question,
|
|
5738
|
+
path: preflight.trust_state.root_path,
|
|
5739
|
+
project: preflight.trust_state.project_ref || project,
|
|
5740
|
+
top_k: topK
|
|
5741
|
+
});
|
|
5742
|
+
evidence = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results);
|
|
5743
|
+
} else if (preflight.trust_state.project_ref) {
|
|
5744
|
+
const response = await whisper.query({
|
|
5745
|
+
project: preflight.trust_state.project_ref,
|
|
5746
|
+
query: question,
|
|
5747
|
+
top_k: topK,
|
|
5748
|
+
include_memories: preflight.repo_grounded ? false : true,
|
|
5749
|
+
include_graph: true,
|
|
5750
|
+
...preflight.repo_grounded ? { source_ids: preflight.matched_source_ids } : {}
|
|
5751
|
+
});
|
|
5752
|
+
const filtered = preflight.repo_grounded ? filterProjectRepoResults(response.results || [], preflight.matched_source_ids) : response.results || [];
|
|
5753
|
+
evidence = filtered.map((r) => toEvidenceRef(r, preflight.trust_state.workspace_id, "semantic"));
|
|
5754
|
+
}
|
|
5755
|
+
if (evidence.length === 0) {
|
|
5756
|
+
const reason = preflight.trust_state.health === "stale" || preflight.trust_state.health === "drifted" ? "stale_index" : "no_retrieval_hits";
|
|
5191
5757
|
const abstain = buildAbstain({
|
|
5192
5758
|
reason,
|
|
5193
|
-
message:
|
|
5759
|
+
message: preflight.warnings[0] || "Workspace trust requirements were not met.",
|
|
5194
5760
|
closest_evidence: [],
|
|
5195
5761
|
claims_evaluated: 1,
|
|
5196
5762
|
evidence_items_found: 0,
|
|
5197
5763
|
min_required: minEvidenceItems,
|
|
5198
|
-
index_fresh:
|
|
5199
|
-
warnings:
|
|
5200
|
-
trust_state:
|
|
5201
|
-
recommended_next_calls:
|
|
5764
|
+
index_fresh: preflight.trust_state.health === "healthy",
|
|
5765
|
+
warnings: preflight.warnings,
|
|
5766
|
+
trust_state: preflight.trust_state,
|
|
5767
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5202
5768
|
});
|
|
5203
5769
|
return { content: [{ type: "text", text: JSON.stringify(abstain, null, 2) }] };
|
|
5204
5770
|
}
|
|
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
5771
|
const sorted = evidence.sort((a, b) => b.score - a.score);
|
|
5214
5772
|
const confidence = sorted.length ? sorted[0].score : 0;
|
|
5215
5773
|
if (sorted.length === 0) {
|
|
@@ -5220,10 +5778,10 @@ server.tool(
|
|
|
5220
5778
|
claims_evaluated: 1,
|
|
5221
5779
|
evidence_items_found: 0,
|
|
5222
5780
|
min_required: minEvidenceItems,
|
|
5223
|
-
index_fresh:
|
|
5224
|
-
warnings:
|
|
5225
|
-
trust_state:
|
|
5226
|
-
recommended_next_calls:
|
|
5781
|
+
index_fresh: preflight.trust_state.health === "healthy",
|
|
5782
|
+
warnings: preflight.warnings,
|
|
5783
|
+
trust_state: preflight.trust_state,
|
|
5784
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5227
5785
|
});
|
|
5228
5786
|
return { content: [{ type: "text", text: JSON.stringify(abstain, null, 2) }] };
|
|
5229
5787
|
}
|
|
@@ -5238,9 +5796,9 @@ server.tool(
|
|
|
5238
5796
|
evidence_items_found: supportedEvidence.length,
|
|
5239
5797
|
min_required: minEvidenceItems,
|
|
5240
5798
|
index_fresh: true,
|
|
5241
|
-
warnings:
|
|
5242
|
-
trust_state:
|
|
5243
|
-
recommended_next_calls:
|
|
5799
|
+
warnings: preflight.warnings,
|
|
5800
|
+
trust_state: preflight.trust_state,
|
|
5801
|
+
recommended_next_calls: preflight.recommended_next_calls
|
|
5244
5802
|
});
|
|
5245
5803
|
return { content: [{ type: "text", text: JSON.stringify(abstain, null, 2) }] };
|
|
5246
5804
|
}
|
|
@@ -5253,9 +5811,11 @@ server.tool(
|
|
|
5253
5811
|
answer: answerLines.join("\n"),
|
|
5254
5812
|
citations,
|
|
5255
5813
|
confidence,
|
|
5256
|
-
trust_state:
|
|
5257
|
-
|
|
5258
|
-
|
|
5814
|
+
trust_state: preflight.trust_state,
|
|
5815
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
5816
|
+
retrieval_route: preflight.retrieval_route,
|
|
5817
|
+
warnings: preflight.warnings,
|
|
5818
|
+
recommended_next_calls: preflight.recommended_next_calls,
|
|
5259
5819
|
verification: {
|
|
5260
5820
|
verdict,
|
|
5261
5821
|
supported_claims: verdict === "supported" ? 1 : 0,
|
|
@@ -5271,26 +5831,134 @@ server.tool(
|
|
|
5271
5831
|
);
|
|
5272
5832
|
server.tool(
|
|
5273
5833
|
"context.query",
|
|
5274
|
-
"
|
|
5834
|
+
"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
5835
|
{
|
|
5276
5836
|
project: z.string().optional().describe("Project name or slug (optional if WHISPER_PROJECT is set)"),
|
|
5277
5837
|
query: z.string().describe("What are you looking for?"),
|
|
5838
|
+
path: z.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
5278
5839
|
top_k: z.number().optional().default(10).describe("Number of results"),
|
|
5279
5840
|
chunk_types: z.array(z.string()).optional().describe("Filter: code, function, class, documentation, api_spec, schema, config, text"),
|
|
5280
5841
|
include_memories: z.boolean().optional().describe("Include relevant memories. Omit to use automatic runtime defaults."),
|
|
5281
5842
|
include_graph: z.boolean().optional().default(false).describe("Include knowledge graph traversal"),
|
|
5282
5843
|
user_id: z.string().optional().describe("User ID for memory scoping"),
|
|
5283
5844
|
session_id: z.string().optional().describe("Session ID for memory scoping"),
|
|
5284
|
-
max_tokens: z.number().optional().describe("Max tokens for packed context")
|
|
5845
|
+
max_tokens: z.number().optional().describe("Max tokens for packed context"),
|
|
5846
|
+
include_parent_content: z.boolean().optional().default(false),
|
|
5847
|
+
retrieval_profile: z.enum(MCP_RETRIEVAL_PROFILE_VALUES).optional()
|
|
5285
5848
|
},
|
|
5286
|
-
async ({ project, query, top_k, chunk_types, include_memories, include_graph, user_id, session_id, max_tokens }) => {
|
|
5849
|
+
async ({ project, query, path, top_k, chunk_types, include_memories, include_graph, user_id, session_id, max_tokens, include_parent_content, retrieval_profile }) => {
|
|
5287
5850
|
try {
|
|
5288
|
-
const
|
|
5289
|
-
|
|
5851
|
+
const preflight = await resolveRepoGroundingPreflight({ query, path, project, chunk_types });
|
|
5852
|
+
const retrievalProfile = resolveMcpRetrievalProfile(retrieval_profile);
|
|
5853
|
+
if (shouldAbstainForCodebaseTrust({
|
|
5854
|
+
retrievalProfile,
|
|
5855
|
+
codebaseIntent: preflight.repo_grounded,
|
|
5856
|
+
preflight
|
|
5857
|
+
})) {
|
|
5858
|
+
return toTextResult(buildCodebaseTrustAbstainPayload({
|
|
5859
|
+
query,
|
|
5860
|
+
preflight,
|
|
5861
|
+
retrievalProfile
|
|
5862
|
+
}));
|
|
5863
|
+
}
|
|
5864
|
+
const resolvedProject = preflight.trust_state.project_ref || await resolveProjectRef(project);
|
|
5865
|
+
if (!resolvedProject && !preflight.repo_grounded) {
|
|
5290
5866
|
return { content: [{ type: "text", text: "Error: No project resolved. Set WHISPER_PROJECT or pass project." }] };
|
|
5291
5867
|
}
|
|
5292
|
-
const scope = resolveMcpScope({ project: resolvedProject, user_id, session_id });
|
|
5293
|
-
|
|
5868
|
+
const scope = resolveMcpScope({ project: resolvedProject, user_id, session_id, path: preflight.trust_state.root_path });
|
|
5869
|
+
if (preflight.repo_grounded && preflight.retrieval_route === "local_workspace_fallback") {
|
|
5870
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5871
|
+
query,
|
|
5872
|
+
path: preflight.trust_state.root_path,
|
|
5873
|
+
project: resolvedProject || project,
|
|
5874
|
+
top_k
|
|
5875
|
+
});
|
|
5876
|
+
if (localRetrieval.results.length > 0) {
|
|
5877
|
+
return {
|
|
5878
|
+
content: [{
|
|
5879
|
+
type: "text",
|
|
5880
|
+
text: renderLocalWorkspaceContext({
|
|
5881
|
+
query,
|
|
5882
|
+
project_ref: resolvedProject || null,
|
|
5883
|
+
scope,
|
|
5884
|
+
route: "local_workspace_fallback",
|
|
5885
|
+
hits: localRetrieval.results,
|
|
5886
|
+
diagnostics: localRetrieval.diagnostics,
|
|
5887
|
+
warnings: Array.from(/* @__PURE__ */ new Set([...preflight.warnings, ...localRetrieval.warnings]))
|
|
5888
|
+
})
|
|
5889
|
+
}]
|
|
5890
|
+
};
|
|
5891
|
+
}
|
|
5892
|
+
}
|
|
5893
|
+
if (preflight.repo_grounded && resolvedProject) {
|
|
5894
|
+
const queryResult2 = await queryWithDegradedFallback({
|
|
5895
|
+
project: resolvedProject,
|
|
5896
|
+
query,
|
|
5897
|
+
top_k,
|
|
5898
|
+
include_memories: false,
|
|
5899
|
+
include_graph,
|
|
5900
|
+
user_id: user_id || scope.userId,
|
|
5901
|
+
session_id: session_id || scope.sessionId,
|
|
5902
|
+
source_ids: preflight.matched_source_ids,
|
|
5903
|
+
retrieval_profile: retrievalProfile,
|
|
5904
|
+
include_parent_content
|
|
5905
|
+
});
|
|
5906
|
+
const repoResults = filterProjectRepoResults(queryResult2.response.results || [], preflight.matched_source_ids);
|
|
5907
|
+
if (repoResults.length > 0) {
|
|
5908
|
+
const scopedResponse = { ...queryResult2.response, results: repoResults };
|
|
5909
|
+
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}):
|
|
5910
|
+
|
|
5911
|
+
`;
|
|
5912
|
+
const suffix2 = [
|
|
5913
|
+
`[diagnostics] identity_source=${preflight.trust_state.identity_source} retrieval_readiness=${preflight.retrieval_readiness} retrieval_route=project_repo`,
|
|
5914
|
+
queryResult2.degraded_mode ? `[degraded_mode=true] ${queryResult2.degraded_reason}
|
|
5915
|
+
Recommendation: ${queryResult2.recommendation}` : "",
|
|
5916
|
+
preflight.warnings.length ? `[warnings]
|
|
5917
|
+
${preflight.warnings.join("\n")}` : ""
|
|
5918
|
+
].filter(Boolean).join("\n\n");
|
|
5919
|
+
return { content: [{ type: "text", text: `${header2}${scopedResponse.context}${suffix2 ? `
|
|
5920
|
+
|
|
5921
|
+
${suffix2}` : ""}` }] };
|
|
5922
|
+
}
|
|
5923
|
+
}
|
|
5924
|
+
if (preflight.repo_grounded) {
|
|
5925
|
+
if (resolvedProject && include_memories !== false) {
|
|
5926
|
+
const memoryRescue = await runContextQueryMemoryRescue({
|
|
5927
|
+
project: resolvedProject,
|
|
5928
|
+
query,
|
|
5929
|
+
user_id: user_id ? scope.userId : void 0,
|
|
5930
|
+
session_id: session_id ? scope.sessionId : void 0,
|
|
5931
|
+
top_k
|
|
5932
|
+
});
|
|
5933
|
+
if (memoryRescue.results.length && memoryRescue.rescue_mode) {
|
|
5934
|
+
return {
|
|
5935
|
+
content: [{
|
|
5936
|
+
type: "text",
|
|
5937
|
+
text: `${renderContextQueryMemoryRescue({
|
|
5938
|
+
project: resolvedProject,
|
|
5939
|
+
query,
|
|
5940
|
+
scope,
|
|
5941
|
+
results: memoryRescue.results,
|
|
5942
|
+
rescue_mode: memoryRescue.rescue_mode
|
|
5943
|
+
})}
|
|
5944
|
+
|
|
5945
|
+
[diagnostics]
|
|
5946
|
+
retrieval_route=memory_only retrieval_readiness=${preflight.retrieval_readiness} workspace=${preflight.trust_state.workspace_id}`
|
|
5947
|
+
}]
|
|
5948
|
+
};
|
|
5949
|
+
}
|
|
5950
|
+
}
|
|
5951
|
+
return {
|
|
5952
|
+
content: [{
|
|
5953
|
+
type: "text",
|
|
5954
|
+
text: `No relevant repo-grounded context found.
|
|
5955
|
+
|
|
5956
|
+
[diagnostics]
|
|
5957
|
+
workspace=${preflight.trust_state.workspace_id} identity_source=${preflight.trust_state.identity_source} retrieval_readiness=${preflight.retrieval_readiness} retrieval_route=${preflight.retrieval_route}`
|
|
5958
|
+
}]
|
|
5959
|
+
};
|
|
5960
|
+
}
|
|
5961
|
+
const automaticMode = !preflight.repo_grounded && include_memories !== false && include_graph !== true && !(chunk_types && chunk_types.length > 0) && max_tokens === void 0 && runtimeClient;
|
|
5294
5962
|
if (automaticMode) {
|
|
5295
5963
|
try {
|
|
5296
5964
|
const prepared = await prepareAutomaticQuery({
|
|
@@ -5298,14 +5966,15 @@ server.tool(
|
|
|
5298
5966
|
query,
|
|
5299
5967
|
top_k,
|
|
5300
5968
|
user_id,
|
|
5301
|
-
session_id
|
|
5969
|
+
session_id,
|
|
5970
|
+
path: preflight.trust_state.root_path
|
|
5302
5971
|
});
|
|
5303
5972
|
if (!prepared.items.length) {
|
|
5304
5973
|
const memoryRescue = include_memories !== false ? await runContextQueryMemoryRescue({
|
|
5305
5974
|
project: resolvedProject,
|
|
5306
5975
|
query,
|
|
5307
|
-
user_id: user_id ?
|
|
5308
|
-
session_id: session_id ?
|
|
5976
|
+
user_id: user_id ? scope.userId : void 0,
|
|
5977
|
+
session_id: session_id ? scope.sessionId : void 0,
|
|
5309
5978
|
top_k
|
|
5310
5979
|
}) : { results: [], rescue_mode: null };
|
|
5311
5980
|
if (memoryRescue.results.length && memoryRescue.rescue_mode) {
|
|
@@ -5315,7 +5984,7 @@ server.tool(
|
|
|
5315
5984
|
text: renderContextQueryMemoryRescue({
|
|
5316
5985
|
project: resolvedProject,
|
|
5317
5986
|
query,
|
|
5318
|
-
scope
|
|
5987
|
+
scope,
|
|
5319
5988
|
results: memoryRescue.results,
|
|
5320
5989
|
rescue_mode: memoryRescue.rescue_mode
|
|
5321
5990
|
})
|
|
@@ -5334,11 +6003,11 @@ ${prepared.retrieval.warnings.join("\n")}` : "";
|
|
|
5334
6003
|
`deduped=${prepared.retrieval.dedupedCount}`,
|
|
5335
6004
|
`dropped_below_floor=${prepared.retrieval.droppedBelowFloor}`
|
|
5336
6005
|
].join(" ");
|
|
5337
|
-
const
|
|
6006
|
+
const scopeLabel = `project=${prepared.scope.project} user=${prepared.scope.userId} session=${prepared.scope.sessionId}`;
|
|
5338
6007
|
return {
|
|
5339
6008
|
content: [{
|
|
5340
6009
|
type: "text",
|
|
5341
|
-
text: `Found ${prepared.items.length} runtime-ranked items (${prepared.retrieval.durationMs}ms, ${
|
|
6010
|
+
text: `Found ${prepared.items.length} runtime-ranked items (${prepared.retrieval.durationMs}ms, ${scopeLabel}, ${diagnostics}):
|
|
5342
6011
|
|
|
5343
6012
|
${prepared.context}${warnings}`
|
|
5344
6013
|
}]
|
|
@@ -5351,8 +6020,10 @@ ${prepared.context}${warnings}`
|
|
|
5351
6020
|
top_k,
|
|
5352
6021
|
include_memories: include_memories === true,
|
|
5353
6022
|
include_graph,
|
|
5354
|
-
user_id: user_id ||
|
|
5355
|
-
session_id: session_id ||
|
|
6023
|
+
user_id: user_id || scope.userId,
|
|
6024
|
+
session_id: session_id || scope.sessionId,
|
|
6025
|
+
retrieval_profile: retrievalProfile,
|
|
6026
|
+
include_parent_content
|
|
5356
6027
|
});
|
|
5357
6028
|
const response2 = queryResult2.response;
|
|
5358
6029
|
if (response2.results.length === 0) {
|
|
@@ -5404,8 +6075,10 @@ ${automaticWarning}${suffix2}` }] };
|
|
|
5404
6075
|
top_k,
|
|
5405
6076
|
include_memories: include_memories === true,
|
|
5406
6077
|
include_graph,
|
|
5407
|
-
user_id: user_id ||
|
|
5408
|
-
session_id: session_id ||
|
|
6078
|
+
user_id: user_id || scope.userId,
|
|
6079
|
+
session_id: session_id || scope.sessionId,
|
|
6080
|
+
retrieval_profile: retrievalProfile,
|
|
6081
|
+
include_parent_content
|
|
5409
6082
|
});
|
|
5410
6083
|
const response = queryResult.response;
|
|
5411
6084
|
if (response.results.length === 0) {
|
|
@@ -5483,7 +6156,7 @@ server.tool(
|
|
|
5483
6156
|
);
|
|
5484
6157
|
server.tool(
|
|
5485
6158
|
"memory.search",
|
|
5486
|
-
"
|
|
6159
|
+
"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
6160
|
{
|
|
5488
6161
|
project: z.string().optional().describe("Project name or slug"),
|
|
5489
6162
|
query: z.string().describe("What to search for"),
|
|
@@ -6462,6 +7135,7 @@ var CODE_EXTENSIONS = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx", "py", "
|
|
|
6462
7135
|
function extractSignature(filePath, content) {
|
|
6463
7136
|
const lines = content.split("\n");
|
|
6464
7137
|
const signature = /* @__PURE__ */ new Set([`// File: ${filePath}`]);
|
|
7138
|
+
signature.add(`relative_path:${filePath.replace(/\\/g, "/").toLowerCase()}`);
|
|
6465
7139
|
for (const segment of filePath.split(/[\\/._-]+/).filter(Boolean)) {
|
|
6466
7140
|
signature.add(`path:${segment}`);
|
|
6467
7141
|
}
|
|
@@ -6477,29 +7151,39 @@ function extractSignature(filePath, content) {
|
|
|
6477
7151
|
if (routeMatches) {
|
|
6478
7152
|
for (const match of routeMatches.slice(0, 3)) signature.add(`route:${match.slice(0, 120)}`);
|
|
6479
7153
|
}
|
|
7154
|
+
const envKeyMatches = trimmed.match(/[A-Z][A-Z0-9_]{2,}/g);
|
|
7155
|
+
if (envKeyMatches) {
|
|
7156
|
+
for (const match of envKeyMatches.slice(0, 3)) signature.add(`env:${match}`);
|
|
7157
|
+
}
|
|
6480
7158
|
if (/^(import|from|require|use |pub use )/.test(trimmed)) {
|
|
6481
7159
|
signature.add(trimmed.slice(0, 120));
|
|
6482
7160
|
continue;
|
|
6483
7161
|
}
|
|
6484
7162
|
if (/^(export|async function|function|class|interface|type |const |let |def |pub fn |fn |struct |impl |enum )/.test(trimmed)) {
|
|
6485
7163
|
signature.add(trimmed.slice(0, 120));
|
|
7164
|
+
const exportMatch = trimmed.match(/export\s+(?:const|function|class|type|interface)\s+([A-Za-z0-9_$]+)/);
|
|
7165
|
+
if (exportMatch?.[1]) signature.add(`export:${exportMatch[1]}`);
|
|
6486
7166
|
continue;
|
|
6487
7167
|
}
|
|
6488
7168
|
if (trimmed.startsWith("@") || trimmed.startsWith("#[")) {
|
|
6489
7169
|
signature.add(trimmed.slice(0, 80));
|
|
6490
7170
|
}
|
|
6491
7171
|
}
|
|
6492
|
-
for (
|
|
6493
|
-
const trimmed =
|
|
7172
|
+
for (let index = 60; index < lines.length; index += 1) {
|
|
7173
|
+
const trimmed = lines[index].trim();
|
|
6494
7174
|
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
7175
|
signature.add(trimmed.slice(0, 120));
|
|
7176
|
+
const surrounding = lines.slice(index, Math.min(lines.length, index + 3)).map((line) => line.trim()).filter(Boolean);
|
|
7177
|
+
for (const line of surrounding) signature.add(line.slice(0, 120));
|
|
6496
7178
|
continue;
|
|
6497
7179
|
}
|
|
6498
7180
|
if (/^[A-Za-z0-9_$]+\s*[:=]\s*["'`][^"'`]{3,}["'`]/.test(trimmed)) {
|
|
6499
7181
|
signature.add(trimmed.slice(0, 120));
|
|
6500
7182
|
}
|
|
7183
|
+
const exportMatch = trimmed.match(/export\s+(?:const|function|class|type|interface)\s+([A-Za-z0-9_$]+)/);
|
|
7184
|
+
if (exportMatch?.[1]) signature.add(`export:${exportMatch[1]}`);
|
|
6501
7185
|
}
|
|
6502
|
-
return Array.from(signature).join("\n").slice(0,
|
|
7186
|
+
return Array.from(signature).join("\n").slice(0, 3200);
|
|
6503
7187
|
}
|
|
6504
7188
|
server.tool(
|
|
6505
7189
|
"code.search_semantic",
|
|
@@ -6766,7 +7450,7 @@ server.tool(
|
|
|
6766
7450
|
);
|
|
6767
7451
|
server.tool(
|
|
6768
7452
|
"search",
|
|
6769
|
-
"
|
|
7453
|
+
"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
7454
|
{
|
|
6771
7455
|
project: z.string().optional().describe("Project name or slug"),
|
|
6772
7456
|
query: z.string().optional().describe("Semantic retrieval query"),
|
|
@@ -6996,7 +7680,7 @@ server.tool(
|
|
|
6996
7680
|
);
|
|
6997
7681
|
server.tool(
|
|
6998
7682
|
"index",
|
|
6999
|
-
"
|
|
7683
|
+
"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
7684
|
{
|
|
7001
7685
|
action: z.enum(["source", "workspace"]).default("source"),
|
|
7002
7686
|
project: z.string().optional(),
|
|
@@ -7077,7 +7761,7 @@ server.tool(
|
|
|
7077
7761
|
);
|
|
7078
7762
|
server.tool(
|
|
7079
7763
|
"remember",
|
|
7080
|
-
"
|
|
7764
|
+
"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
7765
|
{
|
|
7082
7766
|
project: z.string().optional(),
|
|
7083
7767
|
content: z.string().describe("Memory content"),
|
|
@@ -7156,7 +7840,7 @@ server.tool(
|
|
|
7156
7840
|
);
|
|
7157
7841
|
server.tool(
|
|
7158
7842
|
"learn",
|
|
7159
|
-
"Unified
|
|
7843
|
+
"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
7844
|
{
|
|
7161
7845
|
mode: z.enum(["conversation", "text", "source"]).describe("What kind of learning to perform"),
|
|
7162
7846
|
project: z.string().optional(),
|
|
@@ -7287,8 +7971,14 @@ if (process.argv[1] && /server\.(mjs|cjs|js|ts)$/.test(process.argv[1])) {
|
|
|
7287
7971
|
main().catch(console.error);
|
|
7288
7972
|
}
|
|
7289
7973
|
export {
|
|
7974
|
+
RECOMMENDED_NEXT_CALL_ALLOWLIST,
|
|
7975
|
+
RETRIEVAL_READINESS_VALUES,
|
|
7976
|
+
RETRIEVAL_ROUTE_VALUES,
|
|
7977
|
+
WORKSPACE_HEALTH_VALUES,
|
|
7290
7978
|
canonicalizeWorkspacePath,
|
|
7291
7979
|
chooseWorkspaceProjectSource,
|
|
7980
|
+
classifyProjectRepoReadiness,
|
|
7981
|
+
classifyRepoGroundedQuery,
|
|
7292
7982
|
classifyWorkspaceHealth,
|
|
7293
7983
|
createMcpServer,
|
|
7294
7984
|
createWhisperMcpClient,
|
|
@@ -7296,5 +7986,8 @@ export {
|
|
|
7296
7986
|
extractSignature,
|
|
7297
7987
|
renderScopedMcpConfig,
|
|
7298
7988
|
resolveForgetQueryCandidates,
|
|
7299
|
-
|
|
7989
|
+
resolveWorkspaceIdentity,
|
|
7990
|
+
runSearchCodeSearch,
|
|
7991
|
+
sanitizeRecommendedNextCalls,
|
|
7992
|
+
shouldAbstainForCodebaseTrust
|
|
7300
7993
|
};
|