@zokizuan/satori-mcp 4.11.7 → 4.11.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +6 -6
  2. package/dist/cli/install.js +1 -0
  3. package/dist/config.d.ts +1 -0
  4. package/dist/config.js +1 -1
  5. package/dist/core/backend-diagnostics.js +5 -2
  6. package/dist/core/handlers.d.ts +45 -239
  7. package/dist/core/handlers.js +894 -7710
  8. package/dist/core/manage-indexing-handlers.d.ts +94 -0
  9. package/dist/core/manage-indexing-handlers.js +529 -0
  10. package/dist/core/manage-maintenance-handlers.d.ts +63 -0
  11. package/dist/core/manage-maintenance-handlers.js +471 -0
  12. package/dist/core/manage-types.d.ts +2 -2
  13. package/dist/core/navigation-handlers.d.ts +138 -0
  14. package/dist/core/navigation-handlers.js +675 -0
  15. package/dist/core/python-call-fallback.d.ts +42 -0
  16. package/dist/core/python-call-fallback.js +383 -0
  17. package/dist/core/registry-file-outline.d.ts +26 -0
  18. package/dist/core/registry-file-outline.js +162 -0
  19. package/dist/core/relationship-backed-call-graph.d.ts +56 -0
  20. package/dist/core/relationship-backed-call-graph.js +322 -0
  21. package/dist/core/search-debug-helpers.d.ts +29 -0
  22. package/dist/core/search-debug-helpers.js +150 -0
  23. package/dist/core/search-exact-fast-path.d.ts +100 -0
  24. package/dist/core/search-exact-fast-path.js +163 -0
  25. package/dist/core/search-exact-registry-hit.d.ts +53 -0
  26. package/dist/core/search-exact-registry-hit.js +98 -0
  27. package/dist/core/search-execution.d.ts +144 -0
  28. package/dist/core/search-execution.js +404 -0
  29. package/dist/core/search-frontdoor.d.ts +65 -0
  30. package/dist/core/search-frontdoor.js +153 -0
  31. package/dist/core/search-group-ordering.d.ts +6 -0
  32. package/dist/core/search-group-ordering.js +96 -0
  33. package/dist/core/search-group-results.d.ts +141 -0
  34. package/dist/core/search-group-results.js +383 -0
  35. package/dist/core/search-grouping.d.ts +18 -0
  36. package/dist/core/search-grouping.js +69 -0
  37. package/dist/core/search-lexical-scoring.d.ts +39 -0
  38. package/dist/core/search-lexical-scoring.js +241 -0
  39. package/dist/core/search-navigation.d.ts +44 -0
  40. package/dist/core/search-navigation.js +196 -0
  41. package/dist/core/search-owner-resolution.d.ts +27 -0
  42. package/dist/core/search-owner-resolution.js +252 -0
  43. package/dist/core/search-query-planning.d.ts +13 -0
  44. package/dist/core/search-query-planning.js +320 -0
  45. package/dist/core/search-query-support.d.ts +123 -0
  46. package/dist/core/search-query-support.js +883 -0
  47. package/dist/core/search-ranking-policy.d.ts +64 -0
  48. package/dist/core/search-ranking-policy.js +342 -0
  49. package/dist/core/search-response-envelopes.d.ts +29 -0
  50. package/dist/core/search-response-envelopes.js +65 -0
  51. package/dist/core/search-response-helpers.d.ts +39 -0
  52. package/dist/core/search-response-helpers.js +446 -0
  53. package/dist/core/search-result-finalization.d.ts +96 -0
  54. package/dist/core/search-result-finalization.js +207 -0
  55. package/dist/core/search-types.d.ts +1 -1
  56. package/dist/core/snapshot.d.ts +11 -2
  57. package/dist/core/snapshot.js +71 -12
  58. package/dist/core/sync.d.ts +1 -0
  59. package/dist/core/sync.js +13 -4
  60. package/dist/core/tool-response-builders.d.ts +128 -0
  61. package/dist/core/tool-response-builders.js +399 -0
  62. package/dist/core/tracked-root-readiness.d.ts +132 -0
  63. package/dist/core/tracked-root-readiness.js +278 -0
  64. package/dist/core/vector-backend-maintenance.d.ts +37 -0
  65. package/dist/core/vector-backend-maintenance.js +246 -0
  66. package/dist/core/working-tree-state.d.ts +43 -0
  67. package/dist/core/working-tree-state.js +136 -0
  68. package/dist/server/bootstrap-stdio.js +1 -1
  69. package/dist/server/start-server.js +4 -1
  70. package/dist/tools/list_codebases.js +29 -1
  71. package/dist/tools/manage_index.js +6 -3
  72. package/dist/tools/read_file.js +38 -8
  73. package/package.json +4 -4
@@ -0,0 +1,94 @@
1
+ import { Context } from "@zokizuan/satori-core";
2
+ import type { SnapshotManager } from "./snapshot.js";
3
+ import type { SyncManager } from "./sync.js";
4
+ import { ManageIndexAction } from "./manage-types.js";
5
+ import type { CompletionProofValidationResult } from "./completion-proof.js";
6
+ import { type VectorBackendDiagnostic } from "./backend-diagnostics.js";
7
+ import type { IndexFingerprint } from "../config.js";
8
+ import type { ReindexPreflightResult } from "./working-tree-state.js";
9
+ type ToolTextResponse = {
10
+ content: Array<{
11
+ type: "text";
12
+ text: string;
13
+ }>;
14
+ isError?: boolean;
15
+ };
16
+ type IndexCodebaseArgs = {
17
+ path: string;
18
+ force?: boolean;
19
+ customExtensions?: unknown;
20
+ ignorePatterns?: unknown;
21
+ zillizDropCollection?: unknown;
22
+ __reindexPreflight?: ReindexPreflightResult;
23
+ };
24
+ type ReindexCodebaseArgs = {
25
+ path: string;
26
+ customExtensions?: unknown;
27
+ ignorePatterns?: unknown;
28
+ zillizDropCollection?: unknown;
29
+ allowUnnecessaryReindex?: boolean;
30
+ };
31
+ type IndexProfileView = {
32
+ profile: string;
33
+ configPath?: string;
34
+ };
35
+ type ManageIndexingHandlersHost = {
36
+ context: Context;
37
+ snapshotManager: SnapshotManager;
38
+ syncManager: SyncManager;
39
+ runtimeFingerprint: IndexFingerprint;
40
+ startBackgroundIndexing?: (codebasePath: string, forceReindex: boolean, writeCollectionName?: string) => Promise<void> | void;
41
+ manageResponse(action: ManageIndexAction | "reindex", path: string, status: string, message: string, options?: Record<string, unknown>): ToolTextResponse;
42
+ buildRuntimeOwnerConflictResponseIfBlocked(action: ManageIndexAction | "reindex", codebasePath: string): Promise<ToolTextResponse | null>;
43
+ recoverStaleIndexingStateIfNeeded(codebasePath: string): Promise<void>;
44
+ getSnapshotIndexingCodebases(): string[];
45
+ getSnapshotCodebaseInfo(codebasePath: string): Record<string, unknown> | undefined;
46
+ getSnapshotIndexedCodebases(): string[];
47
+ buildManageActionBlockedMessage(codebasePath: string, action: "create" | "reindex" | "repair"): string;
48
+ buildCreateHint(codebasePath: string): Record<string, unknown>;
49
+ buildStatusHint(codebasePath: string): Record<string, unknown>;
50
+ getManageRetryAfterMs(): number;
51
+ buildIndexingMetadata(codebasePath: string): Record<string, unknown> | undefined;
52
+ buildReindexInstruction(codebasePath: string, detail?: string): string;
53
+ buildManageRequiresReindexHints(codebasePath: string): Record<string, unknown>;
54
+ validateCompletionProof(codebasePath: string): Promise<CompletionProofValidationResult>;
55
+ recoverIndexedSnapshotFromCompletionProof(codebasePath: string, proof: CompletionProofValidationResult): Promise<boolean>;
56
+ isZillizBackend(): boolean;
57
+ resolveCollectionName(codebasePath: string): string;
58
+ dropZillizCollectionForCreate(collectionName: string): Promise<{
59
+ droppedCodebasePath?: string;
60
+ }>;
61
+ resolveStagedCollectionName(codebasePath: string, generationId: string): string;
62
+ buildCollectionLimitMessage(codebasePath: string): Promise<string>;
63
+ manageVectorBackendResponse(action: ManageIndexAction, path: string, diagnostic: VectorBackendDiagnostic, humanText?: string): ToolTextResponse;
64
+ saveSnapshotIfSupported(): void;
65
+ touchWatchedCodebase(codebasePath: string): Promise<void>;
66
+ setWriteCollectionOverride(codebasePath: string, collectionName: string | null): void;
67
+ loadIndexProfileForCodebase(codebasePath: string): IndexProfileView;
68
+ getContextActiveIgnorePatterns(codebasePath: string): string[];
69
+ getContextIndexedExtensions(codebasePath: string): string[];
70
+ canonicalizeCodebasePath(codebasePath: string): string;
71
+ pruneIndexedCollectionFamily(codebasePath: string, keepCollectionName: string): Promise<string[]>;
72
+ pruneUnprovenStagedCollectionFamily(codebasePath: string): Promise<string[]>;
73
+ getContextTrackedRelativePaths(codebasePath: string): string[];
74
+ setIndexingStats(stats: {
75
+ indexedFiles: number;
76
+ totalChunks: number;
77
+ } | null): void;
78
+ rebuildCallGraphForIndex(codebasePath: string): Promise<void>;
79
+ getSnapshotIndexingProgress(codebasePath: string): number | undefined;
80
+ clearIndexCompletionMarker(codebasePath: string): Promise<void>;
81
+ evaluateReindexPreflight(codebasePath: string): ReindexPreflightResult;
82
+ };
83
+ export declare class ManageIndexingHandlers {
84
+ private readonly host;
85
+ constructor(host: ManageIndexingHandlersHost);
86
+ private isStagedCollectionName;
87
+ private cleanupFailedStagedCollection;
88
+ handleIndexCodebase(args: IndexCodebaseArgs): Promise<ToolTextResponse>;
89
+ handleReindexCodebase(args: ReindexCodebaseArgs): Promise<ToolTextResponse>;
90
+ handleRepairIndex(args: Record<string, unknown>): Promise<ToolTextResponse>;
91
+ private startBackgroundIndexing;
92
+ }
93
+ export {};
94
+ //# sourceMappingURL=manage-indexing-handlers.d.ts.map
@@ -0,0 +1,529 @@
1
+ import * as fs from "fs";
2
+ import * as crypto from "node:crypto";
3
+ import { COLLECTION_LIMIT_MESSAGE, deleteCollectionWithVerification, RemoteCollectionDeletePendingError, } from "@zokizuan/satori-core";
4
+ import { classifyVectorBackendError, } from "./backend-diagnostics.js";
5
+ import { ensureAbsolutePath, trackCodebasePath } from "../utils.js";
6
+ function isMatchingVerifiedFingerprint(info, runtimeFingerprint) {
7
+ if (info?.fingerprintSource !== "verified") {
8
+ return undefined;
9
+ }
10
+ const fingerprint = info.indexFingerprint;
11
+ if (!fingerprint || typeof fingerprint !== "object") {
12
+ return undefined;
13
+ }
14
+ const record = fingerprint;
15
+ const matches = record.embeddingProvider === runtimeFingerprint.embeddingProvider
16
+ && record.embeddingModel === runtimeFingerprint.embeddingModel
17
+ && Number(record.embeddingDimension) === Number(runtimeFingerprint.embeddingDimension)
18
+ && record.vectorStoreProvider === runtimeFingerprint.vectorStoreProvider
19
+ && record.schemaVersion === runtimeFingerprint.schemaVersion;
20
+ return matches ? fingerprint : undefined;
21
+ }
22
+ const COLLECTION_LIMIT_PATTERNS = [
23
+ /exceeded the limit number of collections/i,
24
+ /collection limit/i,
25
+ /too many collections/i,
26
+ /quota.*collection/i,
27
+ ];
28
+ function collectErrorFragments(value, output, visited, depth = 0) {
29
+ if (value === null || value === undefined || depth > 4 || output.length >= 8) {
30
+ return;
31
+ }
32
+ if (typeof value === "string") {
33
+ const trimmed = value.trim();
34
+ if (trimmed.length > 0) {
35
+ output.push(trimmed);
36
+ }
37
+ return;
38
+ }
39
+ if (typeof value === "number" || typeof value === "boolean") {
40
+ output.push(String(value));
41
+ return;
42
+ }
43
+ if (value instanceof Error) {
44
+ collectErrorFragments(value.message, output, visited, depth + 1);
45
+ collectErrorFragments(value.cause, output, visited, depth + 1);
46
+ return;
47
+ }
48
+ if (typeof value !== "object") {
49
+ return;
50
+ }
51
+ if (visited.has(value)) {
52
+ return;
53
+ }
54
+ visited.add(value);
55
+ if (Array.isArray(value)) {
56
+ for (const item of value) {
57
+ collectErrorFragments(item, output, visited, depth + 1);
58
+ if (output.length >= 8) {
59
+ return;
60
+ }
61
+ }
62
+ return;
63
+ }
64
+ const record = value;
65
+ for (const key of ["message", "reason", "detail", "details", "error", "msg", "code", "error_code"]) {
66
+ if (key in record) {
67
+ collectErrorFragments(record[key], output, visited, depth + 1);
68
+ if (output.length >= 8) {
69
+ return;
70
+ }
71
+ }
72
+ }
73
+ for (const nestedValue of Object.values(record)) {
74
+ collectErrorFragments(nestedValue, output, visited, depth + 1);
75
+ if (output.length >= 8) {
76
+ return;
77
+ }
78
+ }
79
+ }
80
+ function formatUnknownError(error) {
81
+ if (error === COLLECTION_LIMIT_MESSAGE) {
82
+ return COLLECTION_LIMIT_MESSAGE;
83
+ }
84
+ const fragments = [];
85
+ collectErrorFragments(error, fragments, new Set());
86
+ const deduped = Array.from(new Set(fragments.map((fragment) => fragment.trim()).filter(Boolean)));
87
+ if (deduped.length > 0) {
88
+ return deduped.slice(0, 3).join(" | ");
89
+ }
90
+ try {
91
+ return JSON.stringify(error);
92
+ }
93
+ catch {
94
+ return String(error);
95
+ }
96
+ }
97
+ function isCollectionLimitError(error) {
98
+ if (error === COLLECTION_LIMIT_MESSAGE) {
99
+ return true;
100
+ }
101
+ const message = formatUnknownError(error);
102
+ if (message === COLLECTION_LIMIT_MESSAGE) {
103
+ return true;
104
+ }
105
+ return COLLECTION_LIMIT_PATTERNS.some((pattern) => pattern.test(message));
106
+ }
107
+ function isBackendTimeoutError(error) {
108
+ const message = formatUnknownError(error);
109
+ return /DEADLINE_EXCEEDED|deadline exceeded|timeout|timed out/i.test(message);
110
+ }
111
+ export class ManageIndexingHandlers {
112
+ constructor(host) {
113
+ this.host = host;
114
+ }
115
+ isStagedCollectionName(collectionName) {
116
+ return typeof collectionName === "string" && collectionName.includes("__gen_");
117
+ }
118
+ async cleanupFailedStagedCollection(codebasePath, collectionName) {
119
+ if (!this.isStagedCollectionName(collectionName)) {
120
+ return;
121
+ }
122
+ try {
123
+ await deleteCollectionWithVerification(this.host.context.getVectorStore(), collectionName);
124
+ console.log(`[BACKGROUND-INDEX] Cleaned failed staged collection '${collectionName}' for '${codebasePath}'.`);
125
+ }
126
+ catch (cleanupError) {
127
+ console.warn(`[BACKGROUND-INDEX] Failed to clean staged collection '${collectionName}' after indexing failure for '${codebasePath}': ${formatUnknownError(cleanupError)}`);
128
+ }
129
+ }
130
+ async handleIndexCodebase(args) {
131
+ const { path: codebasePath, force, customExtensions, ignorePatterns, zillizDropCollection } = args;
132
+ const forceReindex = force || false;
133
+ const manageAction = forceReindex ? "reindex" : "create";
134
+ const internalPreflight = forceReindex ? args.__reindexPreflight : undefined;
135
+ const preflightOptions = internalPreflight
136
+ ? { warnings: internalPreflight.warnings, preflight: internalPreflight }
137
+ : {};
138
+ const customFileExtensions = Array.isArray(customExtensions)
139
+ ? customExtensions.filter((extension) => typeof extension === "string")
140
+ : [];
141
+ const customIgnorePatterns = Array.isArray(ignorePatterns)
142
+ ? ignorePatterns.filter((pattern) => typeof pattern === "string")
143
+ : [];
144
+ const requestedDropCollection = typeof zillizDropCollection === "string" ? zillizDropCollection.trim() : undefined;
145
+ let dropSummaryLine = "";
146
+ try {
147
+ const absolutePath = ensureAbsolutePath(codebasePath);
148
+ if (!fs.existsSync(absolutePath)) {
149
+ return this.host.manageResponse(manageAction, absolutePath, "error", `Error: Path '${absolutePath}' does not exist. Original input: '${codebasePath}'`, preflightOptions);
150
+ }
151
+ const stat = fs.statSync(absolutePath);
152
+ if (!stat.isDirectory()) {
153
+ return this.host.manageResponse(manageAction, absolutePath, "error", `Error: Path '${absolutePath}' is not a directory`, preflightOptions);
154
+ }
155
+ const runtimeOwnerConflict = await this.host.buildRuntimeOwnerConflictResponseIfBlocked(manageAction, absolutePath);
156
+ if (runtimeOwnerConflict) {
157
+ return runtimeOwnerConflict;
158
+ }
159
+ await this.host.recoverStaleIndexingStateIfNeeded(absolutePath);
160
+ if (this.host.getSnapshotIndexingCodebases().includes(absolutePath)) {
161
+ const blockedAction = forceReindex ? "reindex" : "create";
162
+ return this.host.manageResponse(manageAction, absolutePath, "not_ready", this.host.buildManageActionBlockedMessage(absolutePath, blockedAction), {
163
+ ...preflightOptions,
164
+ reason: "indexing",
165
+ hints: {
166
+ status: this.host.buildStatusHint(absolutePath),
167
+ retryAfterMs: this.host.getManageRetryAfterMs(),
168
+ indexing: this.host.buildIndexingMetadata(absolutePath),
169
+ },
170
+ });
171
+ }
172
+ const existingInfo = this.host.getSnapshotCodebaseInfo(absolutePath);
173
+ if (!forceReindex && existingInfo?.status === "requires_reindex") {
174
+ return this.host.manageResponse(manageAction, absolutePath, "requires_reindex", this.host.buildReindexInstruction(absolutePath, typeof existingInfo.message === "string" ? existingInfo.message : undefined), {
175
+ ...preflightOptions,
176
+ reason: "requires_reindex",
177
+ hints: this.host.buildManageRequiresReindexHints(absolutePath),
178
+ });
179
+ }
180
+ const isIndexedInSnapshot = this.host.getSnapshotIndexedCodebases().includes(absolutePath);
181
+ if (!forceReindex && !isIndexedInSnapshot) {
182
+ const proof = await this.host.validateCompletionProof(absolutePath);
183
+ if (await this.host.recoverIndexedSnapshotFromCompletionProof(absolutePath, proof)) {
184
+ if (proof.outcome === "fingerprint_mismatch") {
185
+ return this.host.manageResponse(manageAction, absolutePath, "requires_reindex", this.host.buildReindexInstruction(absolutePath, "Recovered local readiness from completion marker proof, but the current runtime fingerprint does not match the existing index."), {
186
+ ...preflightOptions,
187
+ reason: "requires_reindex",
188
+ hints: this.host.buildManageRequiresReindexHints(absolutePath),
189
+ });
190
+ }
191
+ return this.host.manageResponse(manageAction, absolutePath, "blocked", `Codebase '${absolutePath}' is already indexed. Local readiness was recovered from completion marker proof.\n\nTo update incrementally with recent changes: call manage_index with {"action":"sync","path":"${absolutePath}"}.\nTo force rebuild from scratch: call manage_index with {"action":"create","path":"${absolutePath}","force":true}.`, preflightOptions);
192
+ }
193
+ }
194
+ if (!forceReindex && isIndexedInSnapshot) {
195
+ const proof = await this.host.validateCompletionProof(absolutePath);
196
+ if (proof.outcome === "valid") {
197
+ return this.host.manageResponse(manageAction, absolutePath, "blocked", `Codebase '${absolutePath}' is already indexed.\n\nTo update incrementally with recent changes: call manage_index with {"action":"sync","path":"${absolutePath}"}.\nTo force rebuild from scratch: call manage_index with {"action":"create","path":"${absolutePath}","force":true}.`);
198
+ }
199
+ console.warn(`[INDEX-VALIDATION] Snapshot reports indexed for '${absolutePath}', but completion proof is '${proof.reason || proof.outcome}'. Treating as not_indexed and continuing create flow.`);
200
+ }
201
+ if (requestedDropCollection) {
202
+ if (!this.host.isZillizBackend()) {
203
+ return this.host.manageResponse(manageAction, absolutePath, "error", "Error: zillizDropCollection is only supported when connected to a Zilliz Cloud backend.", preflightOptions);
204
+ }
205
+ const targetCollectionName = this.host.resolveCollectionName(absolutePath);
206
+ if (requestedDropCollection === targetCollectionName) {
207
+ return this.host.manageResponse(manageAction, absolutePath, "error", `Error: zillizDropCollection cannot target '${targetCollectionName}' for this same codebase create flow. Use {"action":"create","path":"${absolutePath}","force":true} for reindexing this codebase.`, preflightOptions);
208
+ }
209
+ let dropResult;
210
+ try {
211
+ dropResult = await this.host.dropZillizCollectionForCreate(requestedDropCollection);
212
+ }
213
+ catch (error) {
214
+ if (error instanceof RemoteCollectionDeletePendingError) {
215
+ return this.host.manageResponse(manageAction, absolutePath, "error", `Zilliz collection '${requestedDropCollection}' remote deletion is still pending. Local index state was not changed. Retry after the backend has converged. Details: ${formatUnknownError(error)}`, {
216
+ ...preflightOptions,
217
+ reason: "remote_delete_pending",
218
+ hints: {
219
+ retry: {
220
+ tool: "manage_index",
221
+ args: { action: manageAction, path: absolutePath, zillizDropCollection: requestedDropCollection },
222
+ },
223
+ },
224
+ });
225
+ }
226
+ throw error;
227
+ }
228
+ dropSummaryLine += dropResult.droppedCodebasePath
229
+ ? `\nDropped Zilliz collection '${requestedDropCollection}' (mapped codebase: '${dropResult.droppedCodebasePath}').`
230
+ : `\nDropped Zilliz collection '${requestedDropCollection}'.`;
231
+ }
232
+ const stagedCollectionName = this.host.resolveStagedCollectionName(absolutePath, `run_${crypto.randomUUID()}`);
233
+ try {
234
+ const prunedStagedCollections = await this.host.pruneUnprovenStagedCollectionFamily(absolutePath);
235
+ if (prunedStagedCollections.length > 0) {
236
+ console.log(`[INDEX-VALIDATION] 🧹 Removed ${prunedStagedCollections.length} unproven staged collection(s): ${prunedStagedCollections.join(", ")}`);
237
+ }
238
+ console.log("[INDEX-VALIDATION] 🔍 Validating collection creation capability");
239
+ const canCreateCollection = await this.host.context.getVectorStore().checkCollectionLimit();
240
+ if (!canCreateCollection) {
241
+ console.error(`[INDEX-VALIDATION] ❌ Collection limit validation failed: ${absolutePath}`);
242
+ const guidanceMessage = await this.host.buildCollectionLimitMessage(absolutePath);
243
+ return this.host.manageResponse(manageAction, absolutePath, "error", guidanceMessage, preflightOptions);
244
+ }
245
+ console.log("[INDEX-VALIDATION] ✅ Collection creation validation completed");
246
+ }
247
+ catch (validationError) {
248
+ console.error("[INDEX-VALIDATION] ❌ Collection creation validation failed:", validationError);
249
+ if (isCollectionLimitError(validationError)) {
250
+ const guidanceMessage = await this.host.buildCollectionLimitMessage(absolutePath);
251
+ return this.host.manageResponse(manageAction, absolutePath, "error", guidanceMessage, preflightOptions);
252
+ }
253
+ if (validationError instanceof RemoteCollectionDeletePendingError) {
254
+ return this.host.manageResponse(manageAction, absolutePath, "error", `Zilliz/Milvus validation collection deletion is still pending. Local index state was not changed. Retry after the backend has converged. Details: ${formatUnknownError(validationError)}`, {
255
+ ...preflightOptions,
256
+ reason: "remote_delete_pending",
257
+ hints: {
258
+ retry: {
259
+ tool: "manage_index",
260
+ args: { action: manageAction, path: absolutePath },
261
+ },
262
+ },
263
+ });
264
+ }
265
+ const vectorBackendDiagnostic = classifyVectorBackendError(validationError);
266
+ if (vectorBackendDiagnostic) {
267
+ return this.host.manageVectorBackendResponse(manageAction, absolutePath, vectorBackendDiagnostic);
268
+ }
269
+ const validationMessage = formatUnknownError(validationError);
270
+ const backendTimeout = isBackendTimeoutError(validationError);
271
+ const timeoutOptions = backendTimeout
272
+ ? {
273
+ ...preflightOptions,
274
+ reason: "backend_timeout",
275
+ hints: {
276
+ retry: {
277
+ tool: "manage_index",
278
+ args: { action: manageAction, path: absolutePath },
279
+ },
280
+ },
281
+ }
282
+ : preflightOptions;
283
+ const validationText = backendTimeout
284
+ ? `Backend timeout while validating Zilliz/Milvus collection creation for '${absolutePath}'. The repo path is valid and local index state was not changed. This is retryable/operator-actionable: check backend availability or network latency, then retry manage_index action='${manageAction}'. Details: ${validationMessage}`
285
+ : `Error validating collection creation: ${validationMessage}`;
286
+ return this.host.manageResponse(manageAction, absolutePath, "error", validationText, timeoutOptions);
287
+ }
288
+ if (customFileExtensions.length > 0) {
289
+ console.log(`[CUSTOM-EXTENSIONS] Adding ${customFileExtensions.length} custom extensions: ${customFileExtensions.join(", ")}`);
290
+ this.host.context.addCustomExtensions(customFileExtensions);
291
+ }
292
+ if (customIgnorePatterns.length > 0) {
293
+ console.log(`[IGNORE-PATTERNS] Adding ${customIgnorePatterns.length} custom ignore patterns: ${customIgnorePatterns.join(", ")}`);
294
+ this.host.context.addCustomIgnorePatterns(customIgnorePatterns);
295
+ }
296
+ const failedInfo = this.host.getSnapshotCodebaseInfo(absolutePath);
297
+ if (failedInfo?.status === "indexfailed") {
298
+ const previousError = typeof failedInfo.errorMessage === "string" ? failedInfo.errorMessage : "Unknown error";
299
+ console.log(`[BACKGROUND-INDEX] Retrying indexing for previously failed codebase. Previous error: ${previousError}`);
300
+ }
301
+ this.host.snapshotManager.setCodebaseIndexing(absolutePath, 0);
302
+ this.host.saveSnapshotIfSupported();
303
+ trackCodebasePath(absolutePath);
304
+ await this.host.touchWatchedCodebase(absolutePath);
305
+ const startBackgroundIndexing = this.host.startBackgroundIndexing
306
+ ?? this.startBackgroundIndexing.bind(this);
307
+ void startBackgroundIndexing(absolutePath, forceReindex, stagedCollectionName);
308
+ const pathInfo = codebasePath !== absolutePath
309
+ ? `\nNote: Input path '${codebasePath}' was resolved to absolute path '${absolutePath}'`
310
+ : "";
311
+ const extensionInfo = customFileExtensions.length > 0
312
+ ? `\nUsing ${customFileExtensions.length} custom extensions: ${customFileExtensions.join(", ")}`
313
+ : "";
314
+ const ignoreInfo = customIgnorePatterns.length > 0
315
+ ? `\nUsing ${customIgnorePatterns.length} custom ignore patterns: ${customIgnorePatterns.join(", ")}`
316
+ : "";
317
+ return this.host.manageResponse(manageAction, absolutePath, "ok", `Started background indexing for codebase '${absolutePath}'.${pathInfo}${dropSummaryLine}${extensionInfo}${ignoreInfo}\n\nIndexing is running in the background. You can search the codebase while indexing is in progress, but results may be incomplete until indexing completes.`, preflightOptions);
318
+ }
319
+ catch (error) {
320
+ console.error("Error in handleIndexCodebase:", error);
321
+ const vectorBackendDiagnostic = classifyVectorBackendError(error);
322
+ if (vectorBackendDiagnostic) {
323
+ const errorMessage = formatUnknownError(error);
324
+ const preservesLocalState = errorMessage.includes("Force reindex cleanup failed before local state changes");
325
+ const humanText = preservesLocalState
326
+ ? `${vectorBackendDiagnostic.message} ${errorMessage}`
327
+ : vectorBackendDiagnostic.message;
328
+ return this.host.manageVectorBackendResponse(manageAction, ensureAbsolutePath(codebasePath), vectorBackendDiagnostic, humanText);
329
+ }
330
+ return this.host.manageResponse(manageAction, ensureAbsolutePath(codebasePath), "error", `Error starting indexing: ${formatUnknownError(error)}`, preflightOptions);
331
+ }
332
+ }
333
+ async handleReindexCodebase(args) {
334
+ const { path: codebasePath, customExtensions, ignorePatterns, zillizDropCollection, allowUnnecessaryReindex } = args;
335
+ const absolutePath = ensureAbsolutePath(codebasePath);
336
+ const runtimeOwnerConflict = await this.host.buildRuntimeOwnerConflictResponseIfBlocked("reindex", absolutePath);
337
+ if (runtimeOwnerConflict) {
338
+ return runtimeOwnerConflict;
339
+ }
340
+ const preflight = this.host.evaluateReindexPreflight(absolutePath);
341
+ if (preflight.outcome === "reindex_unnecessary_ignore_only" && allowUnnecessaryReindex !== true) {
342
+ return this.host.manageResponse("reindex", absolutePath, "blocked", `Reindex preflight blocked for '${absolutePath}': only ignore/index-policy control changes were detected. Use manage_index with {"action":"sync","path":"${absolutePath}"} for immediate convergence.`, {
343
+ reason: "unnecessary_reindex_ignore_only",
344
+ warnings: preflight.warnings,
345
+ preflight,
346
+ hints: {
347
+ sync: {
348
+ tool: "manage_index",
349
+ args: { action: "sync", path: absolutePath },
350
+ },
351
+ overrideReindex: {
352
+ tool: "manage_index",
353
+ args: { action: "reindex", path: absolutePath, allowUnnecessaryReindex: true },
354
+ },
355
+ },
356
+ });
357
+ }
358
+ const forwardedPreflight = preflight.outcome === "unknown" || preflight.outcome === "probe_failed"
359
+ ? preflight
360
+ : undefined;
361
+ return this.handleIndexCodebase({
362
+ path: codebasePath,
363
+ force: true,
364
+ customExtensions,
365
+ ignorePatterns,
366
+ zillizDropCollection,
367
+ __reindexPreflight: forwardedPreflight,
368
+ });
369
+ }
370
+ async handleRepairIndex(args) {
371
+ const codebasePath = args.path;
372
+ if (typeof codebasePath !== "string" || codebasePath.trim().length === 0) {
373
+ return this.host.manageResponse("repair", "", "error", "Error: Path is required.");
374
+ }
375
+ let absolutePath = codebasePath;
376
+ try {
377
+ absolutePath = ensureAbsolutePath(codebasePath);
378
+ if (!fs.existsSync(absolutePath)) {
379
+ return this.host.manageResponse("repair", absolutePath, "error", `Error: Path '${absolutePath}' does not exist. Original input: '${codebasePath}'`);
380
+ }
381
+ const stat = fs.statSync(absolutePath);
382
+ if (!stat.isDirectory()) {
383
+ return this.host.manageResponse("repair", absolutePath, "error", `Error: Path '${absolutePath}' is not a directory`);
384
+ }
385
+ const runtimeOwnerConflict = await this.host.buildRuntimeOwnerConflictResponseIfBlocked("repair", absolutePath);
386
+ if (runtimeOwnerConflict) {
387
+ return runtimeOwnerConflict;
388
+ }
389
+ if (this.host.getSnapshotIndexingCodebases().includes(absolutePath)) {
390
+ return this.host.manageResponse("repair", absolutePath, "not_ready", this.host.buildManageActionBlockedMessage(absolutePath, "repair"), {
391
+ reason: "indexing",
392
+ hints: {
393
+ status: this.host.buildStatusHint(absolutePath),
394
+ retryAfterMs: this.host.getManageRetryAfterMs(),
395
+ indexing: this.host.buildIndexingMetadata(absolutePath),
396
+ },
397
+ });
398
+ }
399
+ const trustedFingerprint = isMatchingVerifiedFingerprint(this.host.getSnapshotCodebaseInfo(absolutePath), this.host.runtimeFingerprint);
400
+ const result = await this.host.context.repairIndex(absolutePath, { trustedFingerprint });
401
+ if (result.status === "ok") {
402
+ await this.host.rebuildCallGraphForIndex(absolutePath);
403
+ await this.host.touchWatchedCodebase(absolutePath);
404
+ const stats = {
405
+ indexedFiles: result.indexedFiles || 0,
406
+ totalChunks: result.totalChunks || 0,
407
+ status: "completed"
408
+ };
409
+ this.host.snapshotManager.setCodebaseIndexed(absolutePath, stats, this.host.runtimeFingerprint, "verified", result.collectionName);
410
+ const trackedRelativePaths = result.trackedRelativePaths;
411
+ this.host.snapshotManager.setCodebaseIndexManifest(absolutePath, Array.isArray(trackedRelativePaths)
412
+ ? trackedRelativePaths.filter((entry) => typeof entry === "string")
413
+ : this.host.getContextTrackedRelativePaths(absolutePath));
414
+ this.host.setIndexingStats(stats);
415
+ this.host.saveSnapshotIfSupported();
416
+ const warningsArray = Array.isArray(result.warnings) ? result.warnings : [];
417
+ return this.host.manageResponse("repair", absolutePath, "ok", result.message, {
418
+ warnings: warningsArray,
419
+ });
420
+ }
421
+ else if (result.status === "requires_reindex") {
422
+ return this.host.manageResponse("repair", absolutePath, "requires_reindex", result.message, {
423
+ reason: "requires_reindex",
424
+ hints: this.host.buildManageRequiresReindexHints(absolutePath),
425
+ });
426
+ }
427
+ else {
428
+ return this.host.manageResponse("repair", absolutePath, "blocked", result.message, {
429
+ reason: result.reason || "needs_create",
430
+ hints: {
431
+ create: this.host.buildCreateHint(absolutePath),
432
+ missingCount: result.missingCount,
433
+ }
434
+ });
435
+ }
436
+ }
437
+ catch (error) {
438
+ console.error("Error in handleRepairIndex:", error);
439
+ const vectorBackendDiagnostic = classifyVectorBackendError(error);
440
+ if (vectorBackendDiagnostic) {
441
+ return this.host.manageVectorBackendResponse("repair", absolutePath, vectorBackendDiagnostic);
442
+ }
443
+ return this.host.manageResponse("repair", absolutePath, "error", `Error performing repair: ${formatUnknownError(error)}`);
444
+ }
445
+ }
446
+ async startBackgroundIndexing(codebasePath, forceReindex, writeCollectionName) {
447
+ const absolutePath = codebasePath;
448
+ let lastSaveTime = 0;
449
+ let targetCollectionName;
450
+ try {
451
+ console.log(`[BACKGROUND-INDEX] Starting background indexing for: ${absolutePath}`);
452
+ targetCollectionName = typeof writeCollectionName === "string" && writeCollectionName.trim().length > 0
453
+ ? writeCollectionName
454
+ : this.host.resolveCollectionName(absolutePath);
455
+ this.host.setWriteCollectionOverride(absolutePath, targetCollectionName);
456
+ if (forceReindex) {
457
+ console.log("[BACKGROUND-INDEX] ℹ️ Force reindex mode - building a staged generation before retiring the previous proven collection.");
458
+ }
459
+ const profileConfig = this.host.loadIndexProfileForCodebase(absolutePath);
460
+ console.log(`[BACKGROUND-INDEX] Using index profile '${profileConfig.profile}'${profileConfig.configPath ? ` from ${profileConfig.configPath}` : " (default)"}`);
461
+ await this.host.context.loadResolvedIgnorePatterns(absolutePath);
462
+ const { FileSynchronizer } = await import("@zokizuan/satori-core");
463
+ const ignorePatterns = this.host.getContextActiveIgnorePatterns(absolutePath);
464
+ const supportedExtensions = this.host.getContextIndexedExtensions(absolutePath);
465
+ console.log(`[BACKGROUND-INDEX] Using ignore patterns: ${ignorePatterns.join(", ")}`);
466
+ const synchronizer = new FileSynchronizer(absolutePath, ignorePatterns, supportedExtensions);
467
+ await synchronizer.initialize();
468
+ await this.host.context.ensureCollectionPrepared(absolutePath);
469
+ this.host.context.registerSynchronizer(this.host.resolveCollectionName(absolutePath), synchronizer);
470
+ console.log(`[BACKGROUND-INDEX] Starting indexing for: ${absolutePath}`);
471
+ const encoderEngine = this.host.context.getEmbeddingEngine();
472
+ console.log(`[BACKGROUND-INDEX] 🧠 Using embedding provider: ${encoderEngine.getProvider()} with dimension: ${encoderEngine.getDimension()}`);
473
+ console.log("[BACKGROUND-INDEX] 🚀 Beginning codebase indexing process...");
474
+ const stats = await this.host.context.indexCodebase(absolutePath, (progress) => {
475
+ this.host.snapshotManager.setCodebaseIndexing(absolutePath, progress.percentage);
476
+ const currentTime = Date.now();
477
+ if (currentTime - lastSaveTime >= 2000) {
478
+ this.host.saveSnapshotIfSupported();
479
+ lastSaveTime = currentTime;
480
+ console.log(`[BACKGROUND-INDEX] 💾 Saved progress snapshot at ${progress.percentage.toFixed(1)}%`);
481
+ }
482
+ console.log(`[BACKGROUND-INDEX] Progress: ${progress.phase} - ${progress.percentage}% (${progress.current}/${progress.total})`);
483
+ });
484
+ console.log(`[BACKGROUND-INDEX] ✅ Indexing completed successfully! Files: ${stats.indexedFiles}, Chunks: ${stats.totalChunks}`);
485
+ try {
486
+ const droppedCollections = await this.host.pruneIndexedCollectionFamily(absolutePath, targetCollectionName);
487
+ if (droppedCollections.length > 0) {
488
+ console.log(`[BACKGROUND-INDEX] 🧹 Retired ${droppedCollections.length} superseded collection(s): ${droppedCollections.join(", ")}`);
489
+ }
490
+ }
491
+ catch (pruneError) {
492
+ console.warn(`[BACKGROUND-INDEX] Failed to retire superseded generations for '${absolutePath}': ${formatUnknownError(pruneError)}`);
493
+ }
494
+ this.host.snapshotManager.setCodebaseIndexed(absolutePath, stats, this.host.runtimeFingerprint, "verified", targetCollectionName);
495
+ this.host.snapshotManager.setCodebaseIndexManifest(absolutePath, this.host.getContextTrackedRelativePaths(absolutePath));
496
+ this.host.setIndexingStats({ indexedFiles: stats.indexedFiles, totalChunks: stats.totalChunks });
497
+ await this.host.syncManager.recordCurrentIgnoreControlSignature(absolutePath);
498
+ this.host.saveSnapshotIfSupported();
499
+ await this.host.rebuildCallGraphForIndex(absolutePath);
500
+ await this.host.touchWatchedCodebase(absolutePath);
501
+ let message = `Background indexing completed for '${absolutePath}'.\nIndexed ${stats.indexedFiles} files, ${stats.totalChunks} chunks.`;
502
+ if (stats.status === "limit_reached") {
503
+ message += "\n⚠️ Warning: Indexing stopped because the chunk limit (450,000) was reached. The index may be incomplete.";
504
+ }
505
+ console.log(`[BACKGROUND-INDEX] ${message}`);
506
+ }
507
+ catch (error) {
508
+ console.error(`[BACKGROUND-INDEX] Error during indexing for ${absolutePath}:`, error);
509
+ let errorMessage = formatUnknownError(error);
510
+ if (isCollectionLimitError(error)) {
511
+ errorMessage = await this.host.buildCollectionLimitMessage(absolutePath);
512
+ }
513
+ try {
514
+ await this.host.clearIndexCompletionMarker(absolutePath);
515
+ }
516
+ catch (clearError) {
517
+ console.warn(`[BACKGROUND-INDEX] Failed to clear completion marker after indexing error for '${absolutePath}': ${formatUnknownError(clearError)}`);
518
+ }
519
+ await this.cleanupFailedStagedCollection(absolutePath, targetCollectionName);
520
+ this.host.snapshotManager.setCodebaseIndexFailed(absolutePath, errorMessage, this.host.getSnapshotIndexingProgress(absolutePath));
521
+ this.host.saveSnapshotIfSupported();
522
+ console.error(`[BACKGROUND-INDEX] Indexing failed for ${absolutePath}: ${errorMessage}`);
523
+ }
524
+ finally {
525
+ this.host.setWriteCollectionOverride(absolutePath, null);
526
+ }
527
+ }
528
+ }
529
+ //# sourceMappingURL=manage-indexing-handlers.js.map