@prestyj/cli 5.6.0 → 5.7.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/dist/app-sidecar.js +127 -39
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session-review-coverage.test.d.ts +2 -0
- package/dist/core/agent-session-review-coverage.test.d.ts.map +1 -0
- package/dist/core/agent-session-review-coverage.test.js +57 -0
- package/dist/core/agent-session-review-coverage.test.js.map +1 -0
- package/dist/core/agent-session.d.ts +11 -4
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +133 -22
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +1 -1
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/event-bus.d.ts +2 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/ideal-review.d.ts +27 -0
- package/dist/core/ideal-review.d.ts.map +1 -1
- package/dist/core/ideal-review.js +64 -0
- package/dist/core/ideal-review.js.map +1 -1
- package/dist/core/ideal-review.test.js +34 -1
- package/dist/core/ideal-review.test.js.map +1 -1
- package/dist/core/lsp/client.d.ts +3 -0
- package/dist/core/lsp/client.d.ts.map +1 -1
- package/dist/core/lsp/client.js +20 -0
- package/dist/core/lsp/client.js.map +1 -1
- package/dist/core/lsp/manager.d.ts +32 -9
- package/dist/core/lsp/manager.d.ts.map +1 -1
- package/dist/core/lsp/manager.js +89 -37
- package/dist/core/lsp/manager.js.map +1 -1
- package/dist/core/lsp/manager.test.js +56 -4
- package/dist/core/lsp/manager.test.js.map +1 -1
- package/dist/core/run-lifecycle.d.ts +40 -0
- package/dist/core/run-lifecycle.d.ts.map +1 -0
- package/dist/core/run-lifecycle.js +95 -0
- package/dist/core/run-lifecycle.js.map +1 -0
- package/dist/core/run-lifecycle.test.d.ts +2 -0
- package/dist/core/run-lifecycle.test.d.ts.map +1 -0
- package/dist/core/run-lifecycle.test.js +64 -0
- package/dist/core/run-lifecycle.test.js.map +1 -0
- package/dist/core/session-manager.d.ts +25 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +66 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-manager.test.js +80 -2
- package/dist/core/session-manager.test.js.map +1 -1
- package/dist/core/subagent-manager.d.ts +40 -3
- package/dist/core/subagent-manager.d.ts.map +1 -1
- package/dist/core/subagent-manager.js +342 -30
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-manager.test.js +189 -3
- package/dist/core/subagent-manager.test.js.map +1 -1
- package/dist/core/subagent-store.d.ts +20 -0
- package/dist/core/subagent-store.d.ts.map +1 -0
- package/dist/core/subagent-store.js +177 -0
- package/dist/core/subagent-store.js.map +1 -0
- package/dist/core/subagent-store.test.d.ts +2 -0
- package/dist/core/subagent-store.test.d.ts.map +1 -0
- package/dist/core/subagent-store.test.js +94 -0
- package/dist/core/subagent-store.test.js.map +1 -0
- package/dist/modes/agent-home-mode.d.ts.map +1 -1
- package/dist/modes/agent-home-mode.js +10 -3
- package/dist/modes/agent-home-mode.js.map +1 -1
- package/dist/modes/serve-mode.d.ts.map +1 -1
- package/dist/modes/serve-mode.js +10 -3
- package/dist/modes/serve-mode.js.map +1 -1
- package/dist/modes/subagent-worker-mode.d.ts +2 -0
- package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
- package/dist/modes/subagent-worker-mode.js +13 -3
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/tools/goals.d.ts +3 -3
- package/dist/tools/tasks.d.ts +2 -2
- package/dist/tools/web-fetch.d.ts +1 -1
- package/dist/ui/App.d.ts +7 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +63 -17
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/Footer.d.ts +1 -0
- package/dist/ui/components/Footer.d.ts.map +1 -1
- package/dist/ui/components/Footer.js +3 -3
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/Footer.test.d.ts +2 -0
- package/dist/ui/components/Footer.test.d.ts.map +1 -0
- package/dist/ui/components/Footer.test.js +16 -0
- package/dist/ui/components/Footer.test.js.map +1 -0
- package/dist/ui/hooks/useAgentLoop.d.ts +8 -3
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +63 -7
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/useSessionPersistence.d.ts +4 -2
- package/dist/ui/hooks/useSessionPersistence.d.ts.map +1 -1
- package/dist/ui/hooks/useSessionPersistence.js +7 -1
- package/dist/ui/hooks/useSessionPersistence.js.map +1 -1
- package/dist/ui/render.d.ts +7 -0
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +4 -0
- package/dist/ui/render.js.map +1 -1
- package/package.json +5 -5
package/dist/core/lsp/manager.js
CHANGED
|
@@ -5,84 +5,134 @@ import { formatDiagnostics } from "./format.js";
|
|
|
5
5
|
import { LSP_SERVER_CATALOG, findProjectRoot, serverForFile, } from "./servers.js";
|
|
6
6
|
const DEFAULT_WARM_BUDGET_MS = 3000;
|
|
7
7
|
const DEFAULT_FIRST_BUDGET_MS = 8000;
|
|
8
|
+
const DEFAULT_SNAPSHOT_LIMIT = 100;
|
|
8
9
|
const INIT_TIMEOUT_MS = 10_000;
|
|
9
10
|
/**
|
|
10
11
|
* Lazily spawns and pools language servers keyed by (serverId, projectRoot).
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* tool-breaking dependency. A failed spawn/init marks the (server, root) pair
|
|
14
|
-
* broken for the session so a missing binary costs one attempt, not one per
|
|
15
|
-
* edit.
|
|
12
|
+
* The detailed outcome path preserves confidence and failure evidence while the
|
|
13
|
+
* compatibility wrapper keeps edit/write output byte-identical on degradation.
|
|
16
14
|
*/
|
|
17
15
|
export class LspManager {
|
|
18
16
|
cwd;
|
|
19
17
|
catalog;
|
|
20
18
|
warmBudgetMs;
|
|
21
19
|
firstBudgetMs;
|
|
22
|
-
|
|
20
|
+
snapshotLimit;
|
|
21
|
+
/** (serverId\0root) → in-flight or settled client resolution. */
|
|
23
22
|
clients = new Map();
|
|
24
23
|
/** Keys that have completed at least one diagnostics pass (warm). */
|
|
25
24
|
warmKeys = new Set();
|
|
25
|
+
latestOutcomes = new Map();
|
|
26
26
|
shutDown = false;
|
|
27
27
|
constructor(cwd, options) {
|
|
28
28
|
this.cwd = cwd;
|
|
29
29
|
this.catalog = options?.catalog ?? LSP_SERVER_CATALOG;
|
|
30
30
|
this.warmBudgetMs = options?.warmBudgetMs ?? DEFAULT_WARM_BUDGET_MS;
|
|
31
31
|
this.firstBudgetMs = options?.firstBudgetMs ?? DEFAULT_FIRST_BUDGET_MS;
|
|
32
|
+
this.snapshotLimit = Math.max(1, options?.snapshotLimit ?? DEFAULT_SNAPSHOT_LIMIT);
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* unsupported, or anything at all goes wrong (silent graceful degradation).
|
|
35
|
+
* Compatibility surface used by edit/write tools. Diagnostics remain visible;
|
|
36
|
+
* every clean/degraded outcome remains the exact historical empty string.
|
|
37
37
|
*/
|
|
38
38
|
async diagnosticsAfterWrite(filePath, content) {
|
|
39
|
+
const outcome = await this.diagnosticsAfterWriteDetailed(filePath, content);
|
|
40
|
+
return outcome.kind === "diagnostics" ? outcome.formatted : "";
|
|
41
|
+
}
|
|
42
|
+
/** Collect diagnostics with explicit confidence/failure evidence. */
|
|
43
|
+
async diagnosticsAfterWriteDetailed(filePath, content) {
|
|
44
|
+
const normalizedFilePath = path.resolve(this.cwd, filePath);
|
|
39
45
|
if (this.shutDown)
|
|
40
|
-
return "";
|
|
46
|
+
return this.record(this.outcome("unavailable", normalizedFilePath));
|
|
41
47
|
try {
|
|
42
|
-
const spec = serverForFile(
|
|
48
|
+
const spec = serverForFile(normalizedFilePath, this.catalog);
|
|
43
49
|
if (!spec)
|
|
44
|
-
return "";
|
|
45
|
-
const root = findProjectRoot(
|
|
50
|
+
return this.record(this.outcome("unsupported", normalizedFilePath));
|
|
51
|
+
const root = findProjectRoot(normalizedFilePath, spec.rootMarkers, this.cwd);
|
|
46
52
|
const key = `${spec.id}\u0000${root}`;
|
|
47
53
|
const budgetMs = this.warmKeys.has(key) ? this.warmBudgetMs : this.firstBudgetMs;
|
|
48
|
-
const work = this.collect(key, spec, root,
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
54
|
+
const work = this.collect(key, spec, root, normalizedFilePath, content, budgetMs);
|
|
55
|
+
// Leave slow initialization/indexing alive to warm the next edit. Record
|
|
56
|
+
// its eventual evidence too, but report this call honestly as timed out.
|
|
57
|
+
const outcome = await withBudget(work, budgetMs, () => this.outcome("timeout", normalizedFilePath));
|
|
58
|
+
if (outcome.kind === "timeout") {
|
|
59
|
+
void work.then((eventual) => this.record(eventual)).catch(() => { });
|
|
60
|
+
}
|
|
61
|
+
return this.record(outcome);
|
|
52
62
|
}
|
|
53
63
|
catch (error) {
|
|
54
|
-
log("WARN", "lsp", `diagnostics failed for ${
|
|
64
|
+
log("WARN", "lsp", `diagnostics failed for ${normalizedFilePath}`, {
|
|
55
65
|
error: error instanceof Error ? error.message : String(error),
|
|
56
66
|
});
|
|
57
|
-
return "";
|
|
67
|
+
return this.record(this.outcome("server_failed", normalizedFilePath));
|
|
58
68
|
}
|
|
59
69
|
}
|
|
70
|
+
/** Latest bounded evidence for one normalized absolute/relative file path. */
|
|
71
|
+
getLatestOutcome(filePath) {
|
|
72
|
+
return this.latestOutcomes.get(path.resolve(this.cwd, filePath));
|
|
73
|
+
}
|
|
74
|
+
/** Newest retained per-file evidence snapshots. */
|
|
75
|
+
getLatestOutcomes() {
|
|
76
|
+
return [...this.latestOutcomes.values()].reverse();
|
|
77
|
+
}
|
|
60
78
|
/** Shut down every pooled server. Safe in process exit handlers. */
|
|
61
79
|
shutdownAll() {
|
|
62
80
|
this.shutDown = true;
|
|
63
81
|
for (const pending of this.clients.values()) {
|
|
64
|
-
void pending
|
|
82
|
+
void pending
|
|
83
|
+
.then((resolution) => {
|
|
84
|
+
if (resolution.status === "ready")
|
|
85
|
+
resolution.client.shutdown();
|
|
86
|
+
})
|
|
87
|
+
.catch(() => { });
|
|
65
88
|
}
|
|
66
89
|
this.clients.clear();
|
|
67
90
|
this.warmKeys.clear();
|
|
68
91
|
}
|
|
92
|
+
outcome(kind, filePath) {
|
|
93
|
+
return { kind, filePath, updatedAt: Date.now() };
|
|
94
|
+
}
|
|
95
|
+
record(outcome) {
|
|
96
|
+
this.latestOutcomes.delete(outcome.filePath);
|
|
97
|
+
this.latestOutcomes.set(outcome.filePath, outcome);
|
|
98
|
+
while (this.latestOutcomes.size > this.snapshotLimit) {
|
|
99
|
+
const oldest = this.latestOutcomes.keys().next().value;
|
|
100
|
+
if (oldest === undefined)
|
|
101
|
+
break;
|
|
102
|
+
this.latestOutcomes.delete(oldest);
|
|
103
|
+
}
|
|
104
|
+
return outcome;
|
|
105
|
+
}
|
|
69
106
|
async collect(key, spec, root, filePath, content, budgetMs) {
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
return
|
|
107
|
+
const resolution = await this.ensureClient(key, spec, root);
|
|
108
|
+
if (resolution.status !== "ready")
|
|
109
|
+
return this.outcome(resolution.status, filePath);
|
|
110
|
+
const { client } = resolution;
|
|
73
111
|
if (!client.isAlive) {
|
|
74
|
-
|
|
75
|
-
this.clients.set(key, Promise.resolve(null));
|
|
112
|
+
this.clients.set(key, Promise.resolve({ status: "server_failed" }));
|
|
76
113
|
log("WARN", "lsp", `${spec.id} server died`, { root });
|
|
77
|
-
return "";
|
|
114
|
+
return this.outcome("server_failed", filePath);
|
|
78
115
|
}
|
|
79
|
-
const uri = client.syncDocument(
|
|
116
|
+
const uri = client.syncDocument(filePath, content);
|
|
80
117
|
const diagnostics = await client.collectDiagnostics(uri, budgetMs);
|
|
81
118
|
this.warmKeys.add(key);
|
|
119
|
+
if (!client.isAlive) {
|
|
120
|
+
this.clients.set(key, Promise.resolve({ status: "server_failed" }));
|
|
121
|
+
return this.outcome("server_failed", filePath);
|
|
122
|
+
}
|
|
82
123
|
if (diagnostics === null)
|
|
83
|
-
return "";
|
|
84
|
-
|
|
85
|
-
|
|
124
|
+
return this.outcome("timeout", filePath);
|
|
125
|
+
if (diagnostics.length > 0) {
|
|
126
|
+
const relPath = path.relative(this.cwd, filePath);
|
|
127
|
+
return {
|
|
128
|
+
kind: "diagnostics",
|
|
129
|
+
filePath,
|
|
130
|
+
updatedAt: Date.now(),
|
|
131
|
+
diagnostics,
|
|
132
|
+
formatted: formatDiagnostics(relPath, diagnostics),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return this.outcome(client.hasActiveProgress ? "low_confidence" : "clean", filePath);
|
|
86
136
|
}
|
|
87
137
|
ensureClient(key, spec, root) {
|
|
88
138
|
const existing = this.clients.get(key);
|
|
@@ -92,34 +142,36 @@ export class LspManager {
|
|
|
92
142
|
const command = spec.resolveCommand(root);
|
|
93
143
|
if (!command) {
|
|
94
144
|
log("INFO", "lsp", `${spec.id} language server not available`, { root });
|
|
95
|
-
return
|
|
145
|
+
return { status: "unavailable" };
|
|
96
146
|
}
|
|
97
147
|
try {
|
|
98
148
|
const startedAt = Date.now();
|
|
99
149
|
const client = new LspClient(spec, root, command);
|
|
100
150
|
await client.initialize(INIT_TIMEOUT_MS);
|
|
151
|
+
if (!client.isAlive)
|
|
152
|
+
return { status: "server_failed" };
|
|
101
153
|
log("INFO", "lsp", `${spec.id} server initialized`, {
|
|
102
154
|
root,
|
|
103
155
|
ms: String(Date.now() - startedAt),
|
|
104
156
|
});
|
|
105
|
-
return client;
|
|
157
|
+
return { status: "ready", client };
|
|
106
158
|
}
|
|
107
159
|
catch (error) {
|
|
108
160
|
log("WARN", "lsp", `${spec.id} server failed to start`, {
|
|
109
161
|
root,
|
|
110
162
|
error: error instanceof Error ? error.message : String(error),
|
|
111
163
|
});
|
|
112
|
-
return
|
|
164
|
+
return { status: "server_failed" };
|
|
113
165
|
}
|
|
114
166
|
})();
|
|
115
167
|
this.clients.set(key, pending);
|
|
116
168
|
return pending;
|
|
117
169
|
}
|
|
118
170
|
}
|
|
119
|
-
/** Race
|
|
120
|
-
function withBudget(work, budgetMs) {
|
|
171
|
+
/** Race work against a hard budget while allowing it to settle in background. */
|
|
172
|
+
function withBudget(work, budgetMs, onTimeout) {
|
|
121
173
|
return new Promise((resolve) => {
|
|
122
|
-
const timer = setTimeout(() => resolve(
|
|
174
|
+
const timer = setTimeout(() => resolve(onTimeout()), budgetMs);
|
|
123
175
|
timer.unref();
|
|
124
176
|
work
|
|
125
177
|
.then((result) => {
|
|
@@ -128,7 +180,7 @@ function withBudget(work, budgetMs) {
|
|
|
128
180
|
})
|
|
129
181
|
.catch(() => {
|
|
130
182
|
clearTimeout(timer);
|
|
131
|
-
resolve(
|
|
183
|
+
resolve(onTimeout());
|
|
132
184
|
});
|
|
133
185
|
});
|
|
134
186
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/core/lsp/manager.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/core/lsp/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -43,6 +43,10 @@ describe("LspManager", () => {
|
|
|
43
43
|
const result = await manager.diagnosticsAfterWrite(filePath, "ok line\nhas ERROR here\n");
|
|
44
44
|
expect(result).toContain("Diagnostics in broken.fake");
|
|
45
45
|
expect(result).toContain("L2:5 fake error on line 2 (fake)");
|
|
46
|
+
expect(manager.getLatestOutcome(filePath)).toMatchObject({
|
|
47
|
+
kind: "diagnostics",
|
|
48
|
+
filePath: path.resolve(filePath),
|
|
49
|
+
});
|
|
46
50
|
});
|
|
47
51
|
it("returns empty string once a follow-up edit fixes the file", async () => {
|
|
48
52
|
const manager = makeManager(fakeSpec());
|
|
@@ -54,16 +58,42 @@ describe("LspManager", () => {
|
|
|
54
58
|
const rebroken = await manager.diagnosticsAfterWrite(filePath, "fine\nERROR again\n");
|
|
55
59
|
expect(rebroken).toContain("fake error on line 2");
|
|
56
60
|
});
|
|
61
|
+
it("records a high-confidence clean outcome", async () => {
|
|
62
|
+
const manager = makeManager(fakeSpec());
|
|
63
|
+
const filePath = path.join(tmpDir, "clean.fake");
|
|
64
|
+
const outcome = await manager.diagnosticsAfterWriteDetailed(filePath, "all good\n");
|
|
65
|
+
expect(outcome).toMatchObject({ kind: "clean", filePath: path.resolve(filePath) });
|
|
66
|
+
});
|
|
67
|
+
it("records low confidence for an empty result while indexing is active", async () => {
|
|
68
|
+
const manager = makeManager(fakeSpec(["--progress"]));
|
|
69
|
+
const filePath = path.join(tmpDir, "indexing.fake");
|
|
70
|
+
const outcome = await manager.diagnosticsAfterWriteDetailed(filePath, "all good\n");
|
|
71
|
+
expect(outcome.kind).toBe("low_confidence");
|
|
72
|
+
expect(await manager.diagnosticsAfterWrite(filePath, "still good\n")).toBe("");
|
|
73
|
+
});
|
|
74
|
+
it("keeps found errors high confidence while indexing is active", async () => {
|
|
75
|
+
const manager = makeManager(fakeSpec(["--progress"]));
|
|
76
|
+
const filePath = path.join(tmpDir, "indexing-error.fake");
|
|
77
|
+
const outcome = await manager.diagnosticsAfterWriteDetailed(filePath, "ERROR\n");
|
|
78
|
+
expect(outcome.kind).toBe("diagnostics");
|
|
79
|
+
});
|
|
80
|
+
it("records clean after indexing progress ends", async () => {
|
|
81
|
+
const manager = makeManager(fakeSpec(["--progress-end"]));
|
|
82
|
+
const outcome = await manager.diagnosticsAfterWriteDetailed(path.join(tmpDir, "indexed.fake"), "all good\n");
|
|
83
|
+
expect(outcome.kind).toBe("clean");
|
|
84
|
+
});
|
|
57
85
|
it("works with pull-diagnostics servers", async () => {
|
|
58
86
|
const manager = makeManager(fakeSpec(["--pull"]));
|
|
59
87
|
const filePath = path.join(tmpDir, "pull.fake");
|
|
60
88
|
const result = await manager.diagnosticsAfterWrite(filePath, "ERROR\n");
|
|
61
89
|
expect(result).toContain("fake error on line 1");
|
|
62
90
|
});
|
|
63
|
-
it("returns empty string
|
|
91
|
+
it("returns empty string and records unsupported extensions without spawning", async () => {
|
|
64
92
|
const manager = makeManager(fakeSpec());
|
|
65
|
-
const
|
|
93
|
+
const filePath = path.join(tmpDir, "readme.md");
|
|
94
|
+
const result = await manager.diagnosticsAfterWrite(filePath, "# hi");
|
|
66
95
|
expect(result).toBe("");
|
|
96
|
+
expect(manager.getLatestOutcome(filePath)?.kind).toBe("unsupported");
|
|
67
97
|
});
|
|
68
98
|
it("returns empty string when the time budget is exceeded", async () => {
|
|
69
99
|
const manager = makeManager(fakeSpec(["--delay-ms=2000"]), { warm: 300, first: 300 });
|
|
@@ -72,6 +102,7 @@ describe("LspManager", () => {
|
|
|
72
102
|
const result = await manager.diagnosticsAfterWrite(filePath, "ERROR\n");
|
|
73
103
|
expect(result).toBe("");
|
|
74
104
|
expect(Date.now() - started).toBeLessThan(1500);
|
|
105
|
+
expect(manager.getLatestOutcome(filePath)?.kind).toBe("timeout");
|
|
75
106
|
});
|
|
76
107
|
it("marks a server broken after spawn failure and never retries", async () => {
|
|
77
108
|
let resolveCalls = 0;
|
|
@@ -87,11 +118,32 @@ describe("LspManager", () => {
|
|
|
87
118
|
expect(await manager.diagnosticsAfterWrite(filePath, "ERROR\n")).toBe("");
|
|
88
119
|
expect(resolveCalls).toBe(1);
|
|
89
120
|
});
|
|
90
|
-
it("returns empty string when no server command resolves", async () => {
|
|
121
|
+
it("returns empty string and records unavailable when no server command resolves", async () => {
|
|
91
122
|
const spec = fakeSpec([], { resolveCommand: () => null });
|
|
92
123
|
const manager = makeManager(spec);
|
|
93
|
-
const
|
|
124
|
+
const filePath = path.join(tmpDir, "a.fake");
|
|
125
|
+
const result = await manager.diagnosticsAfterWrite(filePath, "ERROR\n");
|
|
94
126
|
expect(result).toBe("");
|
|
127
|
+
expect(manager.getLatestOutcome(filePath)?.kind).toBe("unavailable");
|
|
128
|
+
});
|
|
129
|
+
it("records initialization failure separately", async () => {
|
|
130
|
+
const manager = makeManager(fakeSpec(["--init-error"]));
|
|
131
|
+
const outcome = await manager.diagnosticsAfterWriteDetailed(path.join(tmpDir, "init-failed.fake"), "ERROR\n");
|
|
132
|
+
expect(outcome.kind).toBe("server_failed");
|
|
133
|
+
});
|
|
134
|
+
it("records a post-initialization server crash", async () => {
|
|
135
|
+
const manager = makeManager(fakeSpec(["--crash-on-open"]));
|
|
136
|
+
const outcome = await manager.diagnosticsAfterWriteDetailed(path.join(tmpDir, "crashed.fake"), "ERROR\n");
|
|
137
|
+
expect(outcome.kind).toBe("server_failed");
|
|
138
|
+
});
|
|
139
|
+
it("bounds latest per-file outcomes", async () => {
|
|
140
|
+
const manager = new LspManager(tmpDir, { catalog: [fakeSpec()], snapshotLimit: 2 });
|
|
141
|
+
managers.push(manager);
|
|
142
|
+
for (const name of ["one.md", "two.md", "three.md"]) {
|
|
143
|
+
await manager.diagnosticsAfterWriteDetailed(path.join(tmpDir, name), "safe");
|
|
144
|
+
}
|
|
145
|
+
expect(manager.getLatestOutcomes()).toHaveLength(2);
|
|
146
|
+
expect(manager.getLatestOutcome(path.join(tmpDir, "one.md"))).toBeUndefined();
|
|
95
147
|
});
|
|
96
148
|
it("performs the shutdown handshake on shutdownAll", async () => {
|
|
97
149
|
const shutdownFile = path.join(tmpDir, "shutdown-marker");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.test.js","sourceRoot":"","sources":["../../../src/core/lsp/manager.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAC1B,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAC5C,8CAA8C,CAC/C,CAAC;AAEF,SAAS,QAAQ,CAAC,aAAuB,EAAE,EAAE,SAAkC;IAC7E,OAAO;QACL,EAAE,EAAE,MAAM;QACV,UAAU,EAAE,CAAC,OAAO,CAAC;QACrB,WAAW,EAAE,CAAC,gBAAgB,CAAC;QAC/B,aAAa,EAAE,GAAG,EAAE,CAAC,MAAM;QAC3B,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC;QACrF,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,IAAI,MAAc,CAAC;IACnB,IAAI,QAAsB,CAAC;IAE3B,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;QACvE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,IAAI,CAAC,CAAC;QAC9D,QAAQ,GAAG,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,KAAK,MAAM,OAAO,IAAI,QAAQ;YAAE,OAAO,CAAC,WAAW,EAAE,CAAC;QACtD,MAAM,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,SAAS,WAAW,CAClB,IAAmB,EACnB,OAA2C;QAE3C,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,MAAM,EAAE;YACrC,OAAO,EAAE,CAAC,IAAI,CAAC;YACf,YAAY,EAAE,OAAO,EAAE,IAAI,IAAI,IAAI;YACnC,aAAa,EAAE,OAAO,EAAE,KAAK,IAAI,IAAI;SACtC,CAAC,CAAC;QACH,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QAElD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,qBAAqB,CAAC,QAAQ,EAAE,2BAA2B,CAAC,CAAC;QAE1F,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,kCAAkC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"manager.test.js","sourceRoot":"","sources":["../../../src/core/lsp/manager.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type RunState = "idle" | "running" | "cancelling";
|
|
2
|
+
export type CancelResult = {
|
|
3
|
+
status: "cancelled";
|
|
4
|
+
generation: number;
|
|
5
|
+
} | {
|
|
6
|
+
status: "idle";
|
|
7
|
+
generation: number;
|
|
8
|
+
} | {
|
|
9
|
+
status: "failed";
|
|
10
|
+
generation: number;
|
|
11
|
+
reason: "timeout";
|
|
12
|
+
};
|
|
13
|
+
export interface RunLease {
|
|
14
|
+
generation: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class RunBusyError extends Error {
|
|
17
|
+
constructor(state: RunState);
|
|
18
|
+
}
|
|
19
|
+
/** Generation-safe ownership and acknowledged bounded cancellation. */
|
|
20
|
+
export declare class RunLifecycle {
|
|
21
|
+
private readonly onStateChange?;
|
|
22
|
+
private currentState;
|
|
23
|
+
private nextGeneration;
|
|
24
|
+
private active?;
|
|
25
|
+
constructor(onStateChange?: ((state: RunState) => void) | undefined);
|
|
26
|
+
get state(): RunState;
|
|
27
|
+
get running(): boolean;
|
|
28
|
+
get generation(): number;
|
|
29
|
+
begin(abort: () => void): RunLease;
|
|
30
|
+
isCancellationRequested(generation: number): boolean;
|
|
31
|
+
/** Settle only the matching owner; stale generations cannot release a new run. */
|
|
32
|
+
settle(generation: number): {
|
|
33
|
+
settled: boolean;
|
|
34
|
+
cancelled: boolean;
|
|
35
|
+
};
|
|
36
|
+
/** Abort once, then acknowledge only after the owning operation settles. */
|
|
37
|
+
cancel(timeoutMs: number): Promise<CancelResult>;
|
|
38
|
+
private setState;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=run-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-lifecycle.d.ts","sourceRoot":"","sources":["../../src/core/run-lifecycle.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,CAAC;AAEzD,MAAM,MAAM,YAAY,GACpB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAEhE,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAWD,qBAAa,YAAa,SAAQ,KAAK;gBACzB,KAAK,EAAE,QAAQ;CAI5B;AAED,uEAAuE;AACvE,qBAAa,YAAY;IAKX,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAJ3C,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,MAAM,CAAC,CAAY;gBAEE,aAAa,CAAC,GAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,aAAA;IAEtE,IAAI,KAAK,IAAI,QAAQ,CAEpB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,GAAG,QAAQ;IAkBlC,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIpD,kFAAkF;IAClF,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE;IAYpE,4EAA4E;IAC5E,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAgChD,OAAO,CAAC,QAAQ;CAKjB"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export class RunBusyError extends Error {
|
|
2
|
+
constructor(state) {
|
|
3
|
+
super(`A run is already owned (${state}).`);
|
|
4
|
+
this.name = "RunBusyError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
/** Generation-safe ownership and acknowledged bounded cancellation. */
|
|
8
|
+
export class RunLifecycle {
|
|
9
|
+
onStateChange;
|
|
10
|
+
currentState = "idle";
|
|
11
|
+
nextGeneration = 0;
|
|
12
|
+
active;
|
|
13
|
+
constructor(onStateChange) {
|
|
14
|
+
this.onStateChange = onStateChange;
|
|
15
|
+
}
|
|
16
|
+
get state() {
|
|
17
|
+
return this.currentState;
|
|
18
|
+
}
|
|
19
|
+
get running() {
|
|
20
|
+
return this.currentState !== "idle";
|
|
21
|
+
}
|
|
22
|
+
get generation() {
|
|
23
|
+
return this.active?.generation ?? this.nextGeneration;
|
|
24
|
+
}
|
|
25
|
+
begin(abort) {
|
|
26
|
+
if (this.active)
|
|
27
|
+
throw new RunBusyError(this.currentState);
|
|
28
|
+
let resolveSettlement;
|
|
29
|
+
const settlement = new Promise((resolve) => {
|
|
30
|
+
resolveSettlement = resolve;
|
|
31
|
+
});
|
|
32
|
+
const generation = ++this.nextGeneration;
|
|
33
|
+
this.active = {
|
|
34
|
+
generation,
|
|
35
|
+
abort,
|
|
36
|
+
cancelRequested: false,
|
|
37
|
+
settlement,
|
|
38
|
+
resolveSettlement,
|
|
39
|
+
};
|
|
40
|
+
this.setState("running");
|
|
41
|
+
return { generation };
|
|
42
|
+
}
|
|
43
|
+
isCancellationRequested(generation) {
|
|
44
|
+
return this.active?.generation === generation && this.active.cancelRequested;
|
|
45
|
+
}
|
|
46
|
+
/** Settle only the matching owner; stale generations cannot release a new run. */
|
|
47
|
+
settle(generation) {
|
|
48
|
+
const active = this.active;
|
|
49
|
+
if (!active || active.generation !== generation) {
|
|
50
|
+
return { settled: false, cancelled: false };
|
|
51
|
+
}
|
|
52
|
+
const cancelled = active.cancelRequested;
|
|
53
|
+
this.active = undefined;
|
|
54
|
+
this.setState("idle");
|
|
55
|
+
active.resolveSettlement();
|
|
56
|
+
return { settled: true, cancelled };
|
|
57
|
+
}
|
|
58
|
+
/** Abort once, then acknowledge only after the owning operation settles. */
|
|
59
|
+
cancel(timeoutMs) {
|
|
60
|
+
const active = this.active;
|
|
61
|
+
if (!active) {
|
|
62
|
+
return Promise.resolve({ status: "idle", generation: this.nextGeneration });
|
|
63
|
+
}
|
|
64
|
+
if (active.cancelPromise)
|
|
65
|
+
return active.cancelPromise;
|
|
66
|
+
active.cancelRequested = true;
|
|
67
|
+
this.setState("cancelling");
|
|
68
|
+
try {
|
|
69
|
+
active.abort();
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Settlement remains authoritative even if one abort hook throws.
|
|
73
|
+
}
|
|
74
|
+
active.cancelPromise = new Promise((resolve) => {
|
|
75
|
+
const timer = setTimeout(() => {
|
|
76
|
+
if (this.active?.generation === active.generation)
|
|
77
|
+
this.setState("running");
|
|
78
|
+
resolve({ status: "failed", generation: active.generation, reason: "timeout" });
|
|
79
|
+
}, Math.max(0, timeoutMs));
|
|
80
|
+
timer.unref();
|
|
81
|
+
void active.settlement.then(() => {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
resolve({ status: "cancelled", generation: active.generation });
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
return active.cancelPromise;
|
|
87
|
+
}
|
|
88
|
+
setState(state) {
|
|
89
|
+
if (this.currentState === state)
|
|
90
|
+
return;
|
|
91
|
+
this.currentState = state;
|
|
92
|
+
this.onStateChange?.(state);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=run-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-lifecycle.js","sourceRoot":"","sources":["../../src/core/run-lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-lifecycle.test.d.ts","sourceRoot":"","sources":["../../src/core/run-lifecycle.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { RunBusyError, RunLifecycle } from "./run-lifecycle.js";
|
|
3
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4
|
+
describe("RunLifecycle", () => {
|
|
5
|
+
it("waits for provider-backed ownership to settle before acknowledging cancel", async () => {
|
|
6
|
+
const abort = vi.fn();
|
|
7
|
+
const lifecycle = new RunLifecycle();
|
|
8
|
+
const lease = lifecycle.begin(abort);
|
|
9
|
+
let acknowledged = false;
|
|
10
|
+
const cancellation = lifecycle.cancel(1000).then((result) => {
|
|
11
|
+
acknowledged = true;
|
|
12
|
+
return result;
|
|
13
|
+
});
|
|
14
|
+
await Promise.resolve();
|
|
15
|
+
expect(abort).toHaveBeenCalledTimes(1);
|
|
16
|
+
expect(acknowledged).toBe(false);
|
|
17
|
+
expect(lifecycle.state).toBe("cancelling");
|
|
18
|
+
expect(lifecycle.settle(lease.generation)).toEqual({ settled: true, cancelled: true });
|
|
19
|
+
await expect(cancellation).resolves.toEqual({
|
|
20
|
+
status: "cancelled",
|
|
21
|
+
generation: lease.generation,
|
|
22
|
+
});
|
|
23
|
+
expect(lifecycle.state).toBe("idle");
|
|
24
|
+
});
|
|
25
|
+
it("shares duplicate cancellation and aborts once", async () => {
|
|
26
|
+
const abort = vi.fn();
|
|
27
|
+
const lifecycle = new RunLifecycle();
|
|
28
|
+
const lease = lifecycle.begin(abort);
|
|
29
|
+
const first = lifecycle.cancel(1000);
|
|
30
|
+
const second = lifecycle.cancel(1000);
|
|
31
|
+
expect(second).toBe(first);
|
|
32
|
+
expect(abort).toHaveBeenCalledTimes(1);
|
|
33
|
+
lifecycle.settle(lease.generation);
|
|
34
|
+
await expect(first).resolves.toMatchObject({ status: "cancelled" });
|
|
35
|
+
});
|
|
36
|
+
it("times out visibly, retains ownership, blocks replacement, then recovers on settlement", async () => {
|
|
37
|
+
const lifecycle = new RunLifecycle();
|
|
38
|
+
const lease = lifecycle.begin(() => { });
|
|
39
|
+
await expect(lifecycle.cancel(10)).resolves.toEqual({
|
|
40
|
+
status: "failed",
|
|
41
|
+
generation: lease.generation,
|
|
42
|
+
reason: "timeout",
|
|
43
|
+
});
|
|
44
|
+
expect(lifecycle.state).toBe("running");
|
|
45
|
+
expect(() => lifecycle.begin(() => { })).toThrow(RunBusyError);
|
|
46
|
+
lifecycle.settle(lease.generation);
|
|
47
|
+
expect(lifecycle.state).toBe("idle");
|
|
48
|
+
expect(() => lifecycle.begin(() => { })).not.toThrow();
|
|
49
|
+
});
|
|
50
|
+
it("ignores stale generation settlement", async () => {
|
|
51
|
+
const lifecycle = new RunLifecycle();
|
|
52
|
+
const first = lifecycle.begin(() => { });
|
|
53
|
+
expect(lifecycle.settle(first.generation + 1).settled).toBe(false);
|
|
54
|
+
expect(lifecycle.state).toBe("running");
|
|
55
|
+
lifecycle.settle(first.generation);
|
|
56
|
+
const second = lifecycle.begin(() => { });
|
|
57
|
+
expect(second.generation).toBeGreaterThan(first.generation);
|
|
58
|
+
expect(lifecycle.settle(first.generation).settled).toBe(false);
|
|
59
|
+
expect(lifecycle.state).toBe("running");
|
|
60
|
+
lifecycle.settle(second.generation);
|
|
61
|
+
await delay(0);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=run-lifecycle.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-lifecycle.test.js","sourceRoot":"","sources":["../../src/core/run-lifecycle.test.ts"],"names":[],"mappings":"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"}
|