@usewhisper/mcp-server 2.13.0 → 2.14.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/README.md +5 -2
- package/dist/server.js +1120 -511
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -3785,6 +3785,9 @@ function buildMcpSearchPayload(input) {
|
|
|
3785
3785
|
count: normalizedResults.length,
|
|
3786
3786
|
degraded_mode: Boolean(input.degradedMode),
|
|
3787
3787
|
degraded_reason: input.degradedReason ?? null,
|
|
3788
|
+
semantic_status: input.semanticStatus ?? (input.degradedMode ? "failed" : "ok"),
|
|
3789
|
+
fallback_mode: input.fallbackMode ?? (input.degradedMode ? "lexical_backend" : "none"),
|
|
3790
|
+
recommended_fixes: input.recommendedFixes || [],
|
|
3788
3791
|
warnings: input.warnings || []
|
|
3789
3792
|
});
|
|
3790
3793
|
}
|
|
@@ -3844,6 +3847,8 @@ var RETRIEVAL_READINESS_VALUES = [
|
|
|
3844
3847
|
"local_fallback"
|
|
3845
3848
|
];
|
|
3846
3849
|
var RETRIEVAL_ROUTE_VALUES = ["project_repo", "local_workspace_fallback", "memory_only", "none"];
|
|
3850
|
+
var STATUS_LABEL_VALUES = ["healthy", "degraded_auto_repairing", "needs_user_action", "blocked"];
|
|
3851
|
+
var STATUS_ACTION_VALUES = ["none", "run_auto_repair", "restart_client", "reauth", "add_source", "reindex"];
|
|
3847
3852
|
var STATE_DIR = join(homedir(), ".whisper-mcp");
|
|
3848
3853
|
var STATE_PATH = join(STATE_DIR, "state.json");
|
|
3849
3854
|
var AUDIT_LOG_PATH = join(STATE_DIR, "forget-audit.log");
|
|
@@ -3876,6 +3881,7 @@ var ALIAS_TOOL_MAP = [
|
|
|
3876
3881
|
{ alias: "search", target: "context.query | memory.get" },
|
|
3877
3882
|
{ alias: "search_code", target: "code.search_semantic" },
|
|
3878
3883
|
{ alias: "grep", target: "code.search_text" },
|
|
3884
|
+
{ alias: "fix", target: "index.auto_repair" },
|
|
3879
3885
|
{ alias: "read", target: "local.file_read" },
|
|
3880
3886
|
{ alias: "explore", target: "local.tree" },
|
|
3881
3887
|
{ alias: "research", target: "research.oracle" },
|
|
@@ -3891,11 +3897,14 @@ var RECOMMENDED_NEXT_CALL_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
|
3891
3897
|
"context.list_projects",
|
|
3892
3898
|
"index.workspace_status",
|
|
3893
3899
|
"index.workspace_run",
|
|
3900
|
+
"index.auto_repair",
|
|
3901
|
+
"fix",
|
|
3894
3902
|
"search_code",
|
|
3895
3903
|
"grep",
|
|
3896
3904
|
"context.list_sources",
|
|
3897
3905
|
"context.add_source",
|
|
3898
3906
|
"index.local_scan_ingest",
|
|
3907
|
+
"context.query",
|
|
3899
3908
|
"context.get_relevant"
|
|
3900
3909
|
]);
|
|
3901
3910
|
var UNTRUSTED_CODEBASE_HEALTH = /* @__PURE__ */ new Set(["unbound", "unindexed"]);
|
|
@@ -3903,31 +3912,85 @@ var UNTRUSTED_CODEBASE_PROJECT_READINESS = /* @__PURE__ */ new Set([
|
|
|
3903
3912
|
"project_unverified",
|
|
3904
3913
|
"project_bound_no_repo_source"
|
|
3905
3914
|
]);
|
|
3915
|
+
var RETRIEVAL_READINESS_RANK = {
|
|
3916
|
+
no_project: 0,
|
|
3917
|
+
project_unverified: 0,
|
|
3918
|
+
project_bound_no_repo_source: 1,
|
|
3919
|
+
project_repo_source_stale: 2,
|
|
3920
|
+
local_fallback: 3,
|
|
3921
|
+
project_repo_source_ready: 4
|
|
3922
|
+
};
|
|
3923
|
+
var ROOT_CAUSE_PRIMARY_ACTION = {
|
|
3924
|
+
none: "none",
|
|
3925
|
+
missing_api_key: "reauth",
|
|
3926
|
+
no_project_bound: "add_source",
|
|
3927
|
+
mode_mismatch_remote_local_ingest: "restart_client",
|
|
3928
|
+
workspace_unindexed: "reindex",
|
|
3929
|
+
project_bound_no_repo_source: "add_source",
|
|
3930
|
+
project_unverified: "reauth",
|
|
3931
|
+
project_repo_source_stale: "reindex",
|
|
3932
|
+
semantic_backend_unavailable: "run_auto_repair",
|
|
3933
|
+
local_fallback_active: "run_auto_repair"
|
|
3934
|
+
};
|
|
3935
|
+
var ROOT_CAUSE_NEXT_CALLS = {
|
|
3936
|
+
none: [],
|
|
3937
|
+
missing_api_key: [],
|
|
3938
|
+
no_project_bound: ["context.list_projects"],
|
|
3939
|
+
mode_mismatch_remote_local_ingest: [],
|
|
3940
|
+
workspace_unindexed: ["index.workspace_run"],
|
|
3941
|
+
project_bound_no_repo_source: ["context.add_source"],
|
|
3942
|
+
project_unverified: [],
|
|
3943
|
+
project_repo_source_stale: ["index.workspace_run"],
|
|
3944
|
+
semantic_backend_unavailable: ["fix"],
|
|
3945
|
+
local_fallback_active: ["fix"]
|
|
3946
|
+
};
|
|
3906
3947
|
function sanitizeRecommendedNextCalls(nextCalls) {
|
|
3907
3948
|
return Array.from(
|
|
3908
3949
|
new Set(
|
|
3909
3950
|
nextCalls.map((entry) => String(entry || "").trim()).filter((entry) => RECOMMENDED_NEXT_CALL_ALLOWLIST.has(entry))
|
|
3910
3951
|
)
|
|
3911
|
-
);
|
|
3952
|
+
).slice(0, 3);
|
|
3953
|
+
}
|
|
3954
|
+
function recommendedNextCallsAreAcyclic(nextCalls) {
|
|
3955
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3956
|
+
for (const nextCall of nextCalls) {
|
|
3957
|
+
if (seen.has(nextCall)) return false;
|
|
3958
|
+
seen.add(nextCall);
|
|
3959
|
+
}
|
|
3960
|
+
return true;
|
|
3961
|
+
}
|
|
3962
|
+
function retrievalReadinessRank(readiness) {
|
|
3963
|
+
return RETRIEVAL_READINESS_RANK[readiness];
|
|
3964
|
+
}
|
|
3965
|
+
function isStrictlyBetterReadiness(from, to) {
|
|
3966
|
+
return retrievalReadinessRank(to) > retrievalReadinessRank(from);
|
|
3967
|
+
}
|
|
3968
|
+
function statusActionForRootCause(rootCause) {
|
|
3969
|
+
return ROOT_CAUSE_PRIMARY_ACTION[rootCause] || "none";
|
|
3970
|
+
}
|
|
3971
|
+
function recommendedNextCallsForRootCause(rootCause) {
|
|
3972
|
+
return sanitizeRecommendedNextCalls(ROOT_CAUSE_NEXT_CALLS[rootCause] || []);
|
|
3973
|
+
}
|
|
3974
|
+
function statusLabelForAction(action) {
|
|
3975
|
+
if (action === "none") return "healthy";
|
|
3976
|
+
if (action === "run_auto_repair") return "degraded_auto_repairing";
|
|
3977
|
+
if (action === "reauth") return "blocked";
|
|
3978
|
+
return "needs_user_action";
|
|
3979
|
+
}
|
|
3980
|
+
function actionRequiredForAction(action) {
|
|
3981
|
+
return action !== "none" && action !== "run_auto_repair";
|
|
3912
3982
|
}
|
|
3913
3983
|
function getRepoReadinessNextCalls(readiness) {
|
|
3914
|
-
if (readiness
|
|
3915
|
-
return
|
|
3984
|
+
if (readiness === "project_bound_no_repo_source") {
|
|
3985
|
+
return recommendedNextCallsForRootCause("project_bound_no_repo_source");
|
|
3916
3986
|
}
|
|
3917
|
-
if (
|
|
3918
|
-
return
|
|
3919
|
-
"context.list_sources",
|
|
3920
|
-
"context.add_source",
|
|
3921
|
-
"index.workspace_status",
|
|
3922
|
-
"context.get_relevant"
|
|
3923
|
-
]);
|
|
3987
|
+
if (readiness === "project_unverified") {
|
|
3988
|
+
return recommendedNextCallsForRootCause("project_unverified");
|
|
3924
3989
|
}
|
|
3925
|
-
|
|
3926
|
-
"
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
"context.get_relevant"
|
|
3930
|
-
]);
|
|
3990
|
+
if (readiness === "project_repo_source_stale") {
|
|
3991
|
+
return recommendedNextCallsForRootCause("project_repo_source_stale");
|
|
3992
|
+
}
|
|
3993
|
+
return [];
|
|
3931
3994
|
}
|
|
3932
3995
|
function resolveMcpRetrievalProfile(requested) {
|
|
3933
3996
|
if (requested && MCP_RETRIEVAL_PROFILE_VALUES.includes(requested)) {
|
|
@@ -3935,6 +3998,156 @@ function resolveMcpRetrievalProfile(requested) {
|
|
|
3935
3998
|
}
|
|
3936
3999
|
return MCP_RETRIEVAL_PRECISION_V1_DEFAULT ? "precision_v1" : "legacy";
|
|
3937
4000
|
}
|
|
4001
|
+
function statusContractForReadiness(args) {
|
|
4002
|
+
if (args.root_cause_code && args.root_cause_code !== "none") {
|
|
4003
|
+
const action = statusActionForRootCause(args.root_cause_code);
|
|
4004
|
+
return {
|
|
4005
|
+
status_label: statusLabelForAction(action),
|
|
4006
|
+
status_action: action,
|
|
4007
|
+
action_required: actionRequiredForAction(action)
|
|
4008
|
+
};
|
|
4009
|
+
}
|
|
4010
|
+
if (args.force_repairing) {
|
|
4011
|
+
return {
|
|
4012
|
+
status_label: "degraded_auto_repairing",
|
|
4013
|
+
status_action: "run_auto_repair",
|
|
4014
|
+
action_required: false
|
|
4015
|
+
};
|
|
4016
|
+
}
|
|
4017
|
+
if (args.semantic_status === "failed") {
|
|
4018
|
+
return {
|
|
4019
|
+
status_label: "degraded_auto_repairing",
|
|
4020
|
+
status_action: "run_auto_repair",
|
|
4021
|
+
action_required: false
|
|
4022
|
+
};
|
|
4023
|
+
}
|
|
4024
|
+
if (args.workspace_health === "unindexed") {
|
|
4025
|
+
return {
|
|
4026
|
+
status_label: "needs_user_action",
|
|
4027
|
+
status_action: "reindex",
|
|
4028
|
+
action_required: true
|
|
4029
|
+
};
|
|
4030
|
+
}
|
|
4031
|
+
if (args.retrieval_readiness === "project_unverified") {
|
|
4032
|
+
return {
|
|
4033
|
+
status_label: "blocked",
|
|
4034
|
+
status_action: "reauth",
|
|
4035
|
+
action_required: true
|
|
4036
|
+
};
|
|
4037
|
+
}
|
|
4038
|
+
if (args.retrieval_readiness === "project_bound_no_repo_source") {
|
|
4039
|
+
if ((args.runtime_mode || RUNTIME_MODE) === "remote") {
|
|
4040
|
+
return {
|
|
4041
|
+
status_label: "needs_user_action",
|
|
4042
|
+
status_action: "add_source",
|
|
4043
|
+
action_required: true
|
|
4044
|
+
};
|
|
4045
|
+
}
|
|
4046
|
+
return {
|
|
4047
|
+
status_label: "needs_user_action",
|
|
4048
|
+
status_action: "reindex",
|
|
4049
|
+
action_required: true
|
|
4050
|
+
};
|
|
4051
|
+
}
|
|
4052
|
+
if (args.retrieval_readiness === "project_repo_source_stale") {
|
|
4053
|
+
return {
|
|
4054
|
+
status_label: "needs_user_action",
|
|
4055
|
+
status_action: "reindex",
|
|
4056
|
+
action_required: true
|
|
4057
|
+
};
|
|
4058
|
+
}
|
|
4059
|
+
if (args.retrieval_readiness === "local_fallback") {
|
|
4060
|
+
return {
|
|
4061
|
+
status_label: "degraded_auto_repairing",
|
|
4062
|
+
status_action: "run_auto_repair",
|
|
4063
|
+
action_required: false
|
|
4064
|
+
};
|
|
4065
|
+
}
|
|
4066
|
+
if (args.retrieval_readiness === "no_project") {
|
|
4067
|
+
return {
|
|
4068
|
+
status_label: "needs_user_action",
|
|
4069
|
+
status_action: "add_source",
|
|
4070
|
+
action_required: true
|
|
4071
|
+
};
|
|
4072
|
+
}
|
|
4073
|
+
return {
|
|
4074
|
+
status_label: "healthy",
|
|
4075
|
+
status_action: "none",
|
|
4076
|
+
action_required: false
|
|
4077
|
+
};
|
|
4078
|
+
}
|
|
4079
|
+
function semanticStatusFromSearchMode(mode) {
|
|
4080
|
+
if (mode === "semantic") return "ok";
|
|
4081
|
+
if (mode === "adaptive_semantic") return "adaptive";
|
|
4082
|
+
return "failed";
|
|
4083
|
+
}
|
|
4084
|
+
function fallbackModeFromSearchMode(mode) {
|
|
4085
|
+
if (mode === "semantic") return "none";
|
|
4086
|
+
if (mode === "adaptive_semantic") return "adaptive_threshold";
|
|
4087
|
+
return "lexical_rescue";
|
|
4088
|
+
}
|
|
4089
|
+
function parsePositiveNumber(value, fallback = 0) {
|
|
4090
|
+
const parsed = Number(value);
|
|
4091
|
+
if (!Number.isFinite(parsed) || parsed < 0) return fallback;
|
|
4092
|
+
return parsed;
|
|
4093
|
+
}
|
|
4094
|
+
function getWorkspaceSemanticFailureStats(workspaceId) {
|
|
4095
|
+
const state = loadState();
|
|
4096
|
+
const workspace = getWorkspaceState(state, workspaceId);
|
|
4097
|
+
const failures = parsePositiveNumber(workspace.index_metadata?.semantic_failures, 0);
|
|
4098
|
+
const attempts = parsePositiveNumber(workspace.index_metadata?.semantic_attempts, 0);
|
|
4099
|
+
const rate = attempts > 0 ? failures / attempts : 0;
|
|
4100
|
+
return { failures, attempts, rate };
|
|
4101
|
+
}
|
|
4102
|
+
function recordSemanticAttempt(workspaceId, failed) {
|
|
4103
|
+
const state = loadState();
|
|
4104
|
+
const workspace = getWorkspaceState(state, workspaceId);
|
|
4105
|
+
if (!workspace.index_metadata) workspace.index_metadata = {};
|
|
4106
|
+
const failures = parsePositiveNumber(workspace.index_metadata.semantic_failures, 0);
|
|
4107
|
+
const attempts = parsePositiveNumber(workspace.index_metadata.semantic_attempts, 0);
|
|
4108
|
+
workspace.index_metadata.semantic_attempts = attempts + 1;
|
|
4109
|
+
workspace.index_metadata.semantic_failures = failed ? failures + 1 : failures;
|
|
4110
|
+
saveState(state);
|
|
4111
|
+
}
|
|
4112
|
+
function computeTrustScore(args) {
|
|
4113
|
+
let score = 100;
|
|
4114
|
+
if (args.workspace_health === "unbound" || args.workspace_health === "unindexed") score -= 35;
|
|
4115
|
+
if (args.workspace_health === "stale" || args.workspace_health === "drifted") score -= 18;
|
|
4116
|
+
if (args.retrieval_readiness === "project_unverified") score -= 35;
|
|
4117
|
+
if (args.retrieval_readiness === "project_bound_no_repo_source") score -= 32;
|
|
4118
|
+
if (args.retrieval_readiness === "project_repo_source_stale") score -= 20;
|
|
4119
|
+
if (args.retrieval_readiness === "local_fallback") score -= 10;
|
|
4120
|
+
const failureRate = Math.max(0, Math.min(1, args.semantic_failure_rate || 0));
|
|
4121
|
+
score -= Math.round(failureRate * 25);
|
|
4122
|
+
return Math.max(0, Math.min(100, score));
|
|
4123
|
+
}
|
|
4124
|
+
function persistTrustScore(workspaceId, trustScore) {
|
|
4125
|
+
const state = loadState();
|
|
4126
|
+
const workspace = getWorkspaceState(state, workspaceId);
|
|
4127
|
+
if (!workspace.index_metadata) workspace.index_metadata = {};
|
|
4128
|
+
workspace.index_metadata.trust_score = Math.max(0, Math.min(100, Math.round(trustScore)));
|
|
4129
|
+
saveState(state);
|
|
4130
|
+
}
|
|
4131
|
+
function shouldAutoHeal(args) {
|
|
4132
|
+
if (args.retrieval_readiness === "project_unverified" || args.retrieval_readiness === "project_bound_no_repo_source") {
|
|
4133
|
+
return true;
|
|
4134
|
+
}
|
|
4135
|
+
if ((args.semantic_attempts || 0) >= 3 && args.semantic_failure_rate >= 0.5) {
|
|
4136
|
+
return true;
|
|
4137
|
+
}
|
|
4138
|
+
return args.trust_score < 60;
|
|
4139
|
+
}
|
|
4140
|
+
function resolveAutoRepairRootCause(args) {
|
|
4141
|
+
if (args.restart_required) return "mode_mismatch_remote_local_ingest";
|
|
4142
|
+
if (args.trust_health === "unindexed") return "workspace_unindexed";
|
|
4143
|
+
if (args.project_readiness === "project_bound_no_repo_source") return "project_bound_no_repo_source";
|
|
4144
|
+
if (args.project_readiness === "project_unverified") return "project_unverified";
|
|
4145
|
+
if (args.project_readiness === "project_repo_source_stale") return "project_repo_source_stale";
|
|
4146
|
+
if (args.semantic_attempts >= 3 && args.semantic_failure_rate >= 0.5) return "semantic_backend_unavailable";
|
|
4147
|
+
if (args.retrieval_readiness === "local_fallback") return "local_fallback_active";
|
|
4148
|
+
if (args.retrieval_readiness === "no_project") return "no_project_bound";
|
|
4149
|
+
return "none";
|
|
4150
|
+
}
|
|
3938
4151
|
function shouldAbstainForCodebaseTrust(args) {
|
|
3939
4152
|
if (args.retrievalProfile !== "precision_v1" || !args.codebaseIntent) return false;
|
|
3940
4153
|
return UNTRUSTED_CODEBASE_HEALTH.has(args.preflight.trust_state.health) || UNTRUSTED_CODEBASE_PROJECT_READINESS.has(args.preflight.project_retrieval_readiness);
|
|
@@ -4270,9 +4483,9 @@ async function resolveProjectDescriptor(projectRef) {
|
|
|
4270
4483
|
}
|
|
4271
4484
|
}
|
|
4272
4485
|
function getWorkspaceRecommendedNextCalls(health) {
|
|
4273
|
-
if (health === "unbound") return sanitizeRecommendedNextCalls(["index.workspace_resolve"
|
|
4274
|
-
if (health === "unindexed") return sanitizeRecommendedNextCalls(["index.
|
|
4275
|
-
if (health === "stale" || health === "drifted") return sanitizeRecommendedNextCalls(["index.
|
|
4486
|
+
if (health === "unbound") return sanitizeRecommendedNextCalls(["index.workspace_resolve"]);
|
|
4487
|
+
if (health === "unindexed") return sanitizeRecommendedNextCalls(["index.workspace_run"]);
|
|
4488
|
+
if (health === "stale" || health === "drifted") return sanitizeRecommendedNextCalls(["index.workspace_run"]);
|
|
4276
4489
|
return [];
|
|
4277
4490
|
}
|
|
4278
4491
|
function getWorkspaceWarnings(args) {
|
|
@@ -4468,7 +4681,7 @@ async function resolveRepoGroundingPreflight(args) {
|
|
|
4468
4681
|
}
|
|
4469
4682
|
}
|
|
4470
4683
|
const recommendedNextCalls = [...trust.recommended_next_calls];
|
|
4471
|
-
if (sourceVerification.retrieval_readiness
|
|
4684
|
+
if (sourceVerification.retrieval_readiness !== "project_repo_source_ready" && sourceVerification.retrieval_readiness !== "no_project") {
|
|
4472
4685
|
recommendedNextCalls.push(...getRepoReadinessNextCalls(sourceVerification.retrieval_readiness));
|
|
4473
4686
|
}
|
|
4474
4687
|
return {
|
|
@@ -4498,53 +4711,32 @@ async function ingestSessionWithSyncFallback(params) {
|
|
|
4498
4711
|
return whisper.ingestSession(params);
|
|
4499
4712
|
}
|
|
4500
4713
|
}
|
|
4501
|
-
function defaultMcpUserId() {
|
|
4714
|
+
function defaultMcpUserId(params) {
|
|
4502
4715
|
const explicit = process.env.WHISPER_USER_ID?.trim();
|
|
4503
4716
|
if (explicit) return explicit;
|
|
4504
|
-
const
|
|
4717
|
+
const workspacePath = params?.workspacePath || canonicalizeWorkspacePath(process.cwd());
|
|
4718
|
+
const projectRef = params?.project || DEFAULT_PROJECT || "default";
|
|
4719
|
+
const seed = `${workspacePath}|${projectRef}|${API_KEY.slice(0, 12) || "anon"}`;
|
|
4505
4720
|
return `mcp-user-${createHash("sha256").update(seed).digest("hex").slice(0, 12)}`;
|
|
4506
4721
|
}
|
|
4507
|
-
function resolveMcpScope(params) {
|
|
4722
|
+
function resolveMcpScope(params, options) {
|
|
4723
|
+
const workspacePath = canonicalizeWorkspacePath(params?.path || process.env.WHISPER_WORKSPACE_PATH || process.cwd());
|
|
4724
|
+
const explicitSession = params?.session_id?.trim();
|
|
4508
4725
|
return {
|
|
4509
4726
|
project: params?.project,
|
|
4510
|
-
userId: params?.user_id?.trim() || defaultMcpUserId(
|
|
4511
|
-
|
|
4512
|
-
|
|
4727
|
+
userId: params?.user_id?.trim() || defaultMcpUserId({
|
|
4728
|
+
project: params?.project,
|
|
4729
|
+
workspacePath
|
|
4730
|
+
}),
|
|
4731
|
+
sessionId: explicitSession || (options?.include_default_session ? cachedMcpSessionId : void 0),
|
|
4732
|
+
workspacePath
|
|
4513
4733
|
};
|
|
4514
4734
|
}
|
|
4515
|
-
async function prepareAutomaticQuery(params) {
|
|
4516
|
-
if (!runtimeClient) {
|
|
4517
|
-
throw new Error("Whisper runtime client unavailable.");
|
|
4518
|
-
}
|
|
4519
|
-
const scope = resolveMcpScope(params);
|
|
4520
|
-
const key = [
|
|
4521
|
-
params.project || DEFAULT_PROJECT || "",
|
|
4522
|
-
scope.userId,
|
|
4523
|
-
scope.sessionId,
|
|
4524
|
-
scope.workspacePath || process.cwd(),
|
|
4525
|
-
String(params.top_k || 10)
|
|
4526
|
-
].join("|");
|
|
4527
|
-
let runtime = runtimeSessions.get(key);
|
|
4528
|
-
if (!runtime) {
|
|
4529
|
-
runtime = runtimeClient.createAgentRuntime({
|
|
4530
|
-
project: params.project,
|
|
4531
|
-
userId: scope.userId,
|
|
4532
|
-
sessionId: scope.sessionId,
|
|
4533
|
-
workspacePath: scope.workspacePath,
|
|
4534
|
-
topK: params.top_k,
|
|
4535
|
-
clientName: "whisper-mcp"
|
|
4536
|
-
});
|
|
4537
|
-
runtimeSessions.set(key, runtime);
|
|
4538
|
-
}
|
|
4539
|
-
return runtime.beforeTurn({
|
|
4540
|
-
userMessage: params.query
|
|
4541
|
-
});
|
|
4542
|
-
}
|
|
4543
4735
|
function noteAutomaticSourceActivity(params) {
|
|
4544
4736
|
if (!runtimeClient) return;
|
|
4545
4737
|
const sourceIds = [...new Set((params.sourceIds || []).map((value) => String(value || "").trim()).filter(Boolean))];
|
|
4546
4738
|
if (sourceIds.length === 0) return;
|
|
4547
|
-
const scope = resolveMcpScope(params);
|
|
4739
|
+
const scope = resolveMcpScope(params, { include_default_session: true });
|
|
4548
4740
|
const key = [
|
|
4549
4741
|
params.project || DEFAULT_PROJECT || "",
|
|
4550
4742
|
scope.userId,
|
|
@@ -4576,7 +4768,7 @@ function buildAbstain(args) {
|
|
|
4576
4768
|
warnings: args.warnings || [],
|
|
4577
4769
|
trust_state: args.trust_state,
|
|
4578
4770
|
recommended_next_calls: sanitizeRecommendedNextCalls(
|
|
4579
|
-
args.recommended_next_calls || ["index.workspace_status", "index.workspace_run", "
|
|
4771
|
+
args.recommended_next_calls || ["index.workspace_status", "index.workspace_run", "fix", "context.query"]
|
|
4580
4772
|
),
|
|
4581
4773
|
diagnostics: {
|
|
4582
4774
|
claims_evaluated: args.claims_evaluated,
|
|
@@ -4631,6 +4823,415 @@ function countCodeFiles(searchPath, maxFiles = 5e3) {
|
|
|
4631
4823
|
walk(searchPath);
|
|
4632
4824
|
return { total, skipped };
|
|
4633
4825
|
}
|
|
4826
|
+
function computeWorkspaceSnapshot(searchPath, maxFiles = 250) {
|
|
4827
|
+
const files = collectCodeFiles(searchPath, CODE_EXTENSIONS, maxFiles);
|
|
4828
|
+
const entries = files.slice(0, maxFiles).map((filePath) => {
|
|
4829
|
+
try {
|
|
4830
|
+
const st = statSync(filePath);
|
|
4831
|
+
return `${relative(searchPath, filePath)}:${st.mtimeMs}:${st.size}`;
|
|
4832
|
+
} catch {
|
|
4833
|
+
return "";
|
|
4834
|
+
}
|
|
4835
|
+
}).filter(Boolean);
|
|
4836
|
+
const sample = entries.join("|");
|
|
4837
|
+
return {
|
|
4838
|
+
hash: createHash("sha256").update(sample || searchPath).digest("hex").slice(0, 24),
|
|
4839
|
+
entries
|
|
4840
|
+
};
|
|
4841
|
+
}
|
|
4842
|
+
function computeSnapshotChangedFiles(previousEntries, nextEntries) {
|
|
4843
|
+
if (!previousEntries || previousEntries.length === 0) return nextEntries.length;
|
|
4844
|
+
const previousByPath = /* @__PURE__ */ new Map();
|
|
4845
|
+
for (const entry of previousEntries) {
|
|
4846
|
+
const idx = entry.indexOf(":");
|
|
4847
|
+
if (idx <= 0) continue;
|
|
4848
|
+
previousByPath.set(entry.slice(0, idx), entry.slice(idx + 1));
|
|
4849
|
+
}
|
|
4850
|
+
let changed = 0;
|
|
4851
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4852
|
+
for (const entry of nextEntries) {
|
|
4853
|
+
const idx = entry.indexOf(":");
|
|
4854
|
+
if (idx <= 0) continue;
|
|
4855
|
+
const relPath = entry.slice(0, idx);
|
|
4856
|
+
const fingerprint = entry.slice(idx + 1);
|
|
4857
|
+
seen.add(relPath);
|
|
4858
|
+
if (previousByPath.get(relPath) !== fingerprint) changed += 1;
|
|
4859
|
+
}
|
|
4860
|
+
for (const relPath of previousByPath.keys()) {
|
|
4861
|
+
if (!seen.has(relPath)) changed += 1;
|
|
4862
|
+
}
|
|
4863
|
+
return changed;
|
|
4864
|
+
}
|
|
4865
|
+
function shouldTriggerBackgroundRefresh(args) {
|
|
4866
|
+
const nowMs = args.now_ms ?? Date.now();
|
|
4867
|
+
const lastAutoRefreshAt = args.last_auto_refresh_at ? new Date(args.last_auto_refresh_at).getTime() : 0;
|
|
4868
|
+
const debounceMs = 10 * 60 * 1e3;
|
|
4869
|
+
const dueForDebounce = !lastAutoRefreshAt || Number.isNaN(lastAutoRefreshAt) || nowMs - lastAutoRefreshAt >= debounceMs;
|
|
4870
|
+
if (!dueForDebounce) {
|
|
4871
|
+
return { should_refresh: false, trigger_reason: null };
|
|
4872
|
+
}
|
|
4873
|
+
if (args.last_indexed_commit && args.current_head && args.last_indexed_commit !== args.current_head) {
|
|
4874
|
+
return { should_refresh: true, trigger_reason: "head_changed" };
|
|
4875
|
+
}
|
|
4876
|
+
if (args.changed_files >= 20) {
|
|
4877
|
+
return { should_refresh: true, trigger_reason: "delta_files_threshold" };
|
|
4878
|
+
}
|
|
4879
|
+
const ratio = args.indexed_files > 0 ? args.changed_files / args.indexed_files : 0;
|
|
4880
|
+
if (ratio >= 0.02) {
|
|
4881
|
+
return { should_refresh: true, trigger_reason: "delta_ratio_threshold" };
|
|
4882
|
+
}
|
|
4883
|
+
return { should_refresh: false, trigger_reason: null };
|
|
4884
|
+
}
|
|
4885
|
+
async function runBackgroundWorkspaceRefresh(args) {
|
|
4886
|
+
const state = loadState();
|
|
4887
|
+
const workspace = getWorkspaceState(state, args.workspace_id);
|
|
4888
|
+
if (!workspace.index_metadata) workspace.index_metadata = {};
|
|
4889
|
+
const fileStats = countCodeFiles(args.root_path, args.max_files || 1500);
|
|
4890
|
+
const currentHead = getGitHead(args.root_path) || null;
|
|
4891
|
+
const currentSnapshot = computeWorkspaceSnapshot(args.root_path);
|
|
4892
|
+
const gitPendingCount = getGitPendingCount(args.root_path);
|
|
4893
|
+
const changedFiles = gitPendingCount == null ? computeSnapshotChangedFiles(workspace.index_metadata.last_snapshot_entries, currentSnapshot.entries) : gitPendingCount;
|
|
4894
|
+
const lastIndexedCommit = workspace.index_metadata.last_indexed_commit || null;
|
|
4895
|
+
const refreshDecision = shouldTriggerBackgroundRefresh({
|
|
4896
|
+
current_head: currentHead,
|
|
4897
|
+
last_indexed_commit: lastIndexedCommit,
|
|
4898
|
+
changed_files: changedFiles,
|
|
4899
|
+
indexed_files: parsePositiveNumber(workspace.index_metadata.indexed_files, fileStats.total || 1),
|
|
4900
|
+
last_auto_refresh_at: workspace.index_metadata.last_auto_refresh_at || null
|
|
4901
|
+
});
|
|
4902
|
+
const forceFullRequested = args.force_full === true;
|
|
4903
|
+
if (!refreshDecision.should_refresh && !forceFullRequested) {
|
|
4904
|
+
return { refreshed: false, mode: null, reason: null };
|
|
4905
|
+
}
|
|
4906
|
+
const mode = forceFullRequested ? "full" : "incremental";
|
|
4907
|
+
workspace.root_path = args.root_path;
|
|
4908
|
+
workspace.index_metadata.last_indexed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
4909
|
+
workspace.index_metadata.last_auto_refresh_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
4910
|
+
workspace.index_metadata.last_indexed_commit = currentHead || void 0;
|
|
4911
|
+
workspace.index_metadata.coverage = mode === "full" ? 1 : Math.max(0, Math.min(1, fileStats.total / Math.max(1, args.max_files || 1500)));
|
|
4912
|
+
workspace.index_metadata.indexed_files = fileStats.total;
|
|
4913
|
+
workspace.index_metadata.last_snapshot_hash = currentSnapshot.hash;
|
|
4914
|
+
workspace.index_metadata.last_snapshot_entries = currentSnapshot.entries;
|
|
4915
|
+
if (mode === "full") workspace.index_metadata.last_full_refresh_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
4916
|
+
saveState(state);
|
|
4917
|
+
return {
|
|
4918
|
+
refreshed: true,
|
|
4919
|
+
mode,
|
|
4920
|
+
reason: refreshDecision.trigger_reason || (forceFullRequested ? "manual_full_override" : mode === "full" ? "full_refresh_due" : "manual")
|
|
4921
|
+
};
|
|
4922
|
+
}
|
|
4923
|
+
function buildSourceRepairHint(args) {
|
|
4924
|
+
const remote = parseGitHubRemote(getGitRemoteUrl(args.root_path) || null);
|
|
4925
|
+
if (remote) {
|
|
4926
|
+
const branch = getGitBranch(args.root_path) || "main";
|
|
4927
|
+
return {
|
|
4928
|
+
command: `context.add_source --project ${args.project_ref || "<project>"} --type github --owner ${remote.owner} --repo ${remote.repo} --branch ${branch}`,
|
|
4929
|
+
message: "Attach the matching GitHub source, then rerun fix."
|
|
4930
|
+
};
|
|
4931
|
+
}
|
|
4932
|
+
return {
|
|
4933
|
+
command: `context.add_source --project ${args.project_ref || "<project>"} --type local --path "${args.root_path}"`,
|
|
4934
|
+
message: "Attach the local workspace as a source, then rerun fix."
|
|
4935
|
+
};
|
|
4936
|
+
}
|
|
4937
|
+
async function attemptDeterministicSourceAttach(args) {
|
|
4938
|
+
const remote = parseGitHubRemote(getGitRemoteUrl(args.root_path) || null);
|
|
4939
|
+
if (remote) {
|
|
4940
|
+
const branch = getGitBranch(args.root_path) || "main";
|
|
4941
|
+
const result = await createSourceByType({
|
|
4942
|
+
project: args.project,
|
|
4943
|
+
type: "github",
|
|
4944
|
+
name: `auto-repair:${remote.owner}/${remote.repo}`,
|
|
4945
|
+
owner: remote.owner,
|
|
4946
|
+
repo: remote.repo,
|
|
4947
|
+
branch,
|
|
4948
|
+
auto_index: true
|
|
4949
|
+
});
|
|
4950
|
+
const sourceId = result?.source_id || result?.id;
|
|
4951
|
+
noteAutomaticSourceActivity({
|
|
4952
|
+
project: args.project,
|
|
4953
|
+
sourceIds: sourceId ? [String(sourceId)] : []
|
|
4954
|
+
});
|
|
4955
|
+
return { attached: true, detail: `github:${remote.owner}/${remote.repo}@${branch}` };
|
|
4956
|
+
}
|
|
4957
|
+
if (RUNTIME_MODE !== "remote") {
|
|
4958
|
+
await createSourceByType({
|
|
4959
|
+
project: args.project,
|
|
4960
|
+
type: "local",
|
|
4961
|
+
name: "auto-repair:local-workspace",
|
|
4962
|
+
path: args.root_path,
|
|
4963
|
+
auto_index: true,
|
|
4964
|
+
max_files: 500
|
|
4965
|
+
});
|
|
4966
|
+
return { attached: true, detail: "local_workspace_source_attached" };
|
|
4967
|
+
}
|
|
4968
|
+
return { attached: false, detail: "deterministic_source_inputs_unavailable" };
|
|
4969
|
+
}
|
|
4970
|
+
async function runAutoRepairVerification(args) {
|
|
4971
|
+
if (args.preflight.retrieval_route === "project_repo" && args.preflight.matched_source_ids.length > 0) {
|
|
4972
|
+
try {
|
|
4973
|
+
const queryResult = await queryWithDegradedFallback({
|
|
4974
|
+
project: args.project,
|
|
4975
|
+
query: "how does this project work",
|
|
4976
|
+
top_k: 5,
|
|
4977
|
+
include_memories: false,
|
|
4978
|
+
include_graph: false,
|
|
4979
|
+
source_ids: args.preflight.matched_source_ids
|
|
4980
|
+
});
|
|
4981
|
+
const resultCount = Array.isArray(queryResult.response?.results) ? queryResult.response.results.length : 0;
|
|
4982
|
+
return {
|
|
4983
|
+
passed: resultCount > 0,
|
|
4984
|
+
route: "project_repo",
|
|
4985
|
+
result_count: resultCount,
|
|
4986
|
+
detail: resultCount > 0 ? "project_query_has_results" : "project_query_empty"
|
|
4987
|
+
};
|
|
4988
|
+
} catch (error) {
|
|
4989
|
+
return {
|
|
4990
|
+
passed: false,
|
|
4991
|
+
route: "project_repo",
|
|
4992
|
+
result_count: 0,
|
|
4993
|
+
detail: `project_query_error:${String(error?.message || error)}`
|
|
4994
|
+
};
|
|
4995
|
+
}
|
|
4996
|
+
}
|
|
4997
|
+
if (args.preflight.retrieval_route === "local_workspace_fallback") {
|
|
4998
|
+
try {
|
|
4999
|
+
const local = await runLocalWorkspaceRetrieval({
|
|
5000
|
+
query: "how does this project work",
|
|
5001
|
+
path: args.preflight.trust_state.root_path,
|
|
5002
|
+
project: args.project,
|
|
5003
|
+
top_k: 5
|
|
5004
|
+
});
|
|
5005
|
+
return {
|
|
5006
|
+
passed: local.results.length > 0,
|
|
5007
|
+
route: "local_workspace_fallback",
|
|
5008
|
+
result_count: local.results.length,
|
|
5009
|
+
detail: local.results.length > 0 ? "local_query_has_results" : "local_query_empty"
|
|
5010
|
+
};
|
|
5011
|
+
} catch (error) {
|
|
5012
|
+
return {
|
|
5013
|
+
passed: false,
|
|
5014
|
+
route: "local_workspace_fallback",
|
|
5015
|
+
result_count: 0,
|
|
5016
|
+
detail: `local_query_error:${String(error?.message || error)}`
|
|
5017
|
+
};
|
|
5018
|
+
}
|
|
5019
|
+
}
|
|
5020
|
+
return {
|
|
5021
|
+
passed: false,
|
|
5022
|
+
route: args.preflight.retrieval_route,
|
|
5023
|
+
result_count: 0,
|
|
5024
|
+
detail: "no_verifiable_retrieval_route"
|
|
5025
|
+
};
|
|
5026
|
+
}
|
|
5027
|
+
async function runAutoRepairFlow(input) {
|
|
5028
|
+
const actions_attempted = [];
|
|
5029
|
+
const actions_succeeded = [];
|
|
5030
|
+
const actions_failed = [];
|
|
5031
|
+
const apply = input.apply !== false;
|
|
5032
|
+
const repairScope = input.repair_scope || "safe";
|
|
5033
|
+
const allowRestartHint = input.allow_restart_hint !== false;
|
|
5034
|
+
let restartRequired = false;
|
|
5035
|
+
if (!API_KEY) {
|
|
5036
|
+
return {
|
|
5037
|
+
root_cause_code: "missing_api_key",
|
|
5038
|
+
actions_attempted,
|
|
5039
|
+
actions_succeeded,
|
|
5040
|
+
actions_failed: [...actions_failed, "validate_credentials:missing_api_key"],
|
|
5041
|
+
restart_required: false,
|
|
5042
|
+
verification_passed: false,
|
|
5043
|
+
status_label: "blocked",
|
|
5044
|
+
status_action: "reauth",
|
|
5045
|
+
action_required: true,
|
|
5046
|
+
diagnostics: {
|
|
5047
|
+
message: "WHISPER_API_KEY is required."
|
|
5048
|
+
}
|
|
5049
|
+
};
|
|
5050
|
+
}
|
|
5051
|
+
actions_attempted.push("validate_credentials");
|
|
5052
|
+
actions_succeeded.push("validate_credentials");
|
|
5053
|
+
actions_attempted.push("resolve_project");
|
|
5054
|
+
const resolvedProject = await resolveProjectRef(input.project);
|
|
5055
|
+
if (!resolvedProject) {
|
|
5056
|
+
actions_failed.push("resolve_project:no_project");
|
|
5057
|
+
return {
|
|
5058
|
+
root_cause_code: "no_project_bound",
|
|
5059
|
+
actions_attempted,
|
|
5060
|
+
actions_succeeded,
|
|
5061
|
+
actions_failed,
|
|
5062
|
+
restart_required: false,
|
|
5063
|
+
verification_passed: false,
|
|
5064
|
+
status_label: "needs_user_action",
|
|
5065
|
+
status_action: "add_source",
|
|
5066
|
+
action_required: true,
|
|
5067
|
+
diagnostics: {
|
|
5068
|
+
message: "No project resolved. Set WHISPER_PROJECT or pass project."
|
|
5069
|
+
}
|
|
5070
|
+
};
|
|
5071
|
+
}
|
|
5072
|
+
actions_succeeded.push("resolve_project");
|
|
5073
|
+
actions_attempted.push("resolve_workspace_trust");
|
|
5074
|
+
const beforePreflight = await resolveRepoGroundingPreflight({
|
|
5075
|
+
query: "how does this project work",
|
|
5076
|
+
path: input.path,
|
|
5077
|
+
workspace_id: input.workspace_id,
|
|
5078
|
+
project: resolvedProject
|
|
5079
|
+
});
|
|
5080
|
+
actions_succeeded.push("resolve_workspace_trust");
|
|
5081
|
+
actions_attempted.push("runtime_mode_check");
|
|
5082
|
+
if (allowRestartHint && RUNTIME_MODE === "remote" && (beforePreflight.project_retrieval_readiness === "project_bound_no_repo_source" || beforePreflight.retrieval_readiness === "local_fallback")) {
|
|
5083
|
+
actions_failed.push("runtime_mode_check:remote_mode_blocks_local_repair");
|
|
5084
|
+
restartRequired = true;
|
|
5085
|
+
} else {
|
|
5086
|
+
actions_succeeded.push("runtime_mode_check");
|
|
5087
|
+
}
|
|
5088
|
+
actions_attempted.push("refresh_workspace_index_metadata");
|
|
5089
|
+
let refreshResult = null;
|
|
5090
|
+
try {
|
|
5091
|
+
if (apply) {
|
|
5092
|
+
refreshResult = await runBackgroundWorkspaceRefresh({
|
|
5093
|
+
workspace_id: beforePreflight.trust_state.workspace_id,
|
|
5094
|
+
root_path: beforePreflight.trust_state.root_path,
|
|
5095
|
+
force_full: repairScope === "full"
|
|
5096
|
+
});
|
|
5097
|
+
} else {
|
|
5098
|
+
refreshResult = { refreshed: false, mode: null, reason: "dry_run" };
|
|
5099
|
+
}
|
|
5100
|
+
actions_succeeded.push("refresh_workspace_index_metadata");
|
|
5101
|
+
} catch (error) {
|
|
5102
|
+
actions_failed.push(`refresh_workspace_index_metadata:${String(error?.message || error)}`);
|
|
5103
|
+
}
|
|
5104
|
+
actions_attempted.push("repair_source_readiness");
|
|
5105
|
+
let sourceRepair = {
|
|
5106
|
+
applied: false,
|
|
5107
|
+
attached: false,
|
|
5108
|
+
detail: "not_required",
|
|
5109
|
+
hint: null
|
|
5110
|
+
};
|
|
5111
|
+
if (beforePreflight.project_retrieval_readiness === "project_bound_no_repo_source") {
|
|
5112
|
+
const hint = buildSourceRepairHint({
|
|
5113
|
+
root_path: beforePreflight.trust_state.root_path,
|
|
5114
|
+
project_ref: beforePreflight.trust_state.project_ref
|
|
5115
|
+
});
|
|
5116
|
+
sourceRepair = {
|
|
5117
|
+
applied: apply && repairScope === "full",
|
|
5118
|
+
attached: false,
|
|
5119
|
+
detail: "manual_source_attach_required",
|
|
5120
|
+
hint
|
|
5121
|
+
};
|
|
5122
|
+
if (apply && repairScope === "full") {
|
|
5123
|
+
try {
|
|
5124
|
+
const attach = await attemptDeterministicSourceAttach({
|
|
5125
|
+
project: resolvedProject,
|
|
5126
|
+
root_path: beforePreflight.trust_state.root_path
|
|
5127
|
+
});
|
|
5128
|
+
sourceRepair = {
|
|
5129
|
+
applied: true,
|
|
5130
|
+
attached: attach.attached,
|
|
5131
|
+
detail: attach.detail,
|
|
5132
|
+
hint
|
|
5133
|
+
};
|
|
5134
|
+
if (attach.attached) {
|
|
5135
|
+
actions_succeeded.push("repair_source_readiness");
|
|
5136
|
+
} else {
|
|
5137
|
+
actions_failed.push(`repair_source_readiness:${attach.detail}`);
|
|
5138
|
+
}
|
|
5139
|
+
} catch (error) {
|
|
5140
|
+
actions_failed.push(`repair_source_readiness:${String(error?.message || error)}`);
|
|
5141
|
+
}
|
|
5142
|
+
} else {
|
|
5143
|
+
actions_failed.push("repair_source_readiness:manual_source_attach_required");
|
|
5144
|
+
}
|
|
5145
|
+
} else {
|
|
5146
|
+
actions_succeeded.push("repair_source_readiness");
|
|
5147
|
+
}
|
|
5148
|
+
const afterPreflight = await resolveRepoGroundingPreflight({
|
|
5149
|
+
query: "how does this project work",
|
|
5150
|
+
path: beforePreflight.trust_state.root_path,
|
|
5151
|
+
workspace_id: beforePreflight.trust_state.workspace_id,
|
|
5152
|
+
project: resolvedProject
|
|
5153
|
+
});
|
|
5154
|
+
const semanticStats = getWorkspaceSemanticFailureStats(afterPreflight.trust_state.workspace_id);
|
|
5155
|
+
const trustScore = computeTrustScore({
|
|
5156
|
+
retrieval_readiness: afterPreflight.retrieval_readiness,
|
|
5157
|
+
workspace_health: afterPreflight.trust_state.health,
|
|
5158
|
+
semantic_failure_rate: semanticStats.rate
|
|
5159
|
+
});
|
|
5160
|
+
persistTrustScore(afterPreflight.trust_state.workspace_id, trustScore);
|
|
5161
|
+
const autoHeal = shouldAutoHeal({
|
|
5162
|
+
retrieval_readiness: afterPreflight.retrieval_readiness,
|
|
5163
|
+
trust_score: trustScore,
|
|
5164
|
+
semantic_failure_rate: semanticStats.rate,
|
|
5165
|
+
semantic_attempts: semanticStats.attempts
|
|
5166
|
+
});
|
|
5167
|
+
actions_attempted.push("verification_query");
|
|
5168
|
+
const verification = await runAutoRepairVerification({
|
|
5169
|
+
project: resolvedProject,
|
|
5170
|
+
preflight: afterPreflight
|
|
5171
|
+
});
|
|
5172
|
+
if (verification.passed) actions_succeeded.push("verification_query");
|
|
5173
|
+
else actions_failed.push(`verification_query:${verification.detail}`);
|
|
5174
|
+
const rootCause = resolveAutoRepairRootCause({
|
|
5175
|
+
restart_required: restartRequired,
|
|
5176
|
+
trust_health: beforePreflight.trust_state.health,
|
|
5177
|
+
project_readiness: beforePreflight.project_retrieval_readiness,
|
|
5178
|
+
retrieval_readiness: beforePreflight.retrieval_readiness,
|
|
5179
|
+
semantic_failure_rate: semanticStats.rate,
|
|
5180
|
+
semantic_attempts: semanticStats.attempts
|
|
5181
|
+
});
|
|
5182
|
+
const statusContract = statusContractForReadiness({
|
|
5183
|
+
retrieval_readiness: afterPreflight.retrieval_readiness,
|
|
5184
|
+
workspace_health: afterPreflight.trust_state.health,
|
|
5185
|
+
root_cause_code: rootCause,
|
|
5186
|
+
force_repairing: autoHeal && rootCause === "none"
|
|
5187
|
+
});
|
|
5188
|
+
const verificationPassed = verification.passed;
|
|
5189
|
+
const effectiveStatusAction = statusContract.status_action;
|
|
5190
|
+
const effectiveActionRequired = statusContract.action_required;
|
|
5191
|
+
const transitionImproved = isStrictlyBetterReadiness(
|
|
5192
|
+
beforePreflight.retrieval_readiness,
|
|
5193
|
+
afterPreflight.retrieval_readiness
|
|
5194
|
+
);
|
|
5195
|
+
const explicitBlocker = effectiveActionRequired;
|
|
5196
|
+
const recommendedNextCalls = sanitizeRecommendedNextCalls([
|
|
5197
|
+
...recommendedNextCallsForRootCause(rootCause),
|
|
5198
|
+
...afterPreflight.recommended_next_calls
|
|
5199
|
+
]);
|
|
5200
|
+
return {
|
|
5201
|
+
root_cause_code: rootCause,
|
|
5202
|
+
actions_attempted,
|
|
5203
|
+
actions_succeeded,
|
|
5204
|
+
actions_failed,
|
|
5205
|
+
restart_required: restartRequired,
|
|
5206
|
+
verification_passed: verificationPassed,
|
|
5207
|
+
status_label: statusContract.status_label,
|
|
5208
|
+
status_action: effectiveStatusAction,
|
|
5209
|
+
action_required: effectiveActionRequired,
|
|
5210
|
+
recommended_next_calls: recommendedNextCalls,
|
|
5211
|
+
diagnostics: {
|
|
5212
|
+
runtime_mode: RUNTIME_MODE,
|
|
5213
|
+
workspace_id: afterPreflight.trust_state.workspace_id,
|
|
5214
|
+
root_path: afterPreflight.trust_state.root_path,
|
|
5215
|
+
project_ref: afterPreflight.trust_state.project_ref,
|
|
5216
|
+
retrieval_readiness_before: beforePreflight.retrieval_readiness,
|
|
5217
|
+
retrieval_readiness_after: afterPreflight.retrieval_readiness,
|
|
5218
|
+
project_retrieval_readiness_after: afterPreflight.project_retrieval_readiness,
|
|
5219
|
+
retrieval_readiness_label: retrievalReadinessLabel(afterPreflight.retrieval_readiness),
|
|
5220
|
+
retrieval_readiness_action: retrievalReadinessAction(afterPreflight.retrieval_readiness),
|
|
5221
|
+
trust_score: trustScore,
|
|
5222
|
+
semantic_failure_rate: semanticStats.rate,
|
|
5223
|
+
semantic_attempts: semanticStats.attempts,
|
|
5224
|
+
refresh: refreshResult,
|
|
5225
|
+
source_repair: sourceRepair,
|
|
5226
|
+
verification,
|
|
5227
|
+
auto_heal_triggered: autoHeal,
|
|
5228
|
+
transition_improved: transitionImproved,
|
|
5229
|
+
explicit_blocker: explicitBlocker,
|
|
5230
|
+
recommended_next_calls: recommendedNextCalls,
|
|
5231
|
+
restart_hint: restartRequired ? "Set WHISPER_MCP_MODE=auto in MCP config and restart client." : null
|
|
5232
|
+
}
|
|
5233
|
+
};
|
|
5234
|
+
}
|
|
4634
5235
|
function toTextResult(payload) {
|
|
4635
5236
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
4636
5237
|
}
|
|
@@ -4697,42 +5298,6 @@ function resolveForgetQueryCandidates(rawResults, query) {
|
|
|
4697
5298
|
}
|
|
4698
5299
|
return { memory_ids: [], resolved_by: "none", warning: "Query did not resolve to a reliable memory match. No memories were changed." };
|
|
4699
5300
|
}
|
|
4700
|
-
async function searchMemoriesForContextQuery(args) {
|
|
4701
|
-
return whisper.searchMemoriesSOTA({
|
|
4702
|
-
project: args.project,
|
|
4703
|
-
query: args.query,
|
|
4704
|
-
user_id: args.user_id,
|
|
4705
|
-
session_id: args.session_id,
|
|
4706
|
-
top_k: args.top_k ?? 5,
|
|
4707
|
-
include_relations: false,
|
|
4708
|
-
include_pending: true
|
|
4709
|
-
});
|
|
4710
|
-
}
|
|
4711
|
-
async function runContextQueryMemoryRescue(args) {
|
|
4712
|
-
const scoped = await searchMemoriesForContextQuery(args);
|
|
4713
|
-
const scopedResults = formatCanonicalMemoryResults(scoped);
|
|
4714
|
-
if (scopedResults.length > 0) {
|
|
4715
|
-
return { results: scopedResults, rescue_mode: "scoped" };
|
|
4716
|
-
}
|
|
4717
|
-
if (args.user_id || args.session_id) {
|
|
4718
|
-
return { results: [], rescue_mode: null };
|
|
4719
|
-
}
|
|
4720
|
-
const broad = await searchMemoriesForContextQuery({
|
|
4721
|
-
project: args.project,
|
|
4722
|
-
query: args.query,
|
|
4723
|
-
top_k: args.top_k
|
|
4724
|
-
});
|
|
4725
|
-
return { results: formatCanonicalMemoryResults(broad), rescue_mode: formatCanonicalMemoryResults(broad).length > 0 ? "project_broad" : null };
|
|
4726
|
-
}
|
|
4727
|
-
function renderContextQueryMemoryRescue(args) {
|
|
4728
|
-
const lines = args.results.map(
|
|
4729
|
-
(result, index) => `${index + 1}. [${result.memory_type || "memory"}, score: ${result.similarity ?? "n/a"}] ${result.content}`
|
|
4730
|
-
);
|
|
4731
|
-
const scopeLabel = args.rescue_mode === "project_broad" ? `project=${args.project}, project_broad_memory_rescue=true` : `project=${args.project}, user=${args.scope.userId}, session=${args.scope.sessionId}, scoped_memory_rescue=true`;
|
|
4732
|
-
return `Found ${args.results.length} memory result(s) (${scopeLabel}):
|
|
4733
|
-
|
|
4734
|
-
${lines.join("\n\n")}`;
|
|
4735
|
-
}
|
|
4736
5301
|
function likelyEmbeddingFailure(error) {
|
|
4737
5302
|
const message = String(error?.message || error || "").toLowerCase();
|
|
4738
5303
|
return message.includes("embedding") || message.includes("vector") || message.includes("timeout") || message.includes("timed out") || message.includes("temporarily unavailable");
|
|
@@ -4753,7 +5318,13 @@ async function queryWithDegradedFallback(params) {
|
|
|
4753
5318
|
hybrid: true,
|
|
4754
5319
|
rerank: true
|
|
4755
5320
|
});
|
|
4756
|
-
return {
|
|
5321
|
+
return {
|
|
5322
|
+
response,
|
|
5323
|
+
degraded_mode: false,
|
|
5324
|
+
semantic_status: "ok",
|
|
5325
|
+
fallback_mode: "none",
|
|
5326
|
+
recommended_fixes: []
|
|
5327
|
+
};
|
|
4757
5328
|
} catch (error) {
|
|
4758
5329
|
if (!likelyEmbeddingFailure(error)) throw error;
|
|
4759
5330
|
const response = await whisper.query({
|
|
@@ -4776,7 +5347,13 @@ async function queryWithDegradedFallback(params) {
|
|
|
4776
5347
|
response,
|
|
4777
5348
|
degraded_mode: true,
|
|
4778
5349
|
degraded_reason: "Embedding/graph path unavailable; lexical fallback used.",
|
|
4779
|
-
recommendation: "Check embedding service health, then re-run for full hybrid quality."
|
|
5350
|
+
recommendation: "Check embedding service health, then re-run for full hybrid quality.",
|
|
5351
|
+
semantic_status: "failed",
|
|
5352
|
+
fallback_mode: "lexical_backend",
|
|
5353
|
+
recommended_fixes: [
|
|
5354
|
+
"Run fix to auto-repair retrieval health.",
|
|
5355
|
+
"If this persists, re-authenticate and reindex workspace sources."
|
|
5356
|
+
]
|
|
4780
5357
|
};
|
|
4781
5358
|
}
|
|
4782
5359
|
}
|
|
@@ -4888,6 +5465,47 @@ async function runSearchCodeSearch(args) {
|
|
|
4888
5465
|
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."
|
|
4889
5466
|
};
|
|
4890
5467
|
}
|
|
5468
|
+
function retrievalReadinessLabel(readiness) {
|
|
5469
|
+
if (readiness === "project_repo_source_ready") return "Project retrieval is healthy.";
|
|
5470
|
+
if (readiness === "project_repo_source_stale") return "Project source exists but is not ready.";
|
|
5471
|
+
if (readiness === "project_bound_no_repo_source") return "No matching project source is connected.";
|
|
5472
|
+
if (readiness === "project_unverified") return "Project/source verification failed.";
|
|
5473
|
+
if (readiness === "local_fallback") return "Using local fallback retrieval.";
|
|
5474
|
+
if (readiness === "no_project") return "No project is bound to this workspace.";
|
|
5475
|
+
return "Retrieval readiness is unknown.";
|
|
5476
|
+
}
|
|
5477
|
+
function retrievalReadinessAction(readiness) {
|
|
5478
|
+
if (readiness === "project_repo_source_ready") return "none";
|
|
5479
|
+
if (readiness === "project_repo_source_stale") return "Run fix or index.workspace_run to refresh workspace trust.";
|
|
5480
|
+
if (readiness === "project_bound_no_repo_source") {
|
|
5481
|
+
return RUNTIME_MODE === "remote" ? "Connect a repo source with context.add_source, then run fix." : "Run index.local_scan_ingest or fix.";
|
|
5482
|
+
}
|
|
5483
|
+
if (readiness === "project_unverified") return "Re-authenticate your API key and run fix.";
|
|
5484
|
+
if (readiness === "local_fallback") return "Run fix to restore project-grounded retrieval.";
|
|
5485
|
+
if (readiness === "no_project") return "Provide --project/WHISPER_PROJECT and run fix.";
|
|
5486
|
+
return "Run fix.";
|
|
5487
|
+
}
|
|
5488
|
+
function buildRecommendedFixes(args) {
|
|
5489
|
+
const fixes = /* @__PURE__ */ new Set();
|
|
5490
|
+
if (args.semantic_status === "failed") {
|
|
5491
|
+
fixes.add("Run fix to auto-repair retrieval health.");
|
|
5492
|
+
}
|
|
5493
|
+
if (args.fallback_mode === "lexical_rescue" || args.fallback_mode === "lexical_backend") {
|
|
5494
|
+
fixes.add("Check embedding/vector service health and retry.");
|
|
5495
|
+
}
|
|
5496
|
+
if (args.retrieval_readiness === "project_bound_no_repo_source") {
|
|
5497
|
+
fixes.add(
|
|
5498
|
+
RUNTIME_MODE === "remote" ? "Add a matching source with context.add_source." : "Ingest local files with index.local_scan_ingest."
|
|
5499
|
+
);
|
|
5500
|
+
}
|
|
5501
|
+
if (args.retrieval_readiness === "project_unverified") {
|
|
5502
|
+
fixes.add("Re-authenticate (valid WHISPER_API_KEY) and rerun fix.");
|
|
5503
|
+
}
|
|
5504
|
+
if (args.retrieval_readiness === "project_repo_source_stale") {
|
|
5505
|
+
fixes.add("Refresh workspace metadata via index.workspace_run.");
|
|
5506
|
+
}
|
|
5507
|
+
return [...fixes];
|
|
5508
|
+
}
|
|
4891
5509
|
function buildLocalWorkspaceDocuments(rootPath, allowedExts, maxFiles) {
|
|
4892
5510
|
const files = collectCodeFiles(rootPath, allowedExts, maxFiles);
|
|
4893
5511
|
const documents = [];
|
|
@@ -5023,20 +5641,6 @@ function localHitsToEvidence(workspaceId, hits) {
|
|
|
5023
5641
|
)
|
|
5024
5642
|
);
|
|
5025
5643
|
}
|
|
5026
|
-
function renderLocalWorkspaceContext(args) {
|
|
5027
|
-
const lines = args.hits.map((hit, index) => {
|
|
5028
|
-
const location = hit.line_end && hit.line_end !== hit.line_start ? `${hit.id}:${hit.line_start}-${hit.line_end}` : `${hit.id}:${hit.line_start}`;
|
|
5029
|
-
return `${index + 1}. [${location}, score: ${hit.score.toFixed(2)}, mode: ${hit.search_mode}] ${hit.raw_snippet || hit.snippet || hit.id}`;
|
|
5030
|
-
});
|
|
5031
|
-
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}):`;
|
|
5032
|
-
const warnings = args.warnings.length ? `
|
|
5033
|
-
|
|
5034
|
-
[warnings]
|
|
5035
|
-
${args.warnings.join("\n")}` : "";
|
|
5036
|
-
return `${header}
|
|
5037
|
-
|
|
5038
|
-
${lines.join("\n\n")}${warnings}`;
|
|
5039
|
-
}
|
|
5040
5644
|
async function runSearchCodeTool(args) {
|
|
5041
5645
|
const rootPath = canonicalizeWorkspacePath(args.path);
|
|
5042
5646
|
const allowedExts = args.file_types ? new Set(args.file_types) : CODE_EXTENSIONS;
|
|
@@ -5044,12 +5648,21 @@ async function runSearchCodeTool(args) {
|
|
|
5044
5648
|
if (files.length === 0) {
|
|
5045
5649
|
const workspace = await resolveWorkspaceTrust({ path: rootPath });
|
|
5046
5650
|
const sharedWarnings = [...workspace.warnings];
|
|
5651
|
+
const semanticStatus = "ok";
|
|
5652
|
+
const fallbackMode = "none";
|
|
5047
5653
|
return {
|
|
5048
5654
|
tool: "search_code",
|
|
5049
5655
|
query: args.query,
|
|
5050
5656
|
path: rootPath,
|
|
5051
5657
|
results: [],
|
|
5052
5658
|
count: 0,
|
|
5659
|
+
semantic_status: semanticStatus,
|
|
5660
|
+
fallback_mode: fallbackMode,
|
|
5661
|
+
recommended_fixes: buildRecommendedFixes({
|
|
5662
|
+
retrieval_readiness: workspace.project_ref ? "project_repo_source_ready" : "no_project",
|
|
5663
|
+
semantic_status: semanticStatus,
|
|
5664
|
+
fallback_mode: fallbackMode
|
|
5665
|
+
}),
|
|
5053
5666
|
warnings: sharedWarnings,
|
|
5054
5667
|
diagnostics: {
|
|
5055
5668
|
workspace_id: workspace.workspace_id,
|
|
@@ -5078,6 +5691,13 @@ async function runSearchCodeTool(args) {
|
|
|
5078
5691
|
path: rootPath,
|
|
5079
5692
|
results: localRetrieval.results,
|
|
5080
5693
|
count: localRetrieval.results.length,
|
|
5694
|
+
semantic_status: semanticStatusFromSearchMode(localRetrieval.diagnostics.search_mode),
|
|
5695
|
+
fallback_mode: fallbackModeFromSearchMode(localRetrieval.diagnostics.search_mode),
|
|
5696
|
+
recommended_fixes: buildRecommendedFixes({
|
|
5697
|
+
retrieval_readiness: localRetrieval.workspace.project_ref ? "local_fallback" : "no_project",
|
|
5698
|
+
semantic_status: semanticStatusFromSearchMode(localRetrieval.diagnostics.search_mode),
|
|
5699
|
+
fallback_mode: fallbackModeFromSearchMode(localRetrieval.diagnostics.search_mode)
|
|
5700
|
+
}),
|
|
5081
5701
|
warnings: localRetrieval.warnings,
|
|
5082
5702
|
diagnostics: localRetrieval.diagnostics
|
|
5083
5703
|
};
|
|
@@ -5331,7 +5951,8 @@ function renderScopedMcpConfig(project, source, client) {
|
|
|
5331
5951
|
env: {
|
|
5332
5952
|
WHISPER_API_KEY: "wctx_...",
|
|
5333
5953
|
WHISPER_PROJECT: project,
|
|
5334
|
-
WHISPER_SCOPE_SOURCE: source
|
|
5954
|
+
WHISPER_SCOPE_SOURCE: source,
|
|
5955
|
+
WHISPER_MCP_MODE: "auto"
|
|
5335
5956
|
}
|
|
5336
5957
|
};
|
|
5337
5958
|
if (client === "json") {
|
|
@@ -5437,6 +6058,7 @@ server.tool(
|
|
|
5437
6058
|
},
|
|
5438
6059
|
async ({ workspace_id, path, mode, max_files }) => {
|
|
5439
6060
|
try {
|
|
6061
|
+
const startAt = Date.now();
|
|
5440
6062
|
const identity = resolveWorkspaceIdentity({ path, workspace_id });
|
|
5441
6063
|
const rootPath = identity.root_path;
|
|
5442
6064
|
const workspaceId = identity.workspace_id;
|
|
@@ -5445,11 +6067,17 @@ server.tool(
|
|
|
5445
6067
|
const fileStats = countCodeFiles(rootPath, max_files);
|
|
5446
6068
|
const coverage = Math.max(0, Math.min(1, fileStats.total / Math.max(1, max_files)));
|
|
5447
6069
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6070
|
+
const snapshot = computeWorkspaceSnapshot(rootPath);
|
|
5448
6071
|
workspace.root_path = rootPath;
|
|
5449
6072
|
workspace.index_metadata = {
|
|
5450
6073
|
last_indexed_at: now,
|
|
5451
6074
|
last_indexed_commit: getGitHead(rootPath),
|
|
5452
|
-
coverage: mode === "full" ? 1 : coverage
|
|
6075
|
+
coverage: mode === "full" ? 1 : coverage,
|
|
6076
|
+
indexed_files: fileStats.total,
|
|
6077
|
+
last_auto_refresh_at: now,
|
|
6078
|
+
...mode === "full" ? { last_full_refresh_at: now } : {},
|
|
6079
|
+
last_snapshot_hash: snapshot.hash,
|
|
6080
|
+
last_snapshot_entries: snapshot.entries
|
|
5453
6081
|
};
|
|
5454
6082
|
saveState(state);
|
|
5455
6083
|
const payload = {
|
|
@@ -5458,7 +6086,7 @@ server.tool(
|
|
|
5458
6086
|
mode,
|
|
5459
6087
|
indexed_files: fileStats.total,
|
|
5460
6088
|
skipped_files: fileStats.skipped,
|
|
5461
|
-
duration_ms:
|
|
6089
|
+
duration_ms: Date.now() - startAt,
|
|
5462
6090
|
warnings: fileStats.total === 0 ? ["No code files discovered for indexing."] : [],
|
|
5463
6091
|
index_metadata: workspace.index_metadata
|
|
5464
6092
|
};
|
|
@@ -5504,6 +6132,261 @@ server.tool(
|
|
|
5504
6132
|
}
|
|
5505
6133
|
}
|
|
5506
6134
|
);
|
|
6135
|
+
server.tool(
|
|
6136
|
+
"index.auto_repair",
|
|
6137
|
+
"Diagnose and apply deterministic retrieval/setup repairs. This is the canonical zero-stress repair path.",
|
|
6138
|
+
{
|
|
6139
|
+
path: z.string().optional(),
|
|
6140
|
+
workspace_id: z.string().optional(),
|
|
6141
|
+
project: z.string().optional(),
|
|
6142
|
+
apply: z.boolean().optional().default(true),
|
|
6143
|
+
repair_scope: z.enum(["safe", "full"]).optional().default("safe"),
|
|
6144
|
+
allow_restart_hint: z.boolean().optional().default(true)
|
|
6145
|
+
},
|
|
6146
|
+
async ({ path, workspace_id, project, apply, repair_scope, allow_restart_hint }) => {
|
|
6147
|
+
try {
|
|
6148
|
+
const payload = await runAutoRepairFlow({
|
|
6149
|
+
path,
|
|
6150
|
+
workspace_id,
|
|
6151
|
+
project,
|
|
6152
|
+
apply,
|
|
6153
|
+
repair_scope,
|
|
6154
|
+
allow_restart_hint
|
|
6155
|
+
});
|
|
6156
|
+
return toTextResult(payload);
|
|
6157
|
+
} catch (error) {
|
|
6158
|
+
return primaryToolError(error.message, "auto_repair_failed");
|
|
6159
|
+
}
|
|
6160
|
+
}
|
|
6161
|
+
);
|
|
6162
|
+
server.tool(
|
|
6163
|
+
"fix",
|
|
6164
|
+
"Primary alias for zero-stress setup/retrieval repair. Delegates to index.auto_repair.",
|
|
6165
|
+
{
|
|
6166
|
+
path: z.string().optional(),
|
|
6167
|
+
workspace_id: z.string().optional(),
|
|
6168
|
+
project: z.string().optional(),
|
|
6169
|
+
apply: z.boolean().optional().default(true),
|
|
6170
|
+
repair_scope: z.enum(["safe", "full"]).optional().default("safe"),
|
|
6171
|
+
allow_restart_hint: z.boolean().optional().default(true)
|
|
6172
|
+
},
|
|
6173
|
+
async ({ path, workspace_id, project, apply, repair_scope, allow_restart_hint }) => {
|
|
6174
|
+
try {
|
|
6175
|
+
const payload = await runAutoRepairFlow({
|
|
6176
|
+
path,
|
|
6177
|
+
workspace_id,
|
|
6178
|
+
project,
|
|
6179
|
+
apply,
|
|
6180
|
+
repair_scope,
|
|
6181
|
+
allow_restart_hint
|
|
6182
|
+
});
|
|
6183
|
+
return toTextResult(payload);
|
|
6184
|
+
} catch (error) {
|
|
6185
|
+
return primaryToolError(error.message, "fix_failed");
|
|
6186
|
+
}
|
|
6187
|
+
}
|
|
6188
|
+
);
|
|
6189
|
+
async function runUnifiedContextRetrieval(params) {
|
|
6190
|
+
const preflight = await resolveRepoGroundingPreflight({
|
|
6191
|
+
query: params.question,
|
|
6192
|
+
path: params.path,
|
|
6193
|
+
workspace_id: params.workspace_id,
|
|
6194
|
+
project: params.project,
|
|
6195
|
+
chunk_types: params.chunk_types
|
|
6196
|
+
});
|
|
6197
|
+
const retrievalProfile = resolveMcpRetrievalProfile(params.retrieval_profile);
|
|
6198
|
+
const backgroundRefresh = await runBackgroundWorkspaceRefresh({
|
|
6199
|
+
workspace_id: preflight.trust_state.workspace_id,
|
|
6200
|
+
root_path: preflight.trust_state.root_path,
|
|
6201
|
+
force_full: false
|
|
6202
|
+
});
|
|
6203
|
+
if (shouldAbstainForCodebaseTrust({
|
|
6204
|
+
retrievalProfile,
|
|
6205
|
+
codebaseIntent: preflight.repo_grounded,
|
|
6206
|
+
preflight
|
|
6207
|
+
})) {
|
|
6208
|
+
const abstainPayload = buildCodebaseTrustAbstainPayload({
|
|
6209
|
+
query: params.question,
|
|
6210
|
+
preflight,
|
|
6211
|
+
retrievalProfile
|
|
6212
|
+
});
|
|
6213
|
+
return {
|
|
6214
|
+
...abstainPayload,
|
|
6215
|
+
semantic_status: "failed",
|
|
6216
|
+
fallback_mode: "none",
|
|
6217
|
+
recommended_fixes: buildRecommendedFixes({
|
|
6218
|
+
retrieval_readiness: preflight.retrieval_readiness,
|
|
6219
|
+
semantic_status: "failed",
|
|
6220
|
+
fallback_mode: "none"
|
|
6221
|
+
}),
|
|
6222
|
+
status_label: "blocked",
|
|
6223
|
+
status_action: "reindex",
|
|
6224
|
+
action_required: true,
|
|
6225
|
+
diagnostics: {
|
|
6226
|
+
retrieval_profile: retrievalProfile,
|
|
6227
|
+
background_refresh: backgroundRefresh
|
|
6228
|
+
}
|
|
6229
|
+
};
|
|
6230
|
+
}
|
|
6231
|
+
const resolvedProject = preflight.trust_state.project_ref || await resolveProjectRef(params.project);
|
|
6232
|
+
const scope = resolveMcpScope({
|
|
6233
|
+
project: resolvedProject || params.project,
|
|
6234
|
+
user_id: params.user_id,
|
|
6235
|
+
session_id: params.session_id,
|
|
6236
|
+
path: preflight.trust_state.root_path
|
|
6237
|
+
});
|
|
6238
|
+
const topK = params.top_k ?? 12;
|
|
6239
|
+
let semanticStatus = "ok";
|
|
6240
|
+
let fallbackMode = "none";
|
|
6241
|
+
let contextText = "";
|
|
6242
|
+
let evidence = [];
|
|
6243
|
+
let warnings = [...preflight.warnings];
|
|
6244
|
+
let retrievalReadiness = preflight.retrieval_readiness;
|
|
6245
|
+
let retrievalRoute = preflight.retrieval_route;
|
|
6246
|
+
let latencyMs = 0;
|
|
6247
|
+
if (preflight.retrieval_route === "local_workspace_fallback") {
|
|
6248
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
6249
|
+
query: params.question,
|
|
6250
|
+
path: preflight.trust_state.root_path,
|
|
6251
|
+
project: resolvedProject || params.project,
|
|
6252
|
+
top_k: topK
|
|
6253
|
+
});
|
|
6254
|
+
semanticStatus = semanticStatusFromSearchMode(localRetrieval.diagnostics.search_mode);
|
|
6255
|
+
fallbackMode = fallbackModeFromSearchMode(localRetrieval.diagnostics.search_mode);
|
|
6256
|
+
evidence = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results);
|
|
6257
|
+
contextText = evidence.map((item) => `[${renderCitation(item)}] ${item.snippet || "Relevant context found."}`).join("\n");
|
|
6258
|
+
warnings = Array.from(/* @__PURE__ */ new Set([...warnings, ...localRetrieval.warnings]));
|
|
6259
|
+
retrievalReadiness = "local_fallback";
|
|
6260
|
+
retrievalRoute = "local_workspace_fallback";
|
|
6261
|
+
} else if (!resolvedProject) {
|
|
6262
|
+
contextText = "";
|
|
6263
|
+
evidence = [];
|
|
6264
|
+
retrievalRoute = "none";
|
|
6265
|
+
} else {
|
|
6266
|
+
const queryResult = await queryWithDegradedFallback({
|
|
6267
|
+
project: resolvedProject,
|
|
6268
|
+
query: params.question,
|
|
6269
|
+
top_k: topK,
|
|
6270
|
+
include_memories: preflight.repo_grounded ? false : params.include_memories,
|
|
6271
|
+
include_graph: params.include_graph,
|
|
6272
|
+
session_id: params.session_id,
|
|
6273
|
+
user_id: scope.userId,
|
|
6274
|
+
source_ids: preflight.repo_grounded ? preflight.matched_source_ids : void 0,
|
|
6275
|
+
retrieval_profile: retrievalProfile,
|
|
6276
|
+
include_parent_content: params.include_parent_content
|
|
6277
|
+
});
|
|
6278
|
+
latencyMs = queryResult.response.meta?.latency_ms || 0;
|
|
6279
|
+
semanticStatus = queryResult.semantic_status || "ok";
|
|
6280
|
+
fallbackMode = queryResult.fallback_mode || "none";
|
|
6281
|
+
const rawResults = preflight.repo_grounded ? filterProjectRepoResults(queryResult.response.results || [], preflight.matched_source_ids) : queryResult.response.results || [];
|
|
6282
|
+
if (preflight.repo_grounded && rawResults.length === 0) {
|
|
6283
|
+
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
6284
|
+
query: params.question,
|
|
6285
|
+
path: preflight.trust_state.root_path,
|
|
6286
|
+
project: resolvedProject,
|
|
6287
|
+
top_k: topK
|
|
6288
|
+
});
|
|
6289
|
+
semanticStatus = semanticStatusFromSearchMode(localRetrieval.diagnostics.search_mode);
|
|
6290
|
+
fallbackMode = fallbackModeFromSearchMode(localRetrieval.diagnostics.search_mode);
|
|
6291
|
+
evidence = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results);
|
|
6292
|
+
contextText = evidence.map((item) => `[${renderCitation(item)}] ${item.snippet || "Relevant context found."}`).join("\n");
|
|
6293
|
+
warnings = Array.from(/* @__PURE__ */ new Set([...warnings, ...localRetrieval.warnings]));
|
|
6294
|
+
retrievalReadiness = "local_fallback";
|
|
6295
|
+
retrievalRoute = "local_workspace_fallback";
|
|
6296
|
+
} else {
|
|
6297
|
+
contextText = queryResult.response.context || "";
|
|
6298
|
+
evidence = rawResults.map((r) => toEvidenceRef(r, preflight.trust_state.workspace_id, "semantic"));
|
|
6299
|
+
if (queryResult.degraded_mode) {
|
|
6300
|
+
warnings.push(queryResult.degraded_reason || "Lexical backend fallback active.");
|
|
6301
|
+
}
|
|
6302
|
+
retrievalRoute = preflight.repo_grounded ? "project_repo" : "none";
|
|
6303
|
+
}
|
|
6304
|
+
}
|
|
6305
|
+
recordSemanticAttempt(preflight.trust_state.workspace_id, semanticStatus === "failed");
|
|
6306
|
+
const semanticStats = getWorkspaceSemanticFailureStats(preflight.trust_state.workspace_id);
|
|
6307
|
+
const trustScore = computeTrustScore({
|
|
6308
|
+
retrieval_readiness: retrievalReadiness,
|
|
6309
|
+
workspace_health: preflight.trust_state.health,
|
|
6310
|
+
semantic_failure_rate: semanticStats.rate
|
|
6311
|
+
});
|
|
6312
|
+
persistTrustScore(preflight.trust_state.workspace_id, trustScore);
|
|
6313
|
+
const autoHeal = shouldAutoHeal({
|
|
6314
|
+
retrieval_readiness: retrievalReadiness,
|
|
6315
|
+
trust_score: trustScore,
|
|
6316
|
+
semantic_failure_rate: semanticStats.rate,
|
|
6317
|
+
semantic_attempts: semanticStats.attempts
|
|
6318
|
+
});
|
|
6319
|
+
let autoRepairSummary = null;
|
|
6320
|
+
if (autoHeal) {
|
|
6321
|
+
try {
|
|
6322
|
+
autoRepairSummary = await runAutoRepairFlow({
|
|
6323
|
+
path: preflight.trust_state.root_path,
|
|
6324
|
+
workspace_id: preflight.trust_state.workspace_id,
|
|
6325
|
+
project: resolvedProject || params.project,
|
|
6326
|
+
apply: true,
|
|
6327
|
+
repair_scope: "safe",
|
|
6328
|
+
allow_restart_hint: true
|
|
6329
|
+
});
|
|
6330
|
+
warnings.push("Auto-repair executed in safe mode.");
|
|
6331
|
+
} catch (error) {
|
|
6332
|
+
warnings.push(`Auto-repair failed: ${String(error?.message || error)}`);
|
|
6333
|
+
}
|
|
6334
|
+
}
|
|
6335
|
+
const statusContract = statusContractForReadiness({
|
|
6336
|
+
retrieval_readiness: retrievalReadiness,
|
|
6337
|
+
workspace_health: preflight.trust_state.health,
|
|
6338
|
+
semantic_status: semanticStatus,
|
|
6339
|
+
force_repairing: autoHeal
|
|
6340
|
+
});
|
|
6341
|
+
const recommendedFixes = buildRecommendedFixes({
|
|
6342
|
+
retrieval_readiness: retrievalReadiness,
|
|
6343
|
+
semantic_status: semanticStatus,
|
|
6344
|
+
fallback_mode: fallbackMode
|
|
6345
|
+
});
|
|
6346
|
+
const recommendedNextCalls = sanitizeRecommendedNextCalls([
|
|
6347
|
+
...preflight.recommended_next_calls,
|
|
6348
|
+
...semanticStatus === "failed" ? ["fix"] : []
|
|
6349
|
+
]);
|
|
6350
|
+
const payload = {
|
|
6351
|
+
question: params.question,
|
|
6352
|
+
workspace_id: preflight.trust_state.workspace_id,
|
|
6353
|
+
root_path: preflight.trust_state.root_path,
|
|
6354
|
+
trust_state: preflight.trust_state,
|
|
6355
|
+
retrieval_readiness: retrievalReadiness,
|
|
6356
|
+
retrieval_readiness_label: retrievalReadinessLabel(retrievalReadiness),
|
|
6357
|
+
retrieval_readiness_action: retrievalReadinessAction(retrievalReadiness),
|
|
6358
|
+
retrieval_route: retrievalRoute,
|
|
6359
|
+
context: contextText,
|
|
6360
|
+
evidence,
|
|
6361
|
+
total_results: evidence.length,
|
|
6362
|
+
latency_ms: latencyMs,
|
|
6363
|
+
semantic_status: semanticStatus,
|
|
6364
|
+
fallback_mode: fallbackMode,
|
|
6365
|
+
recommended_fixes: recommendedFixes,
|
|
6366
|
+
status_label: statusContract.status_label,
|
|
6367
|
+
status_action: statusContract.status_action,
|
|
6368
|
+
action_required: statusContract.action_required,
|
|
6369
|
+
warnings: Array.from(new Set(warnings)),
|
|
6370
|
+
recommended_next_calls: recommendedNextCalls,
|
|
6371
|
+
diagnostics: {
|
|
6372
|
+
scope: {
|
|
6373
|
+
project: resolvedProject || params.project || null,
|
|
6374
|
+
user_id: scope.userId,
|
|
6375
|
+
session_id: params.session_id || null
|
|
6376
|
+
},
|
|
6377
|
+
retrieval_profile: retrievalProfile,
|
|
6378
|
+
trust_score: trustScore,
|
|
6379
|
+
semantic_failure_rate: semanticStats.rate,
|
|
6380
|
+
auto_heal_triggered: autoHeal,
|
|
6381
|
+
auto_repair: autoRepairSummary,
|
|
6382
|
+
background_refresh: backgroundRefresh
|
|
6383
|
+
}
|
|
6384
|
+
};
|
|
6385
|
+
if (params.include_alias_warning) {
|
|
6386
|
+
payload.warnings = Array.from(/* @__PURE__ */ new Set([...payload.warnings, "deprecated_alias_use_context.query"]));
|
|
6387
|
+
}
|
|
6388
|
+
return payload;
|
|
6389
|
+
}
|
|
5507
6390
|
server.tool(
|
|
5508
6391
|
"context.get_relevant",
|
|
5509
6392
|
"Default grounded retrieval step for workspace/project questions. Returns ranked evidence with file:line citations and may abstain when workspace/repo trust is not ready.",
|
|
@@ -5522,131 +6405,23 @@ server.tool(
|
|
|
5522
6405
|
},
|
|
5523
6406
|
async ({ question, path, workspace_id, project, top_k, include_memories, include_graph, session_id, user_id, include_parent_content, retrieval_profile }) => {
|
|
5524
6407
|
try {
|
|
5525
|
-
const
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
preflight
|
|
5531
|
-
})) {
|
|
5532
|
-
return toTextResult(buildCodebaseTrustAbstainPayload({
|
|
5533
|
-
query: question,
|
|
5534
|
-
preflight,
|
|
5535
|
-
retrievalProfile
|
|
5536
|
-
}));
|
|
5537
|
-
}
|
|
5538
|
-
if (preflight.retrieval_route === "local_workspace_fallback") {
|
|
5539
|
-
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5540
|
-
query: question,
|
|
5541
|
-
path: preflight.trust_state.root_path,
|
|
5542
|
-
project: preflight.trust_state.project_ref || project,
|
|
5543
|
-
top_k
|
|
5544
|
-
});
|
|
5545
|
-
const evidence2 = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results);
|
|
5546
|
-
const payload2 = {
|
|
5547
|
-
question,
|
|
5548
|
-
workspace_id: preflight.trust_state.workspace_id,
|
|
5549
|
-
root_path: preflight.trust_state.root_path,
|
|
5550
|
-
identity_source: preflight.trust_state.identity_source,
|
|
5551
|
-
trust_state: preflight.trust_state,
|
|
5552
|
-
retrieval_readiness: preflight.retrieval_readiness,
|
|
5553
|
-
retrieval_route: preflight.retrieval_route,
|
|
5554
|
-
grounded_to_workspace: true,
|
|
5555
|
-
total_results: evidence2.length,
|
|
5556
|
-
context: evidence2.map((item) => `[${renderCitation(item)}] ${item.snippet || "Relevant local workspace context found."}`).join("\n"),
|
|
5557
|
-
evidence: evidence2,
|
|
5558
|
-
used_context_ids: evidence2.map((item) => item.source_id),
|
|
5559
|
-
latency_ms: 0,
|
|
5560
|
-
warnings: Array.from(/* @__PURE__ */ new Set([...preflight.warnings, ...localRetrieval.warnings])),
|
|
5561
|
-
recommended_next_calls: preflight.recommended_next_calls
|
|
5562
|
-
};
|
|
5563
|
-
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
5564
|
-
}
|
|
5565
|
-
if (!preflight.trust_state.project_ref) {
|
|
5566
|
-
const payload2 = {
|
|
5567
|
-
question,
|
|
5568
|
-
workspace_id: preflight.trust_state.workspace_id,
|
|
5569
|
-
root_path: preflight.trust_state.root_path,
|
|
5570
|
-
identity_source: preflight.trust_state.identity_source,
|
|
5571
|
-
trust_state: preflight.trust_state,
|
|
5572
|
-
retrieval_readiness: preflight.retrieval_readiness,
|
|
5573
|
-
retrieval_route: "none",
|
|
5574
|
-
grounded_to_workspace: false,
|
|
5575
|
-
total_results: 0,
|
|
5576
|
-
context: "",
|
|
5577
|
-
evidence: [],
|
|
5578
|
-
used_context_ids: [],
|
|
5579
|
-
latency_ms: 0,
|
|
5580
|
-
warnings: preflight.warnings,
|
|
5581
|
-
recommended_next_calls: preflight.recommended_next_calls
|
|
5582
|
-
};
|
|
5583
|
-
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
5584
|
-
}
|
|
5585
|
-
const queryResult = await queryWithDegradedFallback({
|
|
5586
|
-
project: preflight.trust_state.project_ref,
|
|
5587
|
-
query: question,
|
|
6408
|
+
const payload = await runUnifiedContextRetrieval({
|
|
6409
|
+
question,
|
|
6410
|
+
path,
|
|
6411
|
+
workspace_id,
|
|
6412
|
+
project,
|
|
5588
6413
|
top_k,
|
|
5589
|
-
include_memories
|
|
6414
|
+
include_memories,
|
|
5590
6415
|
include_graph,
|
|
5591
6416
|
session_id,
|
|
5592
6417
|
user_id,
|
|
5593
|
-
|
|
5594
|
-
retrieval_profile
|
|
5595
|
-
|
|
6418
|
+
include_parent_content,
|
|
6419
|
+
retrieval_profile,
|
|
6420
|
+
include_alias_warning: true
|
|
5596
6421
|
});
|
|
5597
|
-
|
|
5598
|
-
const rawResults = preflight.repo_grounded ? filterProjectRepoResults(response.results || [], preflight.matched_source_ids) : response.results || [];
|
|
5599
|
-
if (preflight.repo_grounded && rawResults.length === 0) {
|
|
5600
|
-
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5601
|
-
query: question,
|
|
5602
|
-
path: preflight.trust_state.root_path,
|
|
5603
|
-
project: preflight.trust_state.project_ref,
|
|
5604
|
-
top_k
|
|
5605
|
-
});
|
|
5606
|
-
const evidence2 = localHitsToEvidence(preflight.trust_state.workspace_id, localRetrieval.results);
|
|
5607
|
-
const payload2 = {
|
|
5608
|
-
question,
|
|
5609
|
-
workspace_id: preflight.trust_state.workspace_id,
|
|
5610
|
-
root_path: preflight.trust_state.root_path,
|
|
5611
|
-
identity_source: preflight.trust_state.identity_source,
|
|
5612
|
-
trust_state: preflight.trust_state,
|
|
5613
|
-
retrieval_readiness: "local_fallback",
|
|
5614
|
-
retrieval_route: "local_workspace_fallback",
|
|
5615
|
-
grounded_to_workspace: true,
|
|
5616
|
-
total_results: evidence2.length,
|
|
5617
|
-
context: evidence2.map((item) => `[${renderCitation(item)}] ${item.snippet || "Relevant local workspace context found."}`).join("\n"),
|
|
5618
|
-
evidence: evidence2,
|
|
5619
|
-
used_context_ids: evidence2.map((item) => item.source_id),
|
|
5620
|
-
latency_ms: 0,
|
|
5621
|
-
warnings: Array.from(/* @__PURE__ */ new Set([...preflight.warnings, ...localRetrieval.warnings])),
|
|
5622
|
-
recommended_next_calls: preflight.recommended_next_calls
|
|
5623
|
-
};
|
|
5624
|
-
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
5625
|
-
}
|
|
5626
|
-
const evidence = rawResults.map((r) => toEvidenceRef(r, preflight.trust_state.workspace_id, "semantic"));
|
|
5627
|
-
const payload = {
|
|
5628
|
-
question,
|
|
5629
|
-
workspace_id: preflight.trust_state.workspace_id,
|
|
5630
|
-
root_path: preflight.trust_state.root_path,
|
|
5631
|
-
identity_source: preflight.trust_state.identity_source,
|
|
5632
|
-
trust_state: preflight.trust_state,
|
|
5633
|
-
retrieval_readiness: preflight.retrieval_readiness,
|
|
5634
|
-
retrieval_route: preflight.repo_grounded ? "project_repo" : "none",
|
|
5635
|
-
grounded_to_workspace: preflight.repo_grounded ? true : preflight.trust_state.grounded_to_workspace,
|
|
5636
|
-
total_results: rawResults.length || response.meta?.total || evidence.length,
|
|
5637
|
-
context: response.context || "",
|
|
5638
|
-
evidence,
|
|
5639
|
-
used_context_ids: rawResults.map((r) => String(r.id)),
|
|
5640
|
-
latency_ms: response.meta?.latency_ms || 0,
|
|
5641
|
-
degraded_mode: queryResult.degraded_mode,
|
|
5642
|
-
degraded_reason: queryResult.degraded_reason,
|
|
5643
|
-
recommendation: queryResult.recommendation,
|
|
5644
|
-
warnings: preflight.warnings,
|
|
5645
|
-
recommended_next_calls: preflight.recommended_next_calls
|
|
5646
|
-
};
|
|
5647
|
-
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
6422
|
+
return toTextResult(payload);
|
|
5648
6423
|
} catch (error) {
|
|
5649
|
-
return
|
|
6424
|
+
return primaryToolError(error.message, "context_get_relevant_failed");
|
|
5650
6425
|
}
|
|
5651
6426
|
}
|
|
5652
6427
|
);
|
|
@@ -5870,273 +6645,22 @@ server.tool(
|
|
|
5870
6645
|
},
|
|
5871
6646
|
async ({ project, query, path, top_k, chunk_types, include_memories, include_graph, user_id, session_id, max_tokens, include_parent_content, retrieval_profile }) => {
|
|
5872
6647
|
try {
|
|
5873
|
-
const
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
codebaseIntent: preflight.repo_grounded,
|
|
5878
|
-
preflight
|
|
5879
|
-
})) {
|
|
5880
|
-
return toTextResult(buildCodebaseTrustAbstainPayload({
|
|
5881
|
-
query,
|
|
5882
|
-
preflight,
|
|
5883
|
-
retrievalProfile
|
|
5884
|
-
}));
|
|
5885
|
-
}
|
|
5886
|
-
const resolvedProject = preflight.trust_state.project_ref || await resolveProjectRef(project);
|
|
5887
|
-
if (!resolvedProject && !preflight.repo_grounded) {
|
|
5888
|
-
return { content: [{ type: "text", text: "Error: No project resolved. Set WHISPER_PROJECT or pass project." }] };
|
|
5889
|
-
}
|
|
5890
|
-
const scope = resolveMcpScope({ project: resolvedProject, user_id, session_id, path: preflight.trust_state.root_path });
|
|
5891
|
-
if (preflight.repo_grounded && preflight.retrieval_route === "local_workspace_fallback") {
|
|
5892
|
-
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
5893
|
-
query,
|
|
5894
|
-
path: preflight.trust_state.root_path,
|
|
5895
|
-
project: resolvedProject || project,
|
|
5896
|
-
top_k
|
|
5897
|
-
});
|
|
5898
|
-
if (localRetrieval.results.length > 0) {
|
|
5899
|
-
return {
|
|
5900
|
-
content: [{
|
|
5901
|
-
type: "text",
|
|
5902
|
-
text: renderLocalWorkspaceContext({
|
|
5903
|
-
query,
|
|
5904
|
-
project_ref: resolvedProject || null,
|
|
5905
|
-
scope,
|
|
5906
|
-
route: "local_workspace_fallback",
|
|
5907
|
-
hits: localRetrieval.results,
|
|
5908
|
-
diagnostics: localRetrieval.diagnostics,
|
|
5909
|
-
warnings: Array.from(/* @__PURE__ */ new Set([...preflight.warnings, ...localRetrieval.warnings]))
|
|
5910
|
-
})
|
|
5911
|
-
}]
|
|
5912
|
-
};
|
|
5913
|
-
}
|
|
5914
|
-
}
|
|
5915
|
-
if (preflight.repo_grounded && resolvedProject) {
|
|
5916
|
-
const queryResult2 = await queryWithDegradedFallback({
|
|
5917
|
-
project: resolvedProject,
|
|
5918
|
-
query,
|
|
5919
|
-
top_k,
|
|
5920
|
-
include_memories: false,
|
|
5921
|
-
include_graph,
|
|
5922
|
-
user_id: user_id || scope.userId,
|
|
5923
|
-
session_id: session_id || scope.sessionId,
|
|
5924
|
-
source_ids: preflight.matched_source_ids,
|
|
5925
|
-
retrieval_profile: retrievalProfile,
|
|
5926
|
-
include_parent_content
|
|
5927
|
-
});
|
|
5928
|
-
const repoResults = filterProjectRepoResults(queryResult2.response.results || [], preflight.matched_source_ids);
|
|
5929
|
-
if (repoResults.length > 0) {
|
|
5930
|
-
const scopedResponse = { ...queryResult2.response, results: repoResults };
|
|
5931
|
-
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}):
|
|
5932
|
-
|
|
5933
|
-
`;
|
|
5934
|
-
const suffix2 = [
|
|
5935
|
-
`[diagnostics] identity_source=${preflight.trust_state.identity_source} retrieval_readiness=${preflight.retrieval_readiness} retrieval_route=project_repo`,
|
|
5936
|
-
queryResult2.degraded_mode ? `[degraded_mode=true] ${queryResult2.degraded_reason}
|
|
5937
|
-
Recommendation: ${queryResult2.recommendation}` : "",
|
|
5938
|
-
preflight.warnings.length ? `[warnings]
|
|
5939
|
-
${preflight.warnings.join("\n")}` : ""
|
|
5940
|
-
].filter(Boolean).join("\n\n");
|
|
5941
|
-
return { content: [{ type: "text", text: `${header2}${scopedResponse.context}${suffix2 ? `
|
|
5942
|
-
|
|
5943
|
-
${suffix2}` : ""}` }] };
|
|
5944
|
-
}
|
|
5945
|
-
}
|
|
5946
|
-
if (preflight.repo_grounded) {
|
|
5947
|
-
if (resolvedProject && include_memories !== false) {
|
|
5948
|
-
const memoryRescue = await runContextQueryMemoryRescue({
|
|
5949
|
-
project: resolvedProject,
|
|
5950
|
-
query,
|
|
5951
|
-
user_id: user_id ? scope.userId : void 0,
|
|
5952
|
-
session_id: session_id ? scope.sessionId : void 0,
|
|
5953
|
-
top_k
|
|
5954
|
-
});
|
|
5955
|
-
if (memoryRescue.results.length && memoryRescue.rescue_mode) {
|
|
5956
|
-
return {
|
|
5957
|
-
content: [{
|
|
5958
|
-
type: "text",
|
|
5959
|
-
text: `${renderContextQueryMemoryRescue({
|
|
5960
|
-
project: resolvedProject,
|
|
5961
|
-
query,
|
|
5962
|
-
scope,
|
|
5963
|
-
results: memoryRescue.results,
|
|
5964
|
-
rescue_mode: memoryRescue.rescue_mode
|
|
5965
|
-
})}
|
|
5966
|
-
|
|
5967
|
-
[diagnostics]
|
|
5968
|
-
retrieval_route=memory_only retrieval_readiness=${preflight.retrieval_readiness} workspace=${preflight.trust_state.workspace_id}`
|
|
5969
|
-
}]
|
|
5970
|
-
};
|
|
5971
|
-
}
|
|
5972
|
-
}
|
|
5973
|
-
return {
|
|
5974
|
-
content: [{
|
|
5975
|
-
type: "text",
|
|
5976
|
-
text: `No relevant repo-grounded context found.
|
|
5977
|
-
|
|
5978
|
-
[diagnostics]
|
|
5979
|
-
workspace=${preflight.trust_state.workspace_id} identity_source=${preflight.trust_state.identity_source} retrieval_readiness=${preflight.retrieval_readiness} retrieval_route=${preflight.retrieval_route}`
|
|
5980
|
-
}]
|
|
5981
|
-
};
|
|
5982
|
-
}
|
|
5983
|
-
const automaticMode = !preflight.repo_grounded && include_memories !== false && include_graph !== true && !(chunk_types && chunk_types.length > 0) && max_tokens === void 0 && runtimeClient;
|
|
5984
|
-
if (automaticMode) {
|
|
5985
|
-
try {
|
|
5986
|
-
const prepared = await prepareAutomaticQuery({
|
|
5987
|
-
project: resolvedProject,
|
|
5988
|
-
query,
|
|
5989
|
-
top_k,
|
|
5990
|
-
user_id,
|
|
5991
|
-
session_id,
|
|
5992
|
-
path: preflight.trust_state.root_path
|
|
5993
|
-
});
|
|
5994
|
-
if (!prepared.items.length) {
|
|
5995
|
-
const memoryRescue = include_memories !== false ? await runContextQueryMemoryRescue({
|
|
5996
|
-
project: resolvedProject,
|
|
5997
|
-
query,
|
|
5998
|
-
user_id: user_id ? scope.userId : void 0,
|
|
5999
|
-
session_id: session_id ? scope.sessionId : void 0,
|
|
6000
|
-
top_k
|
|
6001
|
-
}) : { results: [], rescue_mode: null };
|
|
6002
|
-
if (memoryRescue.results.length && memoryRescue.rescue_mode) {
|
|
6003
|
-
return {
|
|
6004
|
-
content: [{
|
|
6005
|
-
type: "text",
|
|
6006
|
-
text: renderContextQueryMemoryRescue({
|
|
6007
|
-
project: resolvedProject,
|
|
6008
|
-
query,
|
|
6009
|
-
scope,
|
|
6010
|
-
results: memoryRescue.results,
|
|
6011
|
-
rescue_mode: memoryRescue.rescue_mode
|
|
6012
|
-
})
|
|
6013
|
-
}]
|
|
6014
|
-
};
|
|
6015
|
-
}
|
|
6016
|
-
return { content: [{ type: "text", text: "No relevant context found." }] };
|
|
6017
|
-
}
|
|
6018
|
-
const warnings = prepared.retrieval.warnings.length ? `
|
|
6019
|
-
|
|
6020
|
-
[automatic_runtime]
|
|
6021
|
-
${prepared.retrieval.warnings.join("\n")}` : "";
|
|
6022
|
-
const diagnostics = [
|
|
6023
|
-
`focused_scope=${prepared.retrieval.focusedScopeApplied}`,
|
|
6024
|
-
`fallback_used=${prepared.retrieval.fallbackUsed}`,
|
|
6025
|
-
`deduped=${prepared.retrieval.dedupedCount}`,
|
|
6026
|
-
`dropped_below_floor=${prepared.retrieval.droppedBelowFloor}`
|
|
6027
|
-
].join(" ");
|
|
6028
|
-
const scopeLabel = `project=${prepared.scope.project} user=${prepared.scope.userId} session=${prepared.scope.sessionId}`;
|
|
6029
|
-
return {
|
|
6030
|
-
content: [{
|
|
6031
|
-
type: "text",
|
|
6032
|
-
text: `Found ${prepared.items.length} runtime-ranked items (${prepared.retrieval.durationMs}ms, ${scopeLabel}, ${diagnostics}):
|
|
6033
|
-
|
|
6034
|
-
${prepared.context}${warnings}`
|
|
6035
|
-
}]
|
|
6036
|
-
};
|
|
6037
|
-
} catch (error) {
|
|
6038
|
-
const automaticWarning = `Automatic runtime unavailable: ${error.message}. Falling back to broad/manual query path.`;
|
|
6039
|
-
const queryResult2 = await queryWithDegradedFallback({
|
|
6040
|
-
project: resolvedProject,
|
|
6041
|
-
query,
|
|
6042
|
-
top_k,
|
|
6043
|
-
include_memories: include_memories === true,
|
|
6044
|
-
include_graph,
|
|
6045
|
-
user_id: user_id || scope.userId,
|
|
6046
|
-
session_id: session_id || scope.sessionId,
|
|
6047
|
-
retrieval_profile: retrievalProfile,
|
|
6048
|
-
include_parent_content
|
|
6049
|
-
});
|
|
6050
|
-
const response2 = queryResult2.response;
|
|
6051
|
-
if (response2.results.length === 0) {
|
|
6052
|
-
const memoryRescue = include_memories !== false ? await runContextQueryMemoryRescue({
|
|
6053
|
-
project: resolvedProject,
|
|
6054
|
-
query,
|
|
6055
|
-
user_id: user_id ? scope.userId : void 0,
|
|
6056
|
-
session_id: session_id ? scope.sessionId : void 0,
|
|
6057
|
-
top_k
|
|
6058
|
-
}) : { results: [], rescue_mode: null };
|
|
6059
|
-
if (memoryRescue.results.length && memoryRescue.rescue_mode) {
|
|
6060
|
-
return {
|
|
6061
|
-
content: [{
|
|
6062
|
-
type: "text",
|
|
6063
|
-
text: `${renderContextQueryMemoryRescue({
|
|
6064
|
-
project: resolvedProject,
|
|
6065
|
-
query,
|
|
6066
|
-
scope,
|
|
6067
|
-
results: memoryRescue.results,
|
|
6068
|
-
rescue_mode: memoryRescue.rescue_mode
|
|
6069
|
-
})}
|
|
6070
|
-
|
|
6071
|
-
[automatic_runtime]
|
|
6072
|
-
${automaticWarning}`
|
|
6073
|
-
}]
|
|
6074
|
-
};
|
|
6075
|
-
}
|
|
6076
|
-
return { content: [{ type: "text", text: `No relevant context found.
|
|
6077
|
-
|
|
6078
|
-
[automatic_runtime]
|
|
6079
|
-
${automaticWarning}` }] };
|
|
6080
|
-
}
|
|
6081
|
-
const header2 = `Found ${response2.meta.total} results (${response2.meta.latency_ms}ms${response2.meta.cache_hit ? ", cached" : ""}, project=${resolvedProject}, user=${scope.userId}, session=${scope.sessionId}):
|
|
6082
|
-
|
|
6083
|
-
`;
|
|
6084
|
-
const suffix2 = queryResult2.degraded_mode ? `
|
|
6085
|
-
|
|
6086
|
-
[degraded_mode=true] ${queryResult2.degraded_reason}
|
|
6087
|
-
Recommendation: ${queryResult2.recommendation}` : "";
|
|
6088
|
-
return { content: [{ type: "text", text: `${header2}${response2.context}
|
|
6089
|
-
|
|
6090
|
-
[automatic_runtime]
|
|
6091
|
-
${automaticWarning}${suffix2}` }] };
|
|
6092
|
-
}
|
|
6093
|
-
}
|
|
6094
|
-
const queryResult = await queryWithDegradedFallback({
|
|
6095
|
-
project: resolvedProject,
|
|
6096
|
-
query,
|
|
6648
|
+
const payload = await runUnifiedContextRetrieval({
|
|
6649
|
+
question: query,
|
|
6650
|
+
path,
|
|
6651
|
+
project,
|
|
6097
6652
|
top_k,
|
|
6098
|
-
include_memories
|
|
6653
|
+
include_memories,
|
|
6099
6654
|
include_graph,
|
|
6100
|
-
user_id
|
|
6101
|
-
session_id
|
|
6102
|
-
|
|
6103
|
-
|
|
6655
|
+
user_id,
|
|
6656
|
+
session_id,
|
|
6657
|
+
include_parent_content,
|
|
6658
|
+
retrieval_profile,
|
|
6659
|
+
chunk_types
|
|
6104
6660
|
});
|
|
6105
|
-
|
|
6106
|
-
if (response.results.length === 0) {
|
|
6107
|
-
const memoryRescue = include_memories !== false ? await runContextQueryMemoryRescue({
|
|
6108
|
-
project: resolvedProject,
|
|
6109
|
-
query,
|
|
6110
|
-
user_id: user_id ? scope.userId : void 0,
|
|
6111
|
-
session_id: session_id ? scope.sessionId : void 0,
|
|
6112
|
-
top_k
|
|
6113
|
-
}) : { results: [], rescue_mode: null };
|
|
6114
|
-
if (memoryRescue.results.length && memoryRescue.rescue_mode) {
|
|
6115
|
-
return {
|
|
6116
|
-
content: [{
|
|
6117
|
-
type: "text",
|
|
6118
|
-
text: renderContextQueryMemoryRescue({
|
|
6119
|
-
project: resolvedProject,
|
|
6120
|
-
query,
|
|
6121
|
-
scope,
|
|
6122
|
-
results: memoryRescue.results,
|
|
6123
|
-
rescue_mode: memoryRescue.rescue_mode
|
|
6124
|
-
})
|
|
6125
|
-
}]
|
|
6126
|
-
};
|
|
6127
|
-
}
|
|
6128
|
-
return { content: [{ type: "text", text: "No relevant context found." }] };
|
|
6129
|
-
}
|
|
6130
|
-
const header = `Found ${response.meta.total} results (${response.meta.latency_ms}ms${response.meta.cache_hit ? ", cached" : ""}, project=${resolvedProject}, user=${scope.userId}, session=${scope.sessionId}):
|
|
6131
|
-
|
|
6132
|
-
`;
|
|
6133
|
-
const suffix = queryResult.degraded_mode ? `
|
|
6134
|
-
|
|
6135
|
-
[degraded_mode=true] ${queryResult.degraded_reason}
|
|
6136
|
-
Recommendation: ${queryResult.recommendation}` : "";
|
|
6137
|
-
return { content: [{ type: "text", text: header + response.context + suffix }] };
|
|
6661
|
+
return toTextResult(payload);
|
|
6138
6662
|
} catch (error) {
|
|
6139
|
-
return
|
|
6663
|
+
return primaryToolError(error.message, "context_query_failed");
|
|
6140
6664
|
}
|
|
6141
6665
|
}
|
|
6142
6666
|
);
|
|
@@ -6168,11 +6692,24 @@ server.tool(
|
|
|
6168
6692
|
const jobId = result?.job_id;
|
|
6169
6693
|
const mode = result?.mode;
|
|
6170
6694
|
const semanticStatus = result?.semantic_status;
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6695
|
+
return primaryToolSuccess({
|
|
6696
|
+
tool: "memory.add",
|
|
6697
|
+
memory_id: memoryId || null,
|
|
6698
|
+
job_id: jobId || null,
|
|
6699
|
+
mode: mode || null,
|
|
6700
|
+
semantic_status: semanticStatus || null,
|
|
6701
|
+
memory_type: memory_type || "factual",
|
|
6702
|
+
queued: mode === "async" || Boolean(jobId),
|
|
6703
|
+
diagnostics: {
|
|
6704
|
+
scope: {
|
|
6705
|
+
project: scope.project || null,
|
|
6706
|
+
user_id: scope.userId,
|
|
6707
|
+
session_id: scope.sessionId || null
|
|
6708
|
+
}
|
|
6709
|
+
}
|
|
6710
|
+
});
|
|
6174
6711
|
} catch (error) {
|
|
6175
|
-
return
|
|
6712
|
+
return primaryToolError(error.message);
|
|
6176
6713
|
}
|
|
6177
6714
|
}
|
|
6178
6715
|
);
|
|
@@ -6214,7 +6751,14 @@ server.tool(
|
|
|
6214
6751
|
user_id: scope.userId,
|
|
6215
6752
|
session_id: scope.sessionId,
|
|
6216
6753
|
results: normalizedResults,
|
|
6217
|
-
count: normalizedResults.length
|
|
6754
|
+
count: normalizedResults.length,
|
|
6755
|
+
diagnostics: {
|
|
6756
|
+
scope: {
|
|
6757
|
+
project: scope.project || null,
|
|
6758
|
+
user_id: scope.userId,
|
|
6759
|
+
session_id: scope.sessionId || null
|
|
6760
|
+
}
|
|
6761
|
+
}
|
|
6218
6762
|
});
|
|
6219
6763
|
} catch (error) {
|
|
6220
6764
|
return primaryToolError(error.message);
|
|
@@ -6478,11 +7022,12 @@ server.tool(
|
|
|
6478
7022
|
},
|
|
6479
7023
|
async ({ project, query, user_id, session_id, question_date, memory_types, top_k, include_relations }) => {
|
|
6480
7024
|
try {
|
|
7025
|
+
const scope = resolveMcpScope({ project, user_id, session_id });
|
|
6481
7026
|
const results = await whisper.searchMemoriesSOTA({
|
|
6482
|
-
project,
|
|
7027
|
+
project: scope.project,
|
|
6483
7028
|
query,
|
|
6484
|
-
user_id,
|
|
6485
|
-
session_id,
|
|
7029
|
+
user_id: scope.userId,
|
|
7030
|
+
session_id: scope.sessionId,
|
|
6486
7031
|
question_date,
|
|
6487
7032
|
memory_types,
|
|
6488
7033
|
top_k,
|
|
@@ -6492,10 +7037,19 @@ server.tool(
|
|
|
6492
7037
|
return primaryToolSuccess({
|
|
6493
7038
|
tool: "memory.search_sota",
|
|
6494
7039
|
query,
|
|
7040
|
+
user_id: scope.userId,
|
|
7041
|
+
session_id: scope.sessionId || null,
|
|
6495
7042
|
question_date: question_date || null,
|
|
6496
7043
|
include_relations,
|
|
6497
7044
|
results: normalizedResults,
|
|
6498
|
-
count: normalizedResults.length
|
|
7045
|
+
count: normalizedResults.length,
|
|
7046
|
+
diagnostics: {
|
|
7047
|
+
scope: {
|
|
7048
|
+
project: scope.project || null,
|
|
7049
|
+
user_id: scope.userId,
|
|
7050
|
+
session_id: scope.sessionId || null
|
|
7051
|
+
}
|
|
7052
|
+
}
|
|
6499
7053
|
});
|
|
6500
7054
|
} catch (error) {
|
|
6501
7055
|
return primaryToolError(error.message);
|
|
@@ -7531,7 +8085,10 @@ server.tool(
|
|
|
7531
8085
|
context: response.context,
|
|
7532
8086
|
results: response.results,
|
|
7533
8087
|
degradedMode: queryResult.degraded_mode,
|
|
7534
|
-
degradedReason: queryResult.degraded_reason
|
|
8088
|
+
degradedReason: queryResult.degraded_reason,
|
|
8089
|
+
semanticStatus: queryResult.semantic_status,
|
|
8090
|
+
fallbackMode: queryResult.fallback_mode,
|
|
8091
|
+
recommendedFixes: queryResult.recommended_fixes || []
|
|
7535
8092
|
}));
|
|
7536
8093
|
} catch (error) {
|
|
7537
8094
|
return primaryToolError(error.message);
|
|
@@ -7795,7 +8352,16 @@ server.tool(
|
|
|
7795
8352
|
},
|
|
7796
8353
|
async ({ project, content, memory_type, user_id, session_id, agent_id, importance }) => {
|
|
7797
8354
|
try {
|
|
7798
|
-
const
|
|
8355
|
+
const scope = resolveMcpScope({ project, user_id, session_id });
|
|
8356
|
+
const result = await whisper.addMemory({
|
|
8357
|
+
project: scope.project,
|
|
8358
|
+
content,
|
|
8359
|
+
memory_type,
|
|
8360
|
+
user_id: scope.userId,
|
|
8361
|
+
session_id: scope.sessionId,
|
|
8362
|
+
agent_id,
|
|
8363
|
+
importance
|
|
8364
|
+
});
|
|
7799
8365
|
const memoryId = result?.memory_id || (result.mode === "sync" ? result.id : null);
|
|
7800
8366
|
const jobId = result?.job_id || (result.mode === "async" ? result.id : null);
|
|
7801
8367
|
return primaryToolSuccess({
|
|
@@ -7806,7 +8372,14 @@ server.tool(
|
|
|
7806
8372
|
mode: result?.mode || null,
|
|
7807
8373
|
memory_type,
|
|
7808
8374
|
stored: result.success === true,
|
|
7809
|
-
queued: result?.mode === "async" || Boolean(jobId)
|
|
8375
|
+
queued: result?.mode === "async" || Boolean(jobId),
|
|
8376
|
+
diagnostics: {
|
|
8377
|
+
scope: {
|
|
8378
|
+
project: scope.project || null,
|
|
8379
|
+
user_id: scope.userId,
|
|
8380
|
+
session_id: scope.sessionId || null
|
|
8381
|
+
}
|
|
8382
|
+
}
|
|
7810
8383
|
});
|
|
7811
8384
|
} catch (error) {
|
|
7812
8385
|
return primaryToolError(error.message);
|
|
@@ -7842,18 +8415,31 @@ server.tool(
|
|
|
7842
8415
|
timestamp
|
|
7843
8416
|
});
|
|
7844
8417
|
const resolvedProject = await resolveProjectRef(project);
|
|
7845
|
-
const
|
|
7846
|
-
project: resolvedProject,
|
|
7847
|
-
session_id,
|
|
8418
|
+
const scope = resolveMcpScope({
|
|
8419
|
+
project: resolvedProject || project,
|
|
7848
8420
|
user_id,
|
|
8421
|
+
session_id
|
|
8422
|
+
});
|
|
8423
|
+
const result = await ingestSessionWithSyncFallback({
|
|
8424
|
+
project: scope.project,
|
|
8425
|
+
session_id: scope.sessionId || session_id,
|
|
8426
|
+
user_id: scope.userId,
|
|
7849
8427
|
messages: normalizedMessages
|
|
7850
8428
|
});
|
|
7851
8429
|
return primaryToolSuccess({
|
|
7852
8430
|
tool: "record",
|
|
7853
|
-
session_id,
|
|
8431
|
+
session_id: scope.sessionId || session_id,
|
|
8432
|
+
user_id: scope.userId,
|
|
7854
8433
|
messages_recorded: normalizedMessages.length,
|
|
7855
8434
|
memories_created: result.memories_created,
|
|
7856
|
-
relations_created: result.relations_created
|
|
8435
|
+
relations_created: result.relations_created,
|
|
8436
|
+
diagnostics: {
|
|
8437
|
+
scope: {
|
|
8438
|
+
project: scope.project || null,
|
|
8439
|
+
user_id: scope.userId,
|
|
8440
|
+
session_id: scope.sessionId || session_id
|
|
8441
|
+
}
|
|
8442
|
+
}
|
|
7857
8443
|
});
|
|
7858
8444
|
} catch (error) {
|
|
7859
8445
|
return primaryToolError(error.message);
|
|
@@ -7984,6 +8570,14 @@ async function main() {
|
|
|
7984
8570
|
console.error("Error: WHISPER_API_KEY environment variable is required");
|
|
7985
8571
|
process.exit(1);
|
|
7986
8572
|
}
|
|
8573
|
+
const startupDiagnostics = {
|
|
8574
|
+
runtime_mode: RUNTIME_MODE,
|
|
8575
|
+
local_ingest_enabled: RUNTIME_MODE !== "remote",
|
|
8576
|
+
startup_status: RUNTIME_MODE === "remote" ? "degraded_auto_repairing" : "healthy",
|
|
8577
|
+
startup_action: RUNTIME_MODE === "remote" ? "restart_client" : "none",
|
|
8578
|
+
warning: RUNTIME_MODE === "remote" ? "Local ingest is disabled in remote mode. Set WHISPER_MCP_MODE=auto and restart MCP to enable local repair paths." : null
|
|
8579
|
+
};
|
|
8580
|
+
console.error(`[whisper-context-mcp] startup_diagnostics=${JSON.stringify(startupDiagnostics)}`);
|
|
7987
8581
|
console.error("[whisper-context-mcp] Breaking change: canonical namespaced tool names are active. Run with --print-tool-map for migration mapping.");
|
|
7988
8582
|
const transport = new StdioServerTransport();
|
|
7989
8583
|
await server.connect(transport);
|
|
@@ -7996,20 +8590,35 @@ export {
|
|
|
7996
8590
|
RECOMMENDED_NEXT_CALL_ALLOWLIST,
|
|
7997
8591
|
RETRIEVAL_READINESS_VALUES,
|
|
7998
8592
|
RETRIEVAL_ROUTE_VALUES,
|
|
8593
|
+
STATUS_ACTION_VALUES,
|
|
8594
|
+
STATUS_LABEL_VALUES,
|
|
7999
8595
|
WORKSPACE_HEALTH_VALUES,
|
|
8000
8596
|
canonicalizeWorkspacePath,
|
|
8001
8597
|
chooseWorkspaceProjectSource,
|
|
8002
8598
|
classifyProjectRepoReadiness,
|
|
8003
8599
|
classifyRepoGroundedQuery,
|
|
8004
8600
|
classifyWorkspaceHealth,
|
|
8601
|
+
computeSnapshotChangedFiles,
|
|
8602
|
+
computeTrustScore,
|
|
8005
8603
|
createMcpServer,
|
|
8006
8604
|
createWhisperMcpClient,
|
|
8007
8605
|
createWhisperMcpRuntimeClient,
|
|
8606
|
+
defaultMcpUserId,
|
|
8008
8607
|
extractSignature,
|
|
8608
|
+
fallbackModeFromSearchMode,
|
|
8609
|
+
isStrictlyBetterReadiness,
|
|
8610
|
+
recommendedNextCallsAreAcyclic,
|
|
8611
|
+
recommendedNextCallsForRootCause,
|
|
8009
8612
|
renderScopedMcpConfig,
|
|
8010
8613
|
resolveForgetQueryCandidates,
|
|
8614
|
+
resolveMcpScope,
|
|
8011
8615
|
resolveWorkspaceIdentity,
|
|
8616
|
+
retrievalReadinessRank,
|
|
8012
8617
|
runSearchCodeSearch,
|
|
8013
8618
|
sanitizeRecommendedNextCalls,
|
|
8014
|
-
|
|
8619
|
+
semanticStatusFromSearchMode,
|
|
8620
|
+
shouldAbstainForCodebaseTrust,
|
|
8621
|
+
shouldTriggerBackgroundRefresh,
|
|
8622
|
+
statusActionForRootCause,
|
|
8623
|
+
statusContractForReadiness
|
|
8015
8624
|
};
|