@mrclrchtr/supi-code-intelligence 6.4.0 → 7.0.1
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 +164 -82
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/README.md +35 -13
- 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/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +8 -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/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/index.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/legacy.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +6 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +43 -35
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +83 -17
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +108 -92
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +44 -82
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +3 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +9 -68
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +45 -58
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +230 -331
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +201 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +129 -70
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-typescript.ts +395 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +8 -12
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +685 -183
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +60 -60
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-request-enrollment.ts +75 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-barrier.ts +443 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-errors.ts +30 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-file.ts +15 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +290 -37
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +72 -16
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +1 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +30 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +10 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +50 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +11 -7
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +608 -45
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +37 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +31 -11
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +19 -3
- 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/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +8 -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/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callee-display-name.ts +165 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +67 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +2 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/package.json +1 -1
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +1 -2
- package/src/analysis/health/file-scope.ts +4 -1
- package/src/analysis/health/server-status.ts +1 -1
- package/src/analysis/relations/callees.ts +17 -8
- package/src/analysis/relations/implementations.ts +1 -2
- package/src/analysis/relations/references.ts +8 -4
- package/src/analysis/relations/types.ts +6 -30
- package/src/analysis/target/anchored.ts +9 -1
- package/src/analysis/target/symbol.ts +29 -11
- package/src/analysis/target/types.ts +14 -6
- package/src/api.ts +2 -0
- package/src/session/graph/collect.ts +18 -3
- package/src/session/health-refresh.ts +12 -1
- package/src/session/health-types.ts +18 -6
- package/src/session/health-workflow.ts +21 -19
- package/src/session/orientation-types.ts +5 -6
- package/src/session/orientation-workflow.ts +10 -2
- package/src/session/target-workflow.ts +9 -6
- package/src/substrate/lsp/source-tracking.ts +11 -6
- package/src/tool/code_find/guidance.ts +2 -2
- package/src/tool/code_find/spec.ts +5 -4
- package/src/tool/code_find/tui.ts +7 -1
- package/src/tool/code_graph/details.ts +32 -0
- package/src/tool/code_graph/display.ts +54 -0
- package/src/tool/code_graph/format.ts +60 -0
- package/src/tool/code_graph/guidance.ts +1 -1
- package/src/tool/code_graph/markdown.ts +76 -198
- package/src/tool/code_graph/read-next.ts +65 -0
- package/src/tool/code_graph/result.ts +118 -83
- package/src/tool/code_graph/tui.ts +216 -54
- package/src/tool/code_health/guidance.ts +2 -4
- package/src/tool/code_health/markdown.ts +75 -58
- package/src/tool/code_health/refresh-outcome.ts +41 -1
- package/src/tool/code_health/refresh-status.ts +114 -48
- package/src/tool/code_health/scope.ts +78 -0
- package/src/tool/code_health/spec.ts +1 -1
- package/src/tool/code_health/tui.ts +25 -38
- package/src/tool/code_inspect/tui.ts +7 -1
- package/src/tool/code_orientation/result.ts +31 -8
- package/src/tool/code_orientation/tui.ts +26 -5
- package/src/tool/code_refactor_apply/guidance.ts +1 -1
- package/src/tool/code_refactor_apply/tui.ts +7 -3
- package/src/tool/code_refactor_plan/guidance.ts +1 -1
- package/src/tool/code_refactor_plan/tui.ts +7 -1
- package/src/tool/code_resolve/guidance.ts +2 -4
- package/src/tool/code_resolve/result.ts +13 -10
- package/src/tool/code_resolve/tui.ts +18 -1
- package/src/tool/infra/truncate.ts +39 -13
- package/src/tool/result/candidate-row.ts +27 -0
- package/src/tool/result/errors.ts +21 -6
- package/src/tool/result/types.ts +7 -3
- package/src/tool/schemas.ts +5 -0
- package/src/types/execution.ts +2 -0
- package/src/types/index.ts +2 -0
- package/src/ui/tui/candidate-legacy-body.ts +69 -0
- package/src/ui/tui/candidate-selection.ts +366 -0
- package/src/ui/tui/common.ts +26 -31
- package/src/ui/tui/execution-error.ts +92 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// biome-ignore-all lint/style/noExcessiveLinesPerFile: Diagnostic validation and evidence application stay together.
|
|
1
2
|
import {
|
|
2
3
|
type CodeQueryResult,
|
|
3
4
|
partialCodeQuery,
|
|
@@ -8,7 +9,6 @@ import type {
|
|
|
8
9
|
DocumentDiagnosticReport,
|
|
9
10
|
PublishDiagnosticsParams,
|
|
10
11
|
} from "../config/types.ts";
|
|
11
|
-
import type { DiagnosticStateWait } from "./client-diagnostic-waiters.ts";
|
|
12
12
|
|
|
13
13
|
/** Validate one untrusted LSP diagnostic publication before it enters the cache. */
|
|
14
14
|
export function isValidPublishDiagnosticsParams(value: unknown): value is PublishDiagnosticsParams {
|
|
@@ -154,34 +154,33 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
154
154
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
/** Evidence source that established or observed one cached diagnostic set. */
|
|
158
|
+
export type DiagnosticEvidenceSource = "pull" | "typescript" | "push";
|
|
159
|
+
|
|
157
160
|
/** Stored diagnostic data and the protocol evidence that established it. */
|
|
158
161
|
export interface DiagnosticCacheEntry {
|
|
159
162
|
diagnostics: Diagnostic[];
|
|
160
163
|
receivedAt: number;
|
|
161
|
-
source:
|
|
164
|
+
source: DiagnosticEvidenceSource;
|
|
162
165
|
synchronizationId?: number;
|
|
163
166
|
evidenceRevision?: number;
|
|
164
167
|
version?: number;
|
|
165
168
|
resultId?: string;
|
|
166
169
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
* publication promotes the entry to confirmed (ADR 0021). A confirmed
|
|
170
|
-
* pull entry counts as the first publication when a push continues the
|
|
171
|
-
* same synchronization, so that push keeps the confirmation. Pull
|
|
172
|
-
* entries as such carry no count and are confirmed on arrival.
|
|
170
|
+
* Number of ambient publications retained for compatibility with existing
|
|
171
|
+
* telemetry. Publication count never establishes confirmation.
|
|
173
172
|
*/
|
|
174
173
|
publications?: number;
|
|
175
174
|
}
|
|
176
175
|
|
|
177
176
|
/**
|
|
178
|
-
* Test whether one
|
|
177
|
+
* Test whether one entry came from ambient push observation.
|
|
179
178
|
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
179
|
+
* Push publication count is not a completion contract. Every push stays
|
|
180
|
+
* tentative until a request-based evidence source replaces it.
|
|
182
181
|
*/
|
|
183
182
|
export function isTentativePushEntry(entry: DiagnosticCacheEntry | undefined): boolean {
|
|
184
|
-
return
|
|
183
|
+
return entry?.source === "push";
|
|
185
184
|
}
|
|
186
185
|
|
|
187
186
|
/** One document synchronization that needs current diagnostic evidence. */
|
|
@@ -214,23 +213,6 @@ export function isCurrentSynchronization(
|
|
|
214
213
|
);
|
|
215
214
|
}
|
|
216
215
|
|
|
217
|
-
/** Test whether a fresh push confirms the supplied synchronization. */
|
|
218
|
-
export function hasFreshPush(
|
|
219
|
-
store: ReadonlyMap<string, DiagnosticCacheEntry>,
|
|
220
|
-
synchronization: DiagnosticSynchronization,
|
|
221
|
-
currentEvidenceRevision?: number,
|
|
222
|
-
): boolean {
|
|
223
|
-
const entry = store.get(synchronization.uri);
|
|
224
|
-
return Boolean(
|
|
225
|
-
entry?.source === "push" &&
|
|
226
|
-
!isTentativePushEntry(entry) &&
|
|
227
|
-
entry.synchronizationId === synchronization.synchronizationId &&
|
|
228
|
-
(synchronization.evidenceRevision === undefined ||
|
|
229
|
-
entry.evidenceRevision === synchronization.evidenceRevision) &&
|
|
230
|
-
(currentEvidenceRevision === undefined || entry.evidenceRevision === currentEvidenceRevision),
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
216
|
/**
|
|
235
217
|
* Test whether stored evidence matches a synchronization without a
|
|
236
218
|
* confirmation gate. A tentative push matches here but cannot confirm.
|
|
@@ -262,16 +244,10 @@ export function hasFreshEvidence(
|
|
|
262
244
|
);
|
|
263
245
|
}
|
|
264
246
|
|
|
265
|
-
/**
|
|
266
|
-
* Return the latest cache update that matches one of the synchronizations.
|
|
267
|
-
*
|
|
268
|
-
* Every accepted publication restarts the settle quiet period, tentative
|
|
269
|
-
* publications included, so this helper does not apply the confirmation
|
|
270
|
-
* gate (ADR 0021).
|
|
271
|
-
*/
|
|
247
|
+
/** Return the latest ambient update for one of the synchronizations. */
|
|
272
248
|
export function latestCurrentEvidenceReceivedAt(
|
|
273
249
|
store: ReadonlyMap<string, DiagnosticCacheEntry>,
|
|
274
|
-
synchronizations: DiagnosticSynchronization[],
|
|
250
|
+
synchronizations: readonly DiagnosticSynchronization[],
|
|
275
251
|
currentEvidenceRevision?: number,
|
|
276
252
|
): number {
|
|
277
253
|
let latest = 0;
|
|
@@ -284,34 +260,6 @@ export function latestCurrentEvidenceReceivedAt(
|
|
|
284
260
|
return latest;
|
|
285
261
|
}
|
|
286
262
|
|
|
287
|
-
interface PullRaceOptions {
|
|
288
|
-
readonly pull: Promise<boolean>;
|
|
289
|
-
readonly waitForChange: () => DiagnosticStateWait;
|
|
290
|
-
readonly freshPush: () => boolean;
|
|
291
|
-
readonly current: () => boolean;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/** Race a pull against fresh push evidence and lifecycle release. */
|
|
295
|
-
export async function raceDiagnosticPull(
|
|
296
|
-
options: PullRaceOptions,
|
|
297
|
-
): Promise<"pull" | "push" | "released"> {
|
|
298
|
-
const pull = options.pull.then((confirmed) =>
|
|
299
|
-
confirmed ? ("pull" as const) : ("released" as const),
|
|
300
|
-
);
|
|
301
|
-
while (options.current()) {
|
|
302
|
-
if (options.freshPush()) return "push";
|
|
303
|
-
const change = options.waitForChange();
|
|
304
|
-
let outcome: "changed" | "pull" | "released";
|
|
305
|
-
try {
|
|
306
|
-
outcome = await Promise.race([pull, change.promise.then(() => "changed" as const)]);
|
|
307
|
-
} finally {
|
|
308
|
-
change.cancel();
|
|
309
|
-
}
|
|
310
|
-
if (outcome !== "changed") return outcome;
|
|
311
|
-
}
|
|
312
|
-
return "released";
|
|
313
|
-
}
|
|
314
|
-
|
|
315
263
|
/** Return explicit partial or unavailable evidence after fresh collection fails. */
|
|
316
264
|
export function incompleteDiagnosticResult(
|
|
317
265
|
cachedDiagnostics: Diagnostic[] | null,
|
|
@@ -342,6 +290,7 @@ interface ApplyPullReportOptions {
|
|
|
342
290
|
readonly synchronizationId: number | undefined;
|
|
343
291
|
readonly evidenceRevision: number;
|
|
344
292
|
readonly isRelatedUriTracked: (uri: string) => boolean;
|
|
293
|
+
readonly source?: Exclude<DiagnosticEvidenceSource, "push">;
|
|
345
294
|
}
|
|
346
295
|
|
|
347
296
|
/** Apply one valid full or linked unchanged pull report. */
|
|
@@ -365,12 +314,18 @@ export function applyPullReport(options: ApplyPullReportOptions): boolean {
|
|
|
365
314
|
store.set(uri, {
|
|
366
315
|
diagnostics: report.items,
|
|
367
316
|
receivedAt: Date.now(),
|
|
368
|
-
source: "pull",
|
|
317
|
+
source: options.source ?? "pull",
|
|
369
318
|
synchronizationId,
|
|
370
319
|
evidenceRevision,
|
|
371
320
|
resultId: report.resultId,
|
|
372
321
|
});
|
|
373
|
-
applyRelatedPullReports(
|
|
322
|
+
applyRelatedPullReports({
|
|
323
|
+
store,
|
|
324
|
+
report,
|
|
325
|
+
evidenceRevision,
|
|
326
|
+
isRelatedUriTracked,
|
|
327
|
+
source: options.source ?? "pull",
|
|
328
|
+
});
|
|
374
329
|
return true;
|
|
375
330
|
}
|
|
376
331
|
if (!previous || previousResultId === undefined || typeof report.resultId !== "string") {
|
|
@@ -379,34 +334,41 @@ export function applyPullReport(options: ApplyPullReportOptions): boolean {
|
|
|
379
334
|
store.set(uri, {
|
|
380
335
|
...previous,
|
|
381
336
|
receivedAt: Date.now(),
|
|
382
|
-
source: "pull",
|
|
337
|
+
source: options.source ?? "pull",
|
|
383
338
|
synchronizationId,
|
|
384
339
|
evidenceRevision,
|
|
385
340
|
resultId: report.resultId,
|
|
386
341
|
});
|
|
387
|
-
applyRelatedPullReports(
|
|
342
|
+
applyRelatedPullReports({
|
|
343
|
+
store,
|
|
344
|
+
report,
|
|
345
|
+
evidenceRevision,
|
|
346
|
+
isRelatedUriTracked,
|
|
347
|
+
source: options.source ?? "pull",
|
|
348
|
+
});
|
|
388
349
|
return true;
|
|
389
350
|
}
|
|
390
351
|
|
|
391
|
-
function applyRelatedPullReports(
|
|
392
|
-
store: Map<string, DiagnosticCacheEntry
|
|
393
|
-
report: DocumentDiagnosticReport
|
|
394
|
-
evidenceRevision: number
|
|
395
|
-
isRelatedUriTracked: (uri: string) => boolean
|
|
396
|
-
|
|
397
|
-
|
|
352
|
+
function applyRelatedPullReports(options: {
|
|
353
|
+
store: Map<string, DiagnosticCacheEntry>;
|
|
354
|
+
report: DocumentDiagnosticReport;
|
|
355
|
+
evidenceRevision: number;
|
|
356
|
+
isRelatedUriTracked: (uri: string) => boolean;
|
|
357
|
+
source: Exclude<DiagnosticEvidenceSource, "push">;
|
|
358
|
+
}): void {
|
|
359
|
+
for (const [relatedUri, relatedReport] of Object.entries(options.report.relatedDocuments ?? {})) {
|
|
398
360
|
if (!isValidDocumentDiagnosticReport(relatedReport, false)) continue;
|
|
399
361
|
// Skip explicit unchanged reports; full reports (kind "full", "", or
|
|
400
362
|
// absent) may enter the related-document store.
|
|
401
363
|
if (relatedReport.kind === "unchanged") continue;
|
|
402
|
-
if (isRelatedUriTracked(relatedUri)) continue;
|
|
403
|
-
const existing = store.get(relatedUri);
|
|
404
|
-
if (existing?.evidenceRevision === evidenceRevision) continue;
|
|
405
|
-
store.set(relatedUri, {
|
|
364
|
+
if (options.isRelatedUriTracked(relatedUri)) continue;
|
|
365
|
+
const existing = options.store.get(relatedUri);
|
|
366
|
+
if (existing?.evidenceRevision === options.evidenceRevision) continue;
|
|
367
|
+
options.store.set(relatedUri, {
|
|
406
368
|
diagnostics: relatedReport.items,
|
|
407
369
|
receivedAt: Date.now(),
|
|
408
|
-
source:
|
|
409
|
-
evidenceRevision,
|
|
370
|
+
source: options.source,
|
|
371
|
+
evidenceRevision: options.evidenceRevision,
|
|
410
372
|
resultId: relatedReport.resultId,
|
|
411
373
|
});
|
|
412
374
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { DiagnosticPullRequest } from "./client-diagnostic-request.ts";
|
|
1
|
+
import type { DiagnosticRequestAdapter } from "./client-diagnostic-request.ts";
|
|
3
2
|
|
|
4
3
|
/** Transport and capability operations required by diagnostic state. */
|
|
5
4
|
export interface ClientDiagnosticsHost {
|
|
@@ -8,8 +7,8 @@ export interface ClientDiagnosticsHost {
|
|
|
8
7
|
/** Absolute workspace root, for debug-telemetry identity. */
|
|
9
8
|
cwd?: string;
|
|
10
9
|
isOperational(): boolean;
|
|
11
|
-
|
|
10
|
+
/** Native pull and server-specific request evidence share this priority adapter. */
|
|
11
|
+
diagnosticRequestAdapter: DiagnosticRequestAdapter;
|
|
12
12
|
usesIncrementalDocumentSync(): boolean;
|
|
13
13
|
sendNotification(method: string, params: unknown): void;
|
|
14
|
-
pullDocumentDiagnostics(request: DiagnosticPullRequest): Promise<DocumentDiagnosticReport | null>;
|
|
15
14
|
}
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
// Bounded push-publication telemetry for one client's diagnostic state.
|
|
2
2
|
//
|
|
3
|
-
// Push-only servers can publish
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// and timing data. They never carry diagnostic payloads or source text.
|
|
3
|
+
// Push-only servers can publish one or more results for one document
|
|
4
|
+
// synchronization. This tracker records one bounded per-synchronization
|
|
5
|
+
// publication summary per diagnostic operation. Events
|
|
6
|
+
// carry only bounded server, workspace, relative-file, synchronization
|
|
7
|
+
// identity, count, and timing data. They never carry diagnostic payloads or
|
|
8
|
+
// source text. Publication counts remain telemetry only.
|
|
10
9
|
|
|
11
|
-
import * as path from "node:path";
|
|
12
10
|
import {
|
|
13
11
|
recordDebugEvent,
|
|
14
12
|
truncateDebugIdentity as truncateIdentity,
|
|
15
13
|
} from "@mrclrchtr/supi-core/debug";
|
|
16
|
-
import { uriToFile } from "@mrclrchtr/supi-core/path";
|
|
17
14
|
import { boundCwd } from "../debug-telemetry.ts";
|
|
18
15
|
|
|
19
16
|
/** Maximum tracked synchronizations before the oldest entry is evicted. */
|
|
@@ -59,8 +56,6 @@ interface SynchronizationPublicationState {
|
|
|
59
56
|
publications: number;
|
|
60
57
|
firstReceivedAt: number;
|
|
61
58
|
lastReceivedAt: number;
|
|
62
|
-
/** When a finished operation first reported this synchronization unconfirmed. */
|
|
63
|
-
unconfirmedAt?: number;
|
|
64
59
|
}
|
|
65
60
|
|
|
66
61
|
function synchronizationKey(
|
|
@@ -75,10 +70,7 @@ function synchronizationKey(
|
|
|
75
70
|
export class DiagnosticPublicationTracker {
|
|
76
71
|
readonly #states = new Map<string, SynchronizationPublicationState>();
|
|
77
72
|
|
|
78
|
-
constructor(
|
|
79
|
-
private readonly identity: { server?: string; cwd?: string },
|
|
80
|
-
private readonly fileFor: (uri: string) => string | undefined,
|
|
81
|
-
) {}
|
|
73
|
+
constructor(private readonly identity: { server?: string; cwd?: string }) {}
|
|
82
74
|
|
|
83
75
|
/**
|
|
84
76
|
* Record one accepted push publication for a synchronization.
|
|
@@ -116,9 +108,8 @@ export class DiagnosticPublicationTracker {
|
|
|
116
108
|
/**
|
|
117
109
|
* Emit one bounded per-synchronization publication summary.
|
|
118
110
|
*
|
|
119
|
-
* Synchronizations with no observed push publication are omitted.
|
|
120
|
-
*
|
|
121
|
-
* emit the ambient late-republish event.
|
|
111
|
+
* Synchronizations with no observed push publication are omitted. The
|
|
112
|
+
* caller supplies the confirmation flag; publication counts do not change it.
|
|
122
113
|
*/
|
|
123
114
|
emitSummary(options: {
|
|
124
115
|
readonly operation: "refresh-open" | "sync-file";
|
|
@@ -127,7 +118,6 @@ export class DiagnosticPublicationTracker {
|
|
|
127
118
|
readonly operationId?: string;
|
|
128
119
|
}): void {
|
|
129
120
|
const entries: DiagnosticPublicationSummaryEntry[] = [];
|
|
130
|
-
const now = Date.now();
|
|
131
121
|
for (const synchronization of options.synchronizations) {
|
|
132
122
|
const state = this.#states.get(
|
|
133
123
|
synchronizationKey(
|
|
@@ -144,8 +134,6 @@ export class DiagnosticPublicationTracker {
|
|
|
144
134
|
lastReceivedAt: state.lastReceivedAt,
|
|
145
135
|
confirmed: synchronization.confirmed,
|
|
146
136
|
});
|
|
147
|
-
if (!synchronization.confirmed) state.unconfirmedAt ??= now;
|
|
148
|
-
else state.unconfirmedAt = undefined;
|
|
149
137
|
}
|
|
150
138
|
if (entries.length === 0) return;
|
|
151
139
|
recordDebugEvent({
|
|
@@ -167,51 +155,4 @@ export class DiagnosticPublicationTracker {
|
|
|
167
155
|
},
|
|
168
156
|
});
|
|
169
157
|
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Observe one promotion of a synchronization by a later publication.
|
|
173
|
-
*
|
|
174
|
-
* The ambient late-republish event fires only when a finished operation
|
|
175
|
-
* previously reported the synchronization unconfirmed. The mark is cleared
|
|
176
|
-
* so one promotion emits at most one event.
|
|
177
|
-
*/
|
|
178
|
-
promoted(
|
|
179
|
-
uri: string,
|
|
180
|
-
synchronizationId: number,
|
|
181
|
-
evidenceRevision: number,
|
|
182
|
-
receivedAt: number = Date.now(),
|
|
183
|
-
): void {
|
|
184
|
-
const state = this.#states.get(synchronizationKey(uri, synchronizationId, evidenceRevision));
|
|
185
|
-
const unconfirmedAt = state?.unconfirmedAt;
|
|
186
|
-
if (!state || state.publications < 2 || unconfirmedAt === undefined) return;
|
|
187
|
-
state.unconfirmedAt = undefined;
|
|
188
|
-
const file = this.fileFor(uri);
|
|
189
|
-
recordDebugEvent({
|
|
190
|
-
source: "lsp",
|
|
191
|
-
level: "debug",
|
|
192
|
-
category: "diagnostics.publication",
|
|
193
|
-
message: "LSP diagnostic late republish",
|
|
194
|
-
cwd: boundCwd(this.identity.cwd),
|
|
195
|
-
data: {
|
|
196
|
-
synchronizationId,
|
|
197
|
-
publications: state.publications,
|
|
198
|
-
receivedAt,
|
|
199
|
-
delayMs: Math.max(0, receivedAt - unconfirmedAt),
|
|
200
|
-
...(this.identity.server !== undefined
|
|
201
|
-
? { server: truncateIdentity(this.identity.server) }
|
|
202
|
-
: {}),
|
|
203
|
-
...(file !== undefined ? { file: truncateIdentity(file) } : {}),
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/** Return a workspace-relative diagnostic file path for telemetry identity. */
|
|
210
|
-
export function trackerFileIdentityFor(
|
|
211
|
-
cwd: string | undefined,
|
|
212
|
-
): (uri: string) => string | undefined {
|
|
213
|
-
return (uri) => {
|
|
214
|
-
if (cwd === undefined) return undefined;
|
|
215
|
-
return path.relative(cwd, path.resolve(cwd, uriToFile(uri)));
|
|
216
|
-
};
|
|
217
158
|
}
|
|
@@ -1,62 +1,49 @@
|
|
|
1
|
-
import { CodeRequestDeadlineError } from "@mrclrchtr/supi-code-runtime/api";
|
|
2
|
-
import type { DocumentDiagnosticReport } from "../config/types.ts";
|
|
3
1
|
import { applyPullReport, type DiagnosticCacheEntry } from "./client-diagnostic-evidence.ts";
|
|
4
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
DiagnosticPullRequest,
|
|
4
|
+
DiagnosticRequestAdapter,
|
|
5
|
+
DiagnosticRequestExecution,
|
|
6
|
+
} from "./client-diagnostic-request.ts";
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
options.evidenceRevision !== options.currentRevision() ||
|
|
46
|
-
(options.synchronizationId !== undefined && !options.isCurrentSynchronization())
|
|
47
|
-
) {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
const applied = applyPullReport({
|
|
51
|
-
store: options.store,
|
|
52
|
-
uri: options.uri,
|
|
53
|
-
report,
|
|
54
|
-
previous,
|
|
55
|
-
previousResultId,
|
|
56
|
-
synchronizationId: options.synchronizationId,
|
|
57
|
-
evidenceRevision: options.evidenceRevision,
|
|
58
|
-
isRelatedUriTracked: options.isRelatedUriTracked,
|
|
8
|
+
/** Start one adapter request and apply it only while its evidence is current. */
|
|
9
|
+
export function startDiagnosticEvidenceFromAdapter(options: {
|
|
10
|
+
readonly adapter: DiagnosticRequestAdapter;
|
|
11
|
+
readonly request: DiagnosticPullRequest;
|
|
12
|
+
readonly store: Map<string, DiagnosticCacheEntry>;
|
|
13
|
+
readonly synchronizationId?: number;
|
|
14
|
+
readonly evidenceRevision: number;
|
|
15
|
+
readonly currentRevision: () => number;
|
|
16
|
+
readonly isCurrentSynchronization: () => boolean;
|
|
17
|
+
readonly markEvidenceCurrent?: (uri: string, synchronizationId: number | undefined) => void;
|
|
18
|
+
readonly isRelatedUriTracked: (uri: string) => boolean;
|
|
19
|
+
}): DiagnosticRequestExecution<boolean> {
|
|
20
|
+
const previous = options.store.get(options.request.uri);
|
|
21
|
+
const execution = options.adapter.collect(options.request);
|
|
22
|
+
const result = execution.result.then((requestResult) => {
|
|
23
|
+
if (options.request.signal?.aborted) {
|
|
24
|
+
throw options.request.signal.reason ?? new Error("Diagnostic request cancelled.");
|
|
25
|
+
}
|
|
26
|
+
if (
|
|
27
|
+
options.evidenceRevision !== options.currentRevision() ||
|
|
28
|
+
(options.synchronizationId !== undefined && !options.isCurrentSynchronization())
|
|
29
|
+
) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const applied = applyPullReport({
|
|
33
|
+
store: options.store,
|
|
34
|
+
uri: options.request.uri,
|
|
35
|
+
report: requestResult.report,
|
|
36
|
+
previous,
|
|
37
|
+
previousResultId: options.request.previousResultId,
|
|
38
|
+
synchronizationId: options.synchronizationId,
|
|
39
|
+
evidenceRevision: options.evidenceRevision,
|
|
40
|
+
isRelatedUriTracked: options.isRelatedUriTracked,
|
|
41
|
+
source: requestResult.source,
|
|
42
|
+
});
|
|
43
|
+
if (!applied) throw new Error("Invalid diagnostic request report.");
|
|
44
|
+
options.markEvidenceCurrent?.(options.request.uri, options.synchronizationId);
|
|
45
|
+
return true;
|
|
59
46
|
});
|
|
60
|
-
|
|
61
|
-
return
|
|
47
|
+
result.catch(() => {});
|
|
48
|
+
return { result, settled: execution.settled };
|
|
62
49
|
}
|