@zokizuan/satori-mcp 4.11.16 → 4.11.17

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 (49) hide show
  1. package/README.md +5 -3
  2. package/dist/config.d.ts +18 -0
  3. package/dist/core/call-graph.d.ts +2 -2
  4. package/dist/core/call-graph.js +5 -3
  5. package/dist/core/current-source-symbols.d.ts +15 -0
  6. package/dist/core/current-source-symbols.js +237 -0
  7. package/dist/core/handlers.d.ts +16 -2
  8. package/dist/core/handlers.js +196 -47
  9. package/dist/core/manage-indexing-handlers.d.ts +13 -10
  10. package/dist/core/manage-indexing-handlers.js +425 -69
  11. package/dist/core/manage-maintenance-handlers.d.ts +6 -4
  12. package/dist/core/manage-maintenance-handlers.js +181 -27
  13. package/dist/core/manage-types.d.ts +13 -2
  14. package/dist/core/mutation-lease.d.ts +71 -0
  15. package/dist/core/mutation-lease.js +274 -0
  16. package/dist/core/navigation-handlers.js +1 -1
  17. package/dist/core/registry-file-outline.d.ts +1 -1
  18. package/dist/core/registry-file-outline.js +99 -39
  19. package/dist/core/relationship-backed-call-graph.d.ts +4 -3
  20. package/dist/core/relationship-backed-call-graph.js +27 -8
  21. package/dist/core/runtime-owner.d.ts +3 -0
  22. package/dist/core/runtime-owner.js +1 -1
  23. package/dist/core/search-exact-fast-path.d.ts +1 -0
  24. package/dist/core/search-exact-fast-path.js +21 -1
  25. package/dist/core/search-execution.js +30 -1
  26. package/dist/core/search-query-support.d.ts +5 -0
  27. package/dist/core/search-query-support.js +148 -1
  28. package/dist/core/search-response-helpers.js +9 -0
  29. package/dist/core/snapshot.d.ts +34 -3
  30. package/dist/core/snapshot.js +397 -21
  31. package/dist/core/sync.d.ts +20 -3
  32. package/dist/core/sync.js +302 -52
  33. package/dist/core/tool-response-builders.d.ts +9 -1
  34. package/dist/core/tool-response-builders.js +16 -1
  35. package/dist/core/tracked-root-readiness.d.ts +0 -1
  36. package/dist/core/tracked-root-readiness.js +0 -1
  37. package/dist/core/vector-backend-maintenance.d.ts +14 -3
  38. package/dist/core/vector-backend-maintenance.js +149 -27
  39. package/dist/core/warnings.d.ts +1 -0
  40. package/dist/core/warnings.js +1 -0
  41. package/dist/index.js +14 -5
  42. package/dist/server/bootstrap-stdio.d.ts +1 -1
  43. package/dist/server/provider-runtime.d.ts +3 -0
  44. package/dist/server/provider-runtime.js +15 -6
  45. package/dist/server/start-server.d.ts +4 -2
  46. package/dist/server/start-server.js +21 -37
  47. package/dist/tools/list_codebases.js +3 -1
  48. package/dist/tools/manage_index.js +6 -2
  49. package/package.json +2 -2
package/README.md CHANGED
@@ -45,7 +45,7 @@ For Codex, add `--install-guidance-hook` only when you want an installer-managed
45
45
  Advanced direct execution is available through the package bin:
46
46
 
47
47
  ```bash
48
- npx -y @zokizuan/satori-mcp@4.11.16 --help
48
+ npx -y @zokizuan/satori-mcp@4.11.17 --help
49
49
  ```
50
50
 
51
51
  Use direct package execution for inspection, smoke tests, or unsupported harnesses. For supported clients, prefer `satori-cli install` so startup does not depend on package-manager resolution.
@@ -73,10 +73,12 @@ Important defaults:
73
73
  - `read_file` only reads under tracked searchable roots (`indexed` / `sync_completed`); it is not a general host filesystem reader. Relative paths and root escapes are denied.
74
74
  - Prefer `recommendedNextAction` when present; if `callGraphHint.supported=false`, follow `navigationFallback` args rather than inventing spans.
75
75
  - `requires_reindex` means reindex first, then retry the original call (do not substitute `sync`).
76
- - `manage_index action="clear"` is destructive and should be explicit. `repair` only rebuilds local readiness when vector payload and trusted fingerprint proof match; otherwise use create/reindex.
76
+ - `manage_index action="clear"` is destructive and should be explicit. `repair` reports structured proof across collection, snapshot, marker, fingerprint, payload, stale remote chunks, and navigation. Only no related collection routes to create; existing incompatible, incomplete, stale, malformed, or unprovable generations route to reindex. Backend failures preserve partial proof when available and should be diagnosed before retrying repair. Successful repair may write a fresh completion marker and rebuild navigation, but never re-embeds or rewrites source chunks.
77
77
  - After changing `EMBEDDING_PROVIDER`, `EMBEDDING_MODEL`, embedding dimension, `HYBRID_MODE`, vector backend settings, or the Satori runtime version, restart **all** Satori MCP clients before running `manage_index create`, `reindex`, `sync`, `clear`, or `repair`.
78
78
  - Satori records live runtime owners in `~/.satori/runtime/owners.json` and blocks those index mutations with `status="blocked"` / `reason="runtime_owner_conflict"` if another live Satori MCP runtime has a different fingerprint, package version, or config identity.
79
79
  - On `runtime_owner_conflict`, the manage envelope lists conflicting **pids**, **versions**, and conflict reasons, plus a concrete `hints.nextStep`. MCP tools never kill other processes. Stop the listed host clients (or only orphaned Satori MCP node PIDs), leave a single package version/config running, then retry. `manage_index status` and `list_codebases` also show a compact **Runtime owners** line (live pids/versions); multi-version means mutations may block. `satori-cli doctor` reports multi-version live owners.
80
+ - A separate canonical-root mutation lease permits multiple readers but only one create/reindex/sync/repair/clear writer across processes. Contention returns `reason="mutation_in_progress"`; `manage_index status` exposes `hints.activeMutation`. Leases do not have a time-based expiry, and a live owner is never evicted by age alone.
81
+ - `zillizDropCollection` is fail-closed: Satori must prove the selected collection's owning codebase root and lease both affected roots before deletion. Unknown ownership or an active writer leaves the collection and local snapshot unchanged.
80
82
  - Grouped `search_codebase` results with supported call graph attach **`inboundRecovery`**: a ready-to-run `must:` search to verify callers before blast-radius edits (`call_graph` inbound stays advisory/low).
81
83
 
82
84
  ## Navigation Sidecars
@@ -136,7 +138,7 @@ The full generated tool reference below is kept in the npm README for MCP client
136
138
 
137
139
  ### `manage_index`
138
140
 
139
- Manage index lifecycle operations (create/reindex/sync/status/clear/repair) for a codebase path. repair rebuilds local readiness only when existing vector payload and trusted runtime fingerprint proof match; otherwise it refuses and asks for create/reindex. Ignore-rule edits in repo-root .satoriignore/.gitignore reconcile automatically in the normal sync path. Use action="sync" for immediate convergence and action="reindex" for full rebuild recovery (preflight may block unnecessary ignore-only reindex churn unless allowUnnecessaryReindex=true). create/reindex return the kickoff response immediately and do not poll to terminal state; use action="status" to observe progress.
141
+ Manage index lifecycle operations (create/reindex/sync/status/clear/repair) for a codebase path. repair rebuilds local readiness only when existing vector payload and trusted runtime fingerprint proof match. Repair responses may include optional `repairProof` evidence for collection, snapshot, marker, fingerprint, payload, staleRemoteChunks, and navigation. No related collection routes to create; an existing incompatible, incomplete, stale, malformed, or unprovable generation routes to reindex; backend failures preserve partial proof when collection began and should be diagnosed before retrying repair. Successful repair may write a fresh completion marker and rebuild navigation, but it does not re-embed or rewrite source chunks. Ignore-rule edits in repo-root .satoriignore/.gitignore reconcile automatically in the normal sync path. Use action="sync" for immediate convergence and action="reindex" for full rebuild recovery (preflight may block unnecessary ignore-only reindex churn unless allowUnnecessaryReindex=true). Successful sync responses include `syncStats` with `added`, `removed`, and `modified` counts; consume those fields instead of parsing `humanText`. create/reindex return the kickoff response immediately and do not poll to terminal state; use action="status" to observe progress. Status may include `languageCapabilities`, which combines declared claims with compatible symbol-registry and relationship-sidecar evidence for indexed languages. Mutation responses may include a durable `operation` receipt with `id`, canonical root, generation, accepted time, current phase, last durable transition, runtime fingerprint, and writer identity. Status returns the latest persisted receipt after restart. In a status envelope, top-level `action` remains `status` while `operation.action` names the observed mutation. Terminal phases are `completed`, `failed`, and `blocked`; `operation` is absent when no durable operation exists or contention was rejected before lease acquisition.
140
142
 
141
143
  | Parameter | Type | Required | Default | Description |
142
144
  |---|---|---|---|---|
package/dist/config.d.ts CHANGED
@@ -12,6 +12,23 @@ export interface IndexFingerprint {
12
12
  vectorStoreProvider: VectorStoreProvider;
13
13
  schemaVersion: 'dense_v3' | 'hybrid_v3';
14
14
  }
15
+ export type IndexOperationAction = 'create' | 'reindex' | 'sync' | 'repair' | 'clear';
16
+ export type IndexOperationPhase = 'accepted' | 'preflight' | 'scanning' | 'writing' | 'proving' | 'publishing' | 'completed' | 'failed' | 'blocked';
17
+ export interface IndexOperationReceipt {
18
+ id: string;
19
+ action: IndexOperationAction;
20
+ canonicalRoot: string;
21
+ generation: number;
22
+ acceptedAt: string;
23
+ phase: IndexOperationPhase;
24
+ lastDurableTransitionAt: string;
25
+ runtimeFingerprint: IndexFingerprint;
26
+ writer: {
27
+ ownerId: string;
28
+ pid: number;
29
+ satoriVersion: string;
30
+ };
31
+ }
15
32
  export interface ContextMcpConfig {
16
33
  name: string;
17
34
  version: string;
@@ -103,6 +120,7 @@ export interface CodebaseSnapshotV3 {
103
120
  formatVersion: 'v3';
104
121
  codebases: Record<string, CodebaseInfo>;
105
122
  clearTombstones?: Record<string, CodebaseClearTombstone>;
123
+ latestOperations?: Record<string, IndexOperationReceipt>;
106
124
  lastUpdated: string;
107
125
  }
108
126
  export type CodebaseSnapshot = CodebaseSnapshotV1 | CodebaseSnapshotV2 | CodebaseSnapshotV3;
@@ -107,8 +107,8 @@ export declare class CallGraphSidecarManager {
107
107
  });
108
108
  shouldRebuildForDelta(changedFiles: string[]): boolean;
109
109
  loadSidecar(codebasePath: string): CallGraphSidecar | null;
110
- rebuildForCodebase(codebasePath: string, ignorePatterns?: string[]): Promise<CallGraphSidecarInfo>;
111
- rebuildIfSupportedDelta(codebasePath: string, changedFiles: string[], ignorePatterns?: string[]): Promise<CallGraphSidecarInfo | null>;
110
+ rebuildForCodebase(codebasePath: string, ignorePatterns?: string[], assertMutationCurrent?: () => void): Promise<CallGraphSidecarInfo>;
111
+ rebuildIfSupportedDelta(codebasePath: string, changedFiles: string[], ignorePatterns?: string[], assertMutationCurrent?: () => void): Promise<CallGraphSidecarInfo | null>;
112
112
  queryGraph(codebasePath: string, symbolRef: CallGraphSymbolRef, options: {
113
113
  direction: CallGraphDirection;
114
114
  depth: number;
@@ -68,7 +68,7 @@ export class CallGraphSidecarManager {
68
68
  return null;
69
69
  }
70
70
  }
71
- async rebuildForCodebase(codebasePath, ignorePatterns = []) {
71
+ async rebuildForCodebase(codebasePath, ignorePatterns = [], assertMutationCurrent) {
72
72
  const absoluteRoot = path.resolve(codebasePath);
73
73
  const files = await this.collectSourceFiles(absoluteRoot, ignorePatterns);
74
74
  const graph = await this.buildGraph(absoluteRoot, files);
@@ -83,7 +83,9 @@ export class CallGraphSidecarManager {
83
83
  };
84
84
  const sidecarPath = this.getSidecarPath(absoluteRoot);
85
85
  const sidecarDir = path.dirname(sidecarPath);
86
+ assertMutationCurrent?.();
86
87
  await fs.promises.mkdir(sidecarDir, { recursive: true });
88
+ assertMutationCurrent?.();
87
89
  await fs.promises.writeFile(sidecarPath, JSON.stringify(sidecar, null, 2), 'utf8');
88
90
  return {
89
91
  version: 'v3',
@@ -95,11 +97,11 @@ export class CallGraphSidecarManager {
95
97
  fingerprint: this.runtimeFingerprint,
96
98
  };
97
99
  }
98
- async rebuildIfSupportedDelta(codebasePath, changedFiles, ignorePatterns = []) {
100
+ async rebuildIfSupportedDelta(codebasePath, changedFiles, ignorePatterns = [], assertMutationCurrent) {
99
101
  if (!this.shouldRebuildForDelta(changedFiles)) {
100
102
  return null;
101
103
  }
102
- return this.rebuildForCodebase(codebasePath, ignorePatterns);
104
+ return this.rebuildForCodebase(codebasePath, ignorePatterns, assertMutationCurrent);
103
105
  }
104
106
  queryGraph(codebasePath, symbolRef, options) {
105
107
  const languageSupport = this.isSupportedQueryLanguage(symbolRef.file);
@@ -0,0 +1,15 @@
1
+ import { type SymbolRecord } from "@zokizuan/satori-core";
2
+ import type { PythonSourceBackedSpanRepair } from "./python-call-fallback.js";
3
+ export type CurrentSourceSymbolValidation = PythonSourceBackedSpanRepair & {
4
+ match: "matched" | "missing" | "ambiguous" | "unavailable" | "not_applicable";
5
+ };
6
+ /**
7
+ * Rebuild exact-navigation identities from the current file using the same
8
+ * splitter and registry builder as indexing. Persisted IDs remain handles;
9
+ * only source-proven spans are substituted.
10
+ */
11
+ export declare function validateCurrentSourceSymbolSpans(input: {
12
+ codebaseRoot: string;
13
+ symbols: SymbolRecord[];
14
+ }): Promise<CurrentSourceSymbolValidation[]>;
15
+ //# sourceMappingURL=current-source-symbols.d.ts.map
@@ -0,0 +1,237 @@
1
+ import * as crypto from "node:crypto";
2
+ import * as fs from "node:fs/promises";
3
+ import * as path from "node:path";
4
+ import { AstCodeSplitter, buildSymbolRecordsForFile, normalizeLanguageId, openRegularFileInsideRoot, } from "@zokizuan/satori-core";
5
+ const CURRENT_SOURCE_LANGUAGES = new Set(["typescript", "javascript", "python"]);
6
+ const CURRENT_SOURCE_MAX_BYTES = 256 * 1024;
7
+ function isInsideRoot(candidate, root) {
8
+ const relative = path.relative(root, candidate);
9
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
10
+ }
11
+ async function readCurrentSource(codebaseRoot, relativeFile) {
12
+ let handle;
13
+ try {
14
+ const canonicalRoot = await fs.realpath(codebaseRoot);
15
+ const logicalFile = path.resolve(canonicalRoot, relativeFile);
16
+ if (!isInsideRoot(logicalFile, canonicalRoot)) {
17
+ return undefined;
18
+ }
19
+ handle = await openRegularFileInsideRoot(logicalFile, canonicalRoot);
20
+ const stat = await handle.stat();
21
+ if (stat.size > CURRENT_SOURCE_MAX_BYTES) {
22
+ return undefined;
23
+ }
24
+ const buffer = Buffer.allocUnsafe(CURRENT_SOURCE_MAX_BYTES + 1);
25
+ let offset = 0;
26
+ while (offset < buffer.length) {
27
+ const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, offset);
28
+ if (bytesRead === 0)
29
+ break;
30
+ offset += bytesRead;
31
+ }
32
+ if (offset > CURRENT_SOURCE_MAX_BYTES) {
33
+ return undefined;
34
+ }
35
+ return buffer.subarray(0, offset).toString("utf8");
36
+ }
37
+ catch {
38
+ return undefined;
39
+ }
40
+ finally {
41
+ await handle?.close().catch(() => undefined);
42
+ }
43
+ }
44
+ function unchangedValidation(symbol, match) {
45
+ return {
46
+ symbol,
47
+ attempted: match !== "not_applicable",
48
+ validated: match === "matched",
49
+ repaired: false,
50
+ startBeforeDefinition: false,
51
+ endTruncated: false,
52
+ match,
53
+ };
54
+ }
55
+ function currentDeclarationKey(symbol) {
56
+ const span = symbol.span;
57
+ if (span.endByte !== undefined) {
58
+ // Export/decorator wrapper nodes and their declaration node share the
59
+ // same end boundary. Distinct same-line declarations do not.
60
+ return `${symbol.symbolKey}\0endByte:${span.endByte}`;
61
+ }
62
+ return [
63
+ symbol.symbolKey,
64
+ span.endLine,
65
+ span.endColumn ?? "",
66
+ ].join("\0");
67
+ }
68
+ function minOptional(left, right) {
69
+ if (left === undefined)
70
+ return right;
71
+ if (right === undefined)
72
+ return left;
73
+ return Math.min(left, right);
74
+ }
75
+ function maxOptional(left, right) {
76
+ if (left === undefined)
77
+ return right;
78
+ if (right === undefined)
79
+ return left;
80
+ return Math.max(left, right);
81
+ }
82
+ function compareOptionalPosition(left, right) {
83
+ if (left === right)
84
+ return 0;
85
+ if (left === undefined)
86
+ return 1;
87
+ if (right === undefined)
88
+ return -1;
89
+ return left - right;
90
+ }
91
+ function compareSourcePosition(left, right) {
92
+ return left.span.startLine - right.span.startLine
93
+ || compareOptionalPosition(left.span.startByte, right.span.startByte)
94
+ || compareOptionalPosition(left.span.startColumn, right.span.startColumn)
95
+ || left.span.endLine - right.span.endLine
96
+ || compareOptionalPosition(left.span.endByte, right.span.endByte)
97
+ || compareOptionalPosition(left.span.endColumn, right.span.endColumn);
98
+ }
99
+ function hasDistinctSourcePositions(symbols) {
100
+ return symbols.every((symbol, index) => index === 0 || compareSourcePosition(symbols[index - 1], symbol) !== 0);
101
+ }
102
+ /**
103
+ * Rebuild exact-navigation identities from the current file using the same
104
+ * splitter and registry builder as indexing. Persisted IDs remain handles;
105
+ * only source-proven spans are substituted.
106
+ */
107
+ export async function validateCurrentSourceSymbolSpans(input) {
108
+ if (input.symbols.length === 0) {
109
+ return [];
110
+ }
111
+ const language = normalizeLanguageId(input.symbols[0].language);
112
+ if (!CURRENT_SOURCE_LANGUAGES.has(language)) {
113
+ return input.symbols.map((symbol) => unchangedValidation(symbol, "not_applicable"));
114
+ }
115
+ const relativeFile = input.symbols[0].file;
116
+ if (input.symbols.some((symbol) => symbol.file !== relativeFile || normalizeLanguageId(symbol.language) !== language)) {
117
+ return input.symbols.map((symbol) => unchangedValidation(symbol, "unavailable"));
118
+ }
119
+ const source = await readCurrentSource(input.codebaseRoot, relativeFile);
120
+ if (source === undefined) {
121
+ return input.symbols.map((symbol) => unchangedValidation(symbol, "unavailable"));
122
+ }
123
+ try {
124
+ const splitter = new AstCodeSplitter(Number.MAX_SAFE_INTEGER);
125
+ splitter.setChunkOverlap(0);
126
+ const split = await splitter.splitWithEvidence(source, language, relativeFile);
127
+ if (!split.structuralParseCompleted) {
128
+ return input.symbols.map((symbol) => unchangedValidation(symbol, "unavailable"));
129
+ }
130
+ const chunks = split.chunks;
131
+ const fileHash = crypto.createHash("sha256").update(source, "utf8").digest("hex");
132
+ const extractorVersion = input.symbols[0].extractorVersion;
133
+ const currentByIdentityAndSpan = new Map();
134
+ const currentFileOwner = buildSymbolRecordsForFile({
135
+ relativePath: relativeFile,
136
+ language,
137
+ content: source,
138
+ fileHash,
139
+ extractorVersion,
140
+ chunks: [],
141
+ })[0];
142
+ if (currentFileOwner) {
143
+ currentByIdentityAndSpan.set(currentDeclarationKey(currentFileOwner), currentFileOwner);
144
+ }
145
+ for (const chunk of chunks) {
146
+ const chunkSymbols = buildSymbolRecordsForFile({
147
+ relativePath: relativeFile,
148
+ language,
149
+ content: source,
150
+ fileHash,
151
+ extractorVersion,
152
+ chunks: [chunk],
153
+ });
154
+ for (const current of chunkSymbols.filter((symbol) => symbol.kind !== "file")) {
155
+ const declarationKey = currentDeclarationKey(current);
156
+ const existing = currentByIdentityAndSpan.get(declarationKey);
157
+ if (!existing) {
158
+ currentByIdentityAndSpan.set(declarationKey, current);
159
+ continue;
160
+ }
161
+ currentByIdentityAndSpan.set(declarationKey, {
162
+ ...existing,
163
+ span: {
164
+ ...existing.span,
165
+ startLine: Math.min(existing.span.startLine, current.span.startLine),
166
+ endLine: Math.max(existing.span.endLine, current.span.endLine),
167
+ startByte: minOptional(existing.span.startByte, current.span.startByte),
168
+ endByte: maxOptional(existing.span.endByte, current.span.endByte),
169
+ startColumn: minOptional(existing.span.startColumn, current.span.startColumn),
170
+ endColumn: maxOptional(existing.span.endColumn, current.span.endColumn),
171
+ },
172
+ });
173
+ }
174
+ }
175
+ const currentSymbols = [...currentByIdentityAndSpan.values()];
176
+ const currentByKey = new Map();
177
+ for (const current of currentSymbols) {
178
+ const matches = currentByKey.get(current.symbolKey) || [];
179
+ matches.push(current);
180
+ currentByKey.set(current.symbolKey, matches);
181
+ }
182
+ const persistedByKey = new Map();
183
+ for (const symbol of input.symbols) {
184
+ const matches = persistedByKey.get(symbol.symbolKey) || [];
185
+ matches.push(symbol);
186
+ persistedByKey.set(symbol.symbolKey, matches);
187
+ }
188
+ const assignedCurrentByPersistedId = new Map();
189
+ for (const [symbolKey, persisted] of persistedByKey) {
190
+ const current = currentByKey.get(symbolKey) || [];
191
+ if (persisted.length !== current.length) {
192
+ continue;
193
+ }
194
+ if (persisted.length > 1 && persisted.some((symbol) => symbol.fileHash !== fileHash)) {
195
+ continue;
196
+ }
197
+ const sortedPersisted = [...persisted].sort(compareSourcePosition);
198
+ const sortedCurrent = [...current].sort(compareSourcePosition);
199
+ if (persisted.length > 1
200
+ && (!hasDistinctSourcePositions(sortedPersisted) || !hasDistinctSourcePositions(sortedCurrent))) {
201
+ continue;
202
+ }
203
+ for (let index = 0; index < sortedPersisted.length; index += 1) {
204
+ assignedCurrentByPersistedId.set(sortedPersisted[index].symbolInstanceId, sortedCurrent[index]);
205
+ }
206
+ }
207
+ return input.symbols.map((symbol) => {
208
+ const matches = currentByKey.get(symbol.symbolKey) || [];
209
+ if (matches.length === 0) {
210
+ return unchangedValidation(symbol, "missing");
211
+ }
212
+ const current = assignedCurrentByPersistedId.get(symbol.symbolInstanceId);
213
+ if (!current) {
214
+ return unchangedValidation(symbol, "ambiguous");
215
+ }
216
+ const repaired = current.span.startLine !== symbol.span.startLine
217
+ || current.span.endLine !== symbol.span.endLine
218
+ || current.span.startByte !== symbol.span.startByte
219
+ || current.span.endByte !== symbol.span.endByte
220
+ || current.span.startColumn !== symbol.span.startColumn
221
+ || current.span.endColumn !== symbol.span.endColumn;
222
+ return {
223
+ symbol: repaired ? { ...symbol, span: current.span } : symbol,
224
+ attempted: true,
225
+ validated: true,
226
+ repaired,
227
+ startBeforeDefinition: symbol.span.startLine < current.span.startLine,
228
+ endTruncated: symbol.span.endLine < current.span.endLine,
229
+ match: "matched",
230
+ };
231
+ });
232
+ }
233
+ catch {
234
+ return input.symbols.map((symbol) => unchangedValidation(symbol, "unavailable"));
235
+ }
236
+ }
237
+ //# sourceMappingURL=current-source-symbols.js.map
@@ -8,6 +8,7 @@ import { CallGraphSidecarManager } from "./call-graph.js";
8
8
  import { type ReindexPreflightResult } from "./working-tree-state.js";
9
9
  import { type SearchDiagnostics } from "./search-execution.js";
10
10
  import { type RuntimeOwnerMutationGate } from "./runtime-owner.js";
11
+ import { MutationLeaseCoordinator } from "./mutation-lease.js";
11
12
  type IndexCodebaseArgs = {
12
13
  path: string;
13
14
  force?: boolean;
@@ -26,6 +27,7 @@ type ReindexCodebaseArgs = {
26
27
  type ToolArgs = Record<string, unknown>;
27
28
  export declare class ToolHandlers {
28
29
  private readonly runtimeOwnerGate;
30
+ private readonly mutationLeaseCoordinator;
29
31
  private context;
30
32
  private snapshotManager;
31
33
  private syncManager;
@@ -48,7 +50,7 @@ export declare class ToolHandlers {
48
50
  private readonly vectorBackendMaintenance;
49
51
  private readonly relationshipBackedCallGraph;
50
52
  private readonly toolResponseBuilders;
51
- constructor(context: Context, snapshotManager: SnapshotManager, syncManager: SyncManager, runtimeFingerprint: IndexFingerprint, capabilities: CapabilityResolver, now?: () => number, callGraphManager?: CallGraphSidecarManager, reranker?: VoyageAIReranker | null, gitignoreForceReloadEveryN?: number, navigationStore?: NavigationStore, runtimeOwnerGate?: RuntimeOwnerMutationGate | null);
53
+ constructor(context: Context, snapshotManager: SnapshotManager, syncManager: SyncManager, runtimeFingerprint: IndexFingerprint, capabilities: CapabilityResolver, now?: () => number, callGraphManager?: CallGraphSidecarManager, reranker?: VoyageAIReranker | null, gitignoreForceReloadEveryN?: number, navigationStore?: NavigationStore, runtimeOwnerGate?: RuntimeOwnerMutationGate | null, mutationLeaseCoordinator?: MutationLeaseCoordinator | null);
52
54
  private setIndexingStats;
53
55
  private clearIndexingStats;
54
56
  private createSearchPhaseTimings;
@@ -94,7 +96,20 @@ export declare class ToolHandlers {
94
96
  private buildRuntimeOwnerConflictResponse;
95
97
  private buildIndexingMetadata;
96
98
  private isIndexingStateStale;
99
+ /**
100
+ * Recover abandoned `indexing` lifecycle rows.
101
+ *
102
+ * Wall-clock grace applies only before exclusive ownership is proven. When
103
+ * `existingLease` is held (or startup forces exclusive acquisition), recovery
104
+ * runs immediately because a live compliant writer cannot share the root.
105
+ */
97
106
  private recoverStaleIndexingStateIfNeeded;
107
+ /**
108
+ * Startup entry for interrupted-index recovery. Acquires a mutation lease per
109
+ * root, skips live writers, and reuses the fenced recovery path (no unfenced
110
+ * snapshot lifecycle publication).
111
+ */
112
+ recoverInterruptedIndexingAtStartup(): Promise<void>;
98
113
  private buildManageActionBlockedMessage;
99
114
  private getManageRetryAfterMs;
100
115
  private buildStaleLocalHint;
@@ -110,7 +125,6 @@ export declare class ToolHandlers {
110
125
  private isPathWithinCodebase;
111
126
  private getTrackedRootEntryForPath;
112
127
  private probeLocalSearchCollectionState;
113
- private markCodebaseSearchStateMissing;
114
128
  private summarizeFingerprint;
115
129
  private fingerprintsEqual;
116
130
  private isRuntimeFingerprintMismatch;