@mrclrchtr/supi-code-intelligence 4.10.0 → 5.0.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 +2 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/README.md +80 -5
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/package.json +7 -6
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +10 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +206 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +78 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +114 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +372 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +14 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +62 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +491 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +71 -11
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +206 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +365 -297
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +52 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +185 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-file-state.ts +25 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +467 -115
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +161 -33
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +10 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/config/defaults.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +45 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +38 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +79 -9
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +13 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +300 -24
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +5 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +464 -96
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-refactor-provider.ts +130 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +45 -329
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/refactor-planning.ts +57 -120
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-edit-normalizer.ts +320 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +207 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/readiness.ts +72 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +151 -26
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +40 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +50 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +136 -120
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +49 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +102 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +17 -7
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/LICENSE +21 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/README.md +258 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/babel.cjs +257 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/jiti.cjs +1 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-cli.mjs +34 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-hooks.mjs +124 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-native.mjs +121 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.d.mts +1 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.mjs +4 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-static.mjs +23 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.cjs +30 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.cts +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.mts +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.mjs +29 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/types.d.ts +420 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/package.json +146 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +6 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +0 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -8
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +16 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +65 -87
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +47 -95
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-timing.ts +147 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client-lifecycle.ts +39 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +561 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-message-size.ts +24 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +452 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +12 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/imports.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +5 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +33 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/blocking-test-bootstrap.mjs +34 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/bootstrap.mjs +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store-helpers.ts +23 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +400 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/request-control.ts +48 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-parser-helpers.ts +24 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-query-helpers.ts +43 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +421 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +116 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +201 -0
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +236 -24
- package/src/analysis/health/recovery.ts +6 -2
- package/src/analysis/provider.ts +58 -0
- package/src/analysis/readiness.ts +30 -9
- package/src/analysis/refactor/apply.ts +45 -1
- package/src/analysis/refactor/mutation-authority.ts +143 -0
- package/src/analysis/relations/callees.ts +9 -1
- package/src/analysis/relations/types.ts +5 -2
- package/src/analysis/search/ast-scan-timing.ts +9 -2
- package/src/analysis/search/ast-scan.ts +1 -1
- package/src/analysis/search/deadline.ts +2 -2
- package/src/analysis/search/pattern-analysis.ts +46 -32
- package/src/analysis/search/pattern.ts +12 -2
- package/src/analysis/target/anchored.ts +20 -11
- package/src/analysis/target/file.ts +18 -11
- package/src/analysis/target/identity.ts +19 -3
- package/src/analysis/target/symbol.ts +36 -15
- package/src/api.ts +4 -0
- package/src/diagnostics/evidence.ts +51 -0
- package/src/session/capability-adapter.ts +12 -3
- package/src/session/find-workflow.ts +17 -7
- package/src/session/graph/collect.ts +18 -2
- package/src/session/graph-workflow.ts +19 -12
- package/src/session/health-refresh.ts +148 -0
- package/src/session/health-types.ts +37 -15
- package/src/session/health-workflow.ts +37 -39
- package/src/session/inspect/collect.ts +20 -3
- package/src/session/inspect-workflow.ts +10 -5
- package/src/session/orientation/collect.ts +10 -1
- package/src/session/orientation/context-facts.ts +3 -1
- package/src/session/orientation/context-sections.ts +20 -7
- package/src/session/orientation/gather.ts +17 -11
- package/src/session/orientation-types.ts +2 -1
- package/src/session/orientation-workflow.ts +15 -8
- package/src/session/refactor-plans.ts +2 -0
- package/src/session/refactor-workflow.ts +59 -14
- package/src/session/session.ts +4 -1
- package/src/session/target-workflow.ts +54 -23
- package/src/session/workflow-control.ts +7 -3
- package/src/substrate/lsp/lifecycle.ts +26 -46
- package/src/substrate/lsp/maintenance.ts +199 -25
- package/src/substrate/lsp/state.ts +2 -2
- package/src/substrate/workspace-provider-host.ts +25 -3
- package/src/tool/find/execute.ts +7 -1
- package/src/tool/find/render.ts +20 -3
- package/src/tool/find/tui.ts +23 -21
- package/src/tool/graph/execute.ts +23 -3
- package/src/tool/graph/tui.ts +59 -46
- package/src/tool/health/execute.ts +7 -2
- package/src/tool/health/markdown.ts +56 -14
- package/src/tool/health/refresh-status.ts +105 -0
- package/src/tool/health/tui.ts +95 -98
- package/src/tool/infra/workflow-control.ts +8 -0
- package/src/tool/inspect/execute.ts +7 -1
- package/src/tool/inspect/tui.ts +26 -18
- package/src/tool/orientation/execute.ts +21 -3
- package/src/tool/orientation/tui.ts +51 -35
- package/src/tool/refactor-apply/execute.ts +3 -0
- package/src/tool/refactor-apply/tui.ts +43 -25
- package/src/tool/refactor-plan/execute.ts +4 -0
- package/src/tool/refactor-plan/tui.ts +33 -20
- package/src/tool/register.ts +127 -22
- package/src/tool/resolve/execute.ts +18 -1
- package/src/tool/resolve/tui.ts +42 -24
- package/src/tool/result/display.ts +107 -0
- package/src/tool/result/errors.ts +20 -17
- package/src/tool/result/find.ts +52 -8
- package/src/tool/result/graph.ts +44 -1
- package/src/tool/result/health.ts +53 -1
- package/src/tool/result/inspect.ts +113 -1
- package/src/tool/result/orientation.ts +66 -14
- package/src/tool/result/refactor.ts +36 -4
- package/src/tool/result/resolve.ts +44 -7
- package/src/tool/result/types.ts +20 -0
- package/src/types/execution.ts +18 -5
- package/src/types/index.ts +8 -1
- package/src/ui/status-command.ts +5 -3
- package/src/ui/status-overlay.ts +4 -0
- package/src/ui/tui/common.ts +108 -10
- package/src/ui/tui/display.ts +48 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime.ts +0 -351
|
@@ -1,4 +1,18 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
type CodeRequestControl,
|
|
4
|
+
isCodeRequestInterruption,
|
|
5
|
+
throwIfCodeRequestInterrupted,
|
|
6
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
7
|
+
import type { RecoveryRestartReason } from "../client/client.ts";
|
|
8
|
+
import { getDiagnosticFileState } from "../client/client-file-state.ts";
|
|
1
9
|
import type { Diagnostic, FileEvent } from "../config/types.ts";
|
|
10
|
+
import {
|
|
11
|
+
type DiagnosticEvidenceDocument,
|
|
12
|
+
type DiagnosticEvidenceSummary,
|
|
13
|
+
emptyDiagnosticEvidence,
|
|
14
|
+
summarizeDiagnosticEvidence,
|
|
15
|
+
} from "../diagnostics/evidence.ts";
|
|
2
16
|
import {
|
|
3
17
|
assessStaleDiagnostics,
|
|
4
18
|
type StaleDiagnosticAssessment,
|
|
@@ -8,18 +22,51 @@ export interface WorkspaceRecoveryResult {
|
|
|
8
22
|
/** Active clients targeted by the best-effort refresh, not confirmed successful refreshes. */
|
|
9
23
|
attemptedClients: number;
|
|
10
24
|
restartedClients: number;
|
|
25
|
+
/** Server names of the active clients targeted by this pass, for telemetry identity. */
|
|
26
|
+
attemptedServers: string[];
|
|
27
|
+
/** Server names of the clients restarted during this pass, for telemetry identity. */
|
|
28
|
+
restartedServers: string[];
|
|
29
|
+
/** Stall signal of the first restarted route, when this pass restarted a client. */
|
|
30
|
+
restartReason?: RecoveryRestartReason;
|
|
31
|
+
/** Final document-level evidence from the last refresh in this recovery pass. */
|
|
32
|
+
diagnosticEvidence: DiagnosticEvidenceSummary;
|
|
33
|
+
/** Failure from the first refresh, when no later pass replaced it. */
|
|
34
|
+
refreshFailureReason?: string;
|
|
35
|
+
/** Wall-clock duration of the whole recovery pass, for telemetry. */
|
|
36
|
+
elapsedMs: number;
|
|
11
37
|
staleAssessment: StaleDiagnosticAssessment;
|
|
12
38
|
}
|
|
13
39
|
|
|
40
|
+
/** One client route's diagnostic evidence capability for recovery targeting. */
|
|
41
|
+
export interface WorkspaceDiagnosticRoute {
|
|
42
|
+
key: string;
|
|
43
|
+
/** False for push-only clients, which cannot confirm freshness by pull. */
|
|
44
|
+
supportsPull: boolean;
|
|
45
|
+
/** Files tracked by the route whose evidence is unconfirmed. */
|
|
46
|
+
unconfirmedFiles: string[];
|
|
47
|
+
/** Protocol-stall signal observed on the route, or null when healthy. */
|
|
48
|
+
stallSignal: RecoveryRestartReason | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
14
51
|
export interface WorkspaceRecoveryHost {
|
|
15
52
|
clearAllPullResultIds(): void;
|
|
16
53
|
notifyWorkspaceFileChanges(changes: FileEvent[]): void;
|
|
17
|
-
refreshOpenDiagnostics(
|
|
54
|
+
refreshOpenDiagnostics(
|
|
55
|
+
options?: { maxWaitMs?: number; quietMs?: number } & CodeRequestControl,
|
|
56
|
+
): Promise<DiagnosticEvidenceSummary>;
|
|
18
57
|
getOutstandingDiagnostics(
|
|
19
58
|
maxSeverity?: number,
|
|
20
59
|
): Array<{ file: string; diagnostics: Diagnostic[] }>;
|
|
21
|
-
|
|
22
|
-
|
|
60
|
+
getRunningClientCount(): number;
|
|
61
|
+
getRunningClientNames(): string[];
|
|
62
|
+
isDiagnosticFile(filePath: string): boolean;
|
|
63
|
+
getClientDiagnosticRoutes(): WorkspaceDiagnosticRoute[];
|
|
64
|
+
getDiagnosticEvidence(): DiagnosticEvidenceSummary;
|
|
65
|
+
getCwd(): string;
|
|
66
|
+
restartClientsForFiles(
|
|
67
|
+
filePaths: string[],
|
|
68
|
+
options?: { pushOnly?: boolean; control?: CodeRequestControl },
|
|
69
|
+
): Promise<Array<{ key: string; serverName: string; files: string[]; restarted: boolean }>>;
|
|
23
70
|
}
|
|
24
71
|
|
|
25
72
|
/** Clear cached pull IDs and forward watched-file changes to active clients. */
|
|
@@ -29,10 +76,16 @@ export function softRecoverWorkspaceDiagnostics(
|
|
|
29
76
|
): number {
|
|
30
77
|
host.clearAllPullResultIds();
|
|
31
78
|
if (changes.length > 0) host.notifyWorkspaceFileChanges(changes);
|
|
32
|
-
return
|
|
79
|
+
return host.getRunningClientCount();
|
|
33
80
|
}
|
|
34
81
|
|
|
35
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Run a recovery pass, refreshing diagnostics and escalating if stale state remains.
|
|
84
|
+
*
|
|
85
|
+
* The pass observes request cancellation between its phases and propagates
|
|
86
|
+
* the interruption as a rejection instead of swallowing it. A pass that
|
|
87
|
+
* starts already cancelled rejects before any client or evidence work.
|
|
88
|
+
*/
|
|
36
89
|
export async function recoverWorkspaceDiagnostics(
|
|
37
90
|
host: WorkspaceRecoveryHost,
|
|
38
91
|
options: {
|
|
@@ -40,45 +93,268 @@ export async function recoverWorkspaceDiagnostics(
|
|
|
40
93
|
restartIfStillStale?: boolean;
|
|
41
94
|
maxWaitMs?: number;
|
|
42
95
|
quietMs?: number;
|
|
96
|
+
control?: CodeRequestControl;
|
|
43
97
|
} = {},
|
|
44
98
|
): Promise<WorkspaceRecoveryResult> {
|
|
99
|
+
// Reject immediately when the request was already cancelled: no client or
|
|
100
|
+
// evidence work may start for a pass the caller no longer awaits.
|
|
101
|
+
throwIfCodeRequestInterrupted(options.control);
|
|
102
|
+
const recoveryStartedAt = Date.now();
|
|
45
103
|
const attemptedClients = softRecoverWorkspaceDiagnostics(host, options.changes ?? []);
|
|
104
|
+
const attemptedServers = host.getRunningClientNames();
|
|
105
|
+
let diagnosticEvidence = emptyDiagnosticEvidence();
|
|
106
|
+
let refreshFailureReason: string | undefined;
|
|
46
107
|
|
|
47
108
|
try {
|
|
48
|
-
await host.refreshOpenDiagnostics({
|
|
49
|
-
|
|
50
|
-
|
|
109
|
+
diagnosticEvidence = await host.refreshOpenDiagnostics({
|
|
110
|
+
maxWaitMs: options.maxWaitMs,
|
|
111
|
+
quietMs: options.quietMs,
|
|
112
|
+
...options.control,
|
|
113
|
+
});
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (isCodeRequestInterruption(error, options.control)) throw error;
|
|
116
|
+
refreshFailureReason = errorMessage(error);
|
|
117
|
+
diagnosticEvidence = host.getDiagnosticEvidence();
|
|
51
118
|
}
|
|
52
119
|
|
|
53
120
|
let staleAssessment = assessStaleDiagnostics(host.getOutstandingDiagnostics(1));
|
|
54
121
|
let restartedClients = 0;
|
|
122
|
+
let restartServerNames: string[] = [];
|
|
123
|
+
let restartReason: RecoveryRestartReason | undefined;
|
|
55
124
|
|
|
56
|
-
if (options.restartIfStillStale
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
);
|
|
125
|
+
if (options.restartIfStillStale) {
|
|
126
|
+
// Observe cancellation between pass phases: an abort that arrived during
|
|
127
|
+
// the first refresh stops the pass before any client restart.
|
|
128
|
+
throwIfCodeRequestInterrupted(options.control);
|
|
129
|
+
const restartTargets = collectRecoveryRestartTargets(host);
|
|
130
|
+
const restartFiles = restartTargets.flatMap((target) => target.files);
|
|
131
|
+
if (restartFiles.length > 0) {
|
|
132
|
+
const escalation = await runRestartEscalation(host, {
|
|
133
|
+
restartFiles,
|
|
134
|
+
restartTargets,
|
|
135
|
+
evidence: diagnosticEvidence,
|
|
136
|
+
...options,
|
|
137
|
+
});
|
|
138
|
+
restartedClients = escalation.restartedClients;
|
|
139
|
+
restartServerNames = escalation.restartedServerNames;
|
|
140
|
+
restartReason = escalation.restartReason;
|
|
141
|
+
diagnosticEvidence = escalation.diagnosticEvidence;
|
|
142
|
+
if (escalation.refreshedAfterRestart) refreshFailureReason = undefined;
|
|
143
|
+
staleAssessment = assessStaleDiagnostics(host.getOutstandingDiagnostics(1));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Observe cancellation before returning regardless of escalation: a
|
|
148
|
+
// refresh-only pass must not report a clean result after the caller
|
|
149
|
+
// cancelled the request.
|
|
150
|
+
throwIfCodeRequestInterrupted(options.control);
|
|
151
|
+
|
|
152
|
+
return recoveryResult();
|
|
153
|
+
|
|
154
|
+
function recoveryResult(): WorkspaceRecoveryResult {
|
|
155
|
+
return {
|
|
156
|
+
attemptedClients,
|
|
157
|
+
restartedClients,
|
|
158
|
+
attemptedServers,
|
|
159
|
+
restartedServers: restartServerNames,
|
|
160
|
+
...(restartReason ? { restartReason } : {}),
|
|
161
|
+
diagnosticEvidence,
|
|
162
|
+
...(refreshFailureReason ? { refreshFailureReason } : {}),
|
|
163
|
+
elapsedMs: Date.now() - recoveryStartedAt,
|
|
164
|
+
staleAssessment,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** One push-only route that a recovery pass may restart. */
|
|
170
|
+
interface RecoveryRestartTarget {
|
|
171
|
+
key: string;
|
|
172
|
+
reason: RecoveryRestartReason;
|
|
173
|
+
files: string[];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Collect restart targets: push-only routes with a protocol-stall signal.
|
|
178
|
+
*
|
|
179
|
+
* Unconfirmed evidence alone never restarts a client: the reopen-resync
|
|
180
|
+
* fallback handles unconfirmed push-only documents without discarding
|
|
181
|
+
* server state (ADR 0020). Restarts require a readiness stall or repeated
|
|
182
|
+
* protocol failures, and stay limited to push-only routes.
|
|
183
|
+
*/
|
|
184
|
+
function collectRecoveryRestartTargets(host: WorkspaceRecoveryHost): RecoveryRestartTarget[] {
|
|
185
|
+
const targets: RecoveryRestartTarget[] = [];
|
|
186
|
+
for (const route of host.getClientDiagnosticRoutes()) {
|
|
187
|
+
if (route.supportsPull) continue;
|
|
188
|
+
if (!route.stallSignal) continue;
|
|
189
|
+
if (route.unconfirmedFiles.length === 0) continue;
|
|
190
|
+
targets.push({
|
|
191
|
+
key: route.key,
|
|
192
|
+
reason: route.stallSignal,
|
|
193
|
+
files: route.unconfirmedFiles,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return targets;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Restart affected push-only clients and merge the replacement refresh evidence.
|
|
201
|
+
*
|
|
202
|
+
* Cancellation observed during the escalation is propagated as a rejection;
|
|
203
|
+
* only non-interruption failures degrade to unconfirmed evidence.
|
|
204
|
+
*/
|
|
205
|
+
async function runRestartEscalation(
|
|
206
|
+
host: WorkspaceRecoveryHost,
|
|
207
|
+
options: {
|
|
208
|
+
restartFiles: readonly string[];
|
|
209
|
+
restartTargets: readonly RecoveryRestartTarget[];
|
|
210
|
+
evidence: DiagnosticEvidenceSummary;
|
|
211
|
+
maxWaitMs?: number;
|
|
212
|
+
quietMs?: number;
|
|
213
|
+
control?: CodeRequestControl;
|
|
214
|
+
},
|
|
215
|
+
): Promise<{
|
|
216
|
+
restartedClients: number;
|
|
217
|
+
restartedServerNames: string[];
|
|
218
|
+
restartReason?: RecoveryRestartReason;
|
|
219
|
+
diagnosticEvidence: DiagnosticEvidenceSummary;
|
|
220
|
+
refreshedAfterRestart: boolean;
|
|
221
|
+
}> {
|
|
222
|
+
const { restartFiles, restartTargets, evidence } = options;
|
|
223
|
+
const reasonByKey = new Map(restartTargets.map((target) => [target.key, target.reason]));
|
|
224
|
+
let diagnosticEvidence = evidence;
|
|
225
|
+
let restartedClients = 0;
|
|
226
|
+
let restartedServerNames: string[] = [];
|
|
227
|
+
let restartReason: RecoveryRestartReason | undefined;
|
|
228
|
+
let refreshedAfterRestart = false;
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
const restartResults = await host.restartClientsForFiles([...restartFiles], {
|
|
232
|
+
pushOnly: true,
|
|
233
|
+
control: options.control,
|
|
234
|
+
});
|
|
235
|
+
// Observe cancellation once the restart loop settles: a cancelled pass
|
|
236
|
+
// must not invalidate or refresh evidence for replacement processes.
|
|
237
|
+
throwIfCodeRequestInterrupted(options.control);
|
|
238
|
+
const restarted = restartResults.filter((result) => result.restarted);
|
|
60
239
|
restartedClients = restarted.length;
|
|
240
|
+
restartedServerNames = restarted.map((result) => result.serverName);
|
|
241
|
+
restartReason = firstRestartReason(restarted, reasonByKey);
|
|
242
|
+
// Every attempted route had its client replaced or shut down; its owned
|
|
243
|
+
// files need fresh evidence. Routes the manager skipped (pull-capable or
|
|
244
|
+
// guard-blocked) produce no result and keep their refreshed evidence.
|
|
245
|
+
const attemptedFiles = restartResults
|
|
246
|
+
.flatMap((result) => result.files)
|
|
247
|
+
.filter((file) => host.isDiagnosticFile(file));
|
|
248
|
+
diagnosticEvidence = invalidateDiagnosticEvidence(evidence, attemptedFiles, host.getCwd());
|
|
61
249
|
|
|
62
|
-
if (
|
|
250
|
+
if (restarted.length > 0) {
|
|
63
251
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
252
|
+
diagnosticEvidence = mergeDiagnosticEvidence(
|
|
253
|
+
diagnosticEvidence,
|
|
254
|
+
await host.refreshOpenDiagnostics({
|
|
255
|
+
maxWaitMs: options.maxWaitMs,
|
|
256
|
+
quietMs: options.quietMs,
|
|
257
|
+
...options.control,
|
|
258
|
+
}),
|
|
259
|
+
host.getCwd(),
|
|
260
|
+
);
|
|
261
|
+
// Observe cancellation after the replacement refresh: a cancelled
|
|
262
|
+
// pass discards the fresh result instead of reporting it as current.
|
|
263
|
+
throwIfCodeRequestInterrupted(options.control);
|
|
264
|
+
refreshedAfterRestart = true;
|
|
265
|
+
} catch (error) {
|
|
266
|
+
if (isCodeRequestInterruption(error, options.control)) throw error;
|
|
267
|
+
// Keep affected evidence unconfirmed when replacement refresh fails.
|
|
70
268
|
}
|
|
71
|
-
staleAssessment = assessStaleDiagnostics(host.getOutstandingDiagnostics(1));
|
|
72
269
|
}
|
|
270
|
+
} catch (error) {
|
|
271
|
+
if (isCodeRequestInterruption(error, options.control)) throw error;
|
|
272
|
+
// Keep affected evidence unconfirmed when replacement fails.
|
|
73
273
|
}
|
|
74
274
|
|
|
75
275
|
return {
|
|
76
|
-
attemptedClients,
|
|
77
276
|
restartedClients,
|
|
78
|
-
|
|
277
|
+
restartedServerNames,
|
|
278
|
+
...(restartReason ? { restartReason } : {}),
|
|
279
|
+
diagnosticEvidence,
|
|
280
|
+
refreshedAfterRestart,
|
|
79
281
|
};
|
|
80
282
|
}
|
|
81
283
|
|
|
82
|
-
|
|
83
|
-
|
|
284
|
+
/** Return the stall signal of the first route that actually restarted. */
|
|
285
|
+
function firstRestartReason(
|
|
286
|
+
restarted: Array<{ key: string }>,
|
|
287
|
+
reasonByKey: ReadonlyMap<string, RecoveryRestartReason>,
|
|
288
|
+
): RecoveryRestartReason | undefined {
|
|
289
|
+
for (const result of restarted) {
|
|
290
|
+
const reason = reasonByKey.get(result.key);
|
|
291
|
+
if (reason !== undefined) return reason;
|
|
292
|
+
}
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function invalidateDiagnosticEvidence(
|
|
297
|
+
evidence: DiagnosticEvidenceSummary,
|
|
298
|
+
files: readonly string[],
|
|
299
|
+
cwd: string,
|
|
300
|
+
): DiagnosticEvidenceSummary {
|
|
301
|
+
const affected = new Set(files.map((file) => path.resolve(cwd, file)));
|
|
302
|
+
const documents = evidence.documents.map((document) =>
|
|
303
|
+
affected.has(path.resolve(cwd, document.file))
|
|
304
|
+
? {
|
|
305
|
+
...document,
|
|
306
|
+
status: invalidatedFileStatus(path.resolve(cwd, document.file)),
|
|
307
|
+
}
|
|
308
|
+
: document,
|
|
309
|
+
);
|
|
310
|
+
const known = new Set(documents.map((document) => path.resolve(cwd, document.file)));
|
|
311
|
+
for (const file of files) {
|
|
312
|
+
const resolved = path.resolve(cwd, file);
|
|
313
|
+
if (!known.has(resolved)) {
|
|
314
|
+
documents.push({
|
|
315
|
+
file: path.relative(cwd, resolved),
|
|
316
|
+
status: invalidatedFileStatus(resolved),
|
|
317
|
+
});
|
|
318
|
+
known.add(resolved);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return summarizeDiagnosticEvidence(documents as DiagnosticEvidenceDocument[]);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function invalidatedFileStatus(filePath: string): "unconfirmed" | "failed" | "removed" {
|
|
325
|
+
switch (getDiagnosticFileState(filePath)) {
|
|
326
|
+
case "removed":
|
|
327
|
+
return "removed";
|
|
328
|
+
case "unreadable":
|
|
329
|
+
return "failed";
|
|
330
|
+
default:
|
|
331
|
+
return "unconfirmed";
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function errorMessage(error: unknown): string {
|
|
336
|
+
return error instanceof Error && error.message ? error.message : "Diagnostic refresh failed.";
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function mergeDiagnosticEvidence(
|
|
340
|
+
invalidated: DiagnosticEvidenceSummary,
|
|
341
|
+
refreshed: DiagnosticEvidenceSummary,
|
|
342
|
+
cwd: string,
|
|
343
|
+
): DiagnosticEvidenceSummary {
|
|
344
|
+
const documents = new Map<string, DiagnosticEvidenceDocument>();
|
|
345
|
+
for (const document of invalidated.documents) {
|
|
346
|
+
const resolved = path.resolve(cwd, document.file);
|
|
347
|
+
documents.set(resolved, {
|
|
348
|
+
file: path.relative(cwd, resolved),
|
|
349
|
+
status: document.status,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
for (const document of refreshed.documents) {
|
|
353
|
+
const resolved = path.resolve(cwd, document.file);
|
|
354
|
+
documents.set(resolved, {
|
|
355
|
+
file: path.relative(cwd, resolved),
|
|
356
|
+
status: document.status,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
return summarizeDiagnosticEvidence(Array.from(documents.values()));
|
|
84
360
|
}
|
|
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import {
|
|
4
4
|
type CodeQueryResult,
|
|
5
|
+
type CodeRequestControl,
|
|
5
6
|
completedCodeQuery,
|
|
6
7
|
partialCodeQuery,
|
|
7
8
|
unavailableCodeQuery,
|
|
@@ -55,6 +56,7 @@ export interface WorkspaceSymbolCollection {
|
|
|
55
56
|
export async function collectWorkspaceSymbols(
|
|
56
57
|
clients: Iterable<LspClient>,
|
|
57
58
|
query: string,
|
|
59
|
+
control?: CodeRequestControl,
|
|
58
60
|
): Promise<WorkspaceSymbolCollection> {
|
|
59
61
|
const results: WorkspaceSymbolLike[] = [];
|
|
60
62
|
const failures: string[] = [];
|
|
@@ -65,7 +67,7 @@ export async function collectWorkspaceSymbols(
|
|
|
65
67
|
if (client.status !== "running") continue;
|
|
66
68
|
if (!client.serverCapabilities?.workspaceSymbolProvider) continue;
|
|
67
69
|
hasSupport = true;
|
|
68
|
-
const result = await client.workspaceSymbol(query);
|
|
70
|
+
const result = await client.workspaceSymbol(query, control);
|
|
69
71
|
if (result.kind === "unavailable") {
|
|
70
72
|
failures.push(result.reason);
|
|
71
73
|
continue;
|
|
@@ -99,8 +101,9 @@ export function workspaceSymbolCollectionResult(
|
|
|
99
101
|
export async function managerWorkspaceSymbol(
|
|
100
102
|
clients: Iterable<LspClient>,
|
|
101
103
|
query: string,
|
|
104
|
+
control?: CodeRequestControl,
|
|
102
105
|
): Promise<CodeQueryResult<WorkspaceSymbolLike[]>> {
|
|
103
|
-
return workspaceSymbolCollectionResult(await collectWorkspaceSymbols(clients, query));
|
|
106
|
+
return workspaceSymbolCollectionResult(await collectWorkspaceSymbols(clients, query, control));
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
export function findWorkspaceSymbolWarmTargets(
|