@sayknow-cli/coding-agent 0.5.2 → 0.5.8
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/CHANGELOG.md +66 -16
- package/dist/types/cli/update-cli.d.ts +8 -1
- package/dist/types/config/atomic-yaml-patch.d.ts +20 -1
- package/dist/types/config/keybindings.d.ts +10 -0
- package/dist/types/config/model-registry.d.ts +1 -1
- package/dist/types/config/models-config-schema.d.ts +4 -0
- package/dist/types/config/settings-schema.d.ts +73 -0
- package/dist/types/dap/client.d.ts +1 -0
- package/dist/types/lsp/client.d.ts +5 -0
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
- package/dist/types/modes/interactive-mode.d.ts +13 -1
- package/dist/types/modes/types.d.ts +7 -1
- package/dist/types/runtime-mcp/transports/stdio.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +8 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +3 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +2 -0
- package/dist/types/session/session-manager.d.ts +9 -0
- package/dist/types/session/session-storage.d.ts +17 -0
- package/dist/types/session-import/claude.d.ts +26 -0
- package/dist/types/session-import/codex.d.ts +22 -0
- package/dist/types/session-import/command.d.ts +20 -0
- package/dist/types/session-import/detect.d.ts +22 -0
- package/dist/types/session-import/index.d.ts +7 -0
- package/dist/types/session-import/redact.d.ts +23 -0
- package/dist/types/session-import/service.d.ts +28 -0
- package/dist/types/session-import/types.d.ts +125 -0
- package/dist/types/skc-runtime/launch-worktree.d.ts +17 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +4 -0
- package/dist/types/slash-commands/types.d.ts +5 -0
- package/dist/types/system-prompt.d.ts +5 -0
- package/dist/types/tools/ask.d.ts +5 -0
- package/dist/types/utils/mupdf-wasm.d.ts +1 -0
- package/package.json +13 -7
- package/scripts/compile-args.ts +8 -4
- package/scripts/generate-sdk-operation-inventory.ts +5 -6
- package/src/cli/config-cli.ts +23 -8
- package/src/cli/update-cli.ts +265 -21
- package/src/config/atomic-yaml-patch.ts +167 -30
- package/src/config/keybindings.ts +10 -0
- package/src/config/model-profiles.ts +84 -16
- package/src/config/model-registry.ts +60 -38
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +79 -0
- package/src/config/settings.ts +13 -2
- package/src/dap/client.ts +78 -30
- package/src/internal-urls/docs-index.generated.ts +5 -4
- package/src/lsp/client.ts +17 -29
- package/src/modes/action-registry.ts +2 -0
- package/src/modes/components/welcome.ts +5 -0
- package/src/modes/controllers/goal-mode-controller.ts +7 -2
- package/src/modes/controllers/input-controller.ts +71 -6
- package/src/modes/controllers/plan-mode-controller.ts +8 -1
- package/src/modes/controllers/runtime-mcp-command-controller.ts +14 -0
- package/src/modes/controllers/selector-controller.ts +47 -19
- package/src/modes/interactive-mode.ts +54 -4
- package/src/modes/types.ts +5 -1
- package/src/prompts/system/system-prompt.md +8 -0
- package/src/runtime-mcp/transports/stdio.ts +96 -33
- package/src/sdk/broker/lifecycle.ts +6 -5
- package/src/sdk/protocol/operation-inventory.generated.json +33 -0
- package/src/sdk/session.ts +27 -18
- package/src/session/agent-session.ts +155 -15
- package/src/session/internal/managed-session-scope.ts +41 -6
- package/src/session/internal/managed-session-storage.ts +14 -0
- package/src/session/session-manager.ts +113 -11
- package/src/session/session-storage.ts +70 -0
- package/src/session-import/claude.ts +382 -0
- package/src/session-import/codex.ts +458 -0
- package/src/session-import/command.ts +61 -0
- package/src/session-import/detect.ts +133 -0
- package/src/session-import/index.ts +27 -0
- package/src/session-import/redact.ts +125 -0
- package/src/session-import/service.ts +749 -0
- package/src/session-import/types.ts +163 -0
- package/src/skc-runtime/launch-worktree.ts +277 -4
- package/src/slash-commands/acp-builtins.ts +5 -1
- package/src/slash-commands/builtin-registry.ts +100 -1
- package/src/slash-commands/types.ts +5 -0
- package/src/system-prompt.ts +7 -0
- package/src/tools/ask.ts +5 -0
- package/src/tools/fetch.ts +7 -2
- package/src/utils/markit.ts +24 -2
- package/src/utils/mupdf-wasm.ts +26 -0
- package/src/export/html/template.css +0 -1060
- package/src/export/html/template.html +0 -47
- package/src/export/html/template.js +0 -2348
- package/vendor/insane-search/engine/tests/test_hardening.py +0 -57
- package/vendor/insane-search/engine/tests/test_smoke.py +0 -152
- package/vendor/insane-search/engine/tests/test_u1.py +0 -200
- package/vendor/insane-search/engine/tests/test_u4.py +0 -131
- package/vendor/insane-search/engine/tests/test_u5.py +0 -163
- package/vendor/insane-search/engine/tests/test_u7.py +0 -124
- package/vendor/insane-search/engine/tests/test_u8.py +0 -216
|
@@ -581,6 +581,25 @@ export const SETTINGS_SCHEMA = {
|
|
|
581
581
|
},
|
|
582
582
|
},
|
|
583
583
|
|
|
584
|
+
reasoningLanguage: {
|
|
585
|
+
type: "enum",
|
|
586
|
+
values: ["off", "english"] as const,
|
|
587
|
+
default: "off",
|
|
588
|
+
ui: {
|
|
589
|
+
tab: "appearance",
|
|
590
|
+
label: "Reasoning Language",
|
|
591
|
+
description: "Reason through technical work in English while answering in your own language",
|
|
592
|
+
options: [
|
|
593
|
+
{ value: "off", label: "Off", description: "Do not add reasoning-language guidance" },
|
|
594
|
+
{
|
|
595
|
+
value: "english",
|
|
596
|
+
label: "English",
|
|
597
|
+
description: "Reason in English and answer in the language you used",
|
|
598
|
+
},
|
|
599
|
+
],
|
|
600
|
+
},
|
|
601
|
+
},
|
|
602
|
+
|
|
584
603
|
colorBlindMode: {
|
|
585
604
|
type: "boolean",
|
|
586
605
|
default: false,
|
|
@@ -1417,6 +1436,15 @@ export const SETTINGS_SCHEMA = {
|
|
|
1417
1436
|
],
|
|
1418
1437
|
},
|
|
1419
1438
|
},
|
|
1439
|
+
"startup.skipLogoAnimation": {
|
|
1440
|
+
type: "boolean",
|
|
1441
|
+
default: false,
|
|
1442
|
+
ui: {
|
|
1443
|
+
tab: "interaction",
|
|
1444
|
+
label: "Skip Logo Animation",
|
|
1445
|
+
description: "Show the final welcome logo immediately",
|
|
1446
|
+
},
|
|
1447
|
+
},
|
|
1420
1448
|
|
|
1421
1449
|
"startup.checkUpdate": {
|
|
1422
1450
|
type: "boolean",
|
|
@@ -1616,6 +1644,57 @@ export const SETTINGS_SCHEMA = {
|
|
|
1616
1644
|
},
|
|
1617
1645
|
},
|
|
1618
1646
|
|
|
1647
|
+
// Adaptive compaction. Opt-in; the fixed thresholds above keep precedence.
|
|
1648
|
+
"compaction.adaptive.enabled": {
|
|
1649
|
+
type: "boolean",
|
|
1650
|
+
default: false,
|
|
1651
|
+
ui: {
|
|
1652
|
+
tab: "context",
|
|
1653
|
+
label: "Adaptive Compaction",
|
|
1654
|
+
description: "Adjust compaction frequency by context size and call rate",
|
|
1655
|
+
},
|
|
1656
|
+
},
|
|
1657
|
+
|
|
1658
|
+
"compaction.adaptive.baseThresholdPercent": {
|
|
1659
|
+
type: "number",
|
|
1660
|
+
default: 85,
|
|
1661
|
+
ui: {
|
|
1662
|
+
tab: "context",
|
|
1663
|
+
label: "Adaptive Base Threshold %",
|
|
1664
|
+
description: "Base context percentage used when adaptive compaction is enabled",
|
|
1665
|
+
},
|
|
1666
|
+
},
|
|
1667
|
+
|
|
1668
|
+
"compaction.adaptive.aggression": {
|
|
1669
|
+
type: "number",
|
|
1670
|
+
default: 0.15,
|
|
1671
|
+
ui: {
|
|
1672
|
+
tab: "context",
|
|
1673
|
+
label: "Adaptive Aggression",
|
|
1674
|
+
description: "How strongly call rate lowers the compaction threshold; use 0 to 1",
|
|
1675
|
+
},
|
|
1676
|
+
},
|
|
1677
|
+
|
|
1678
|
+
"compaction.adaptive.turnWindow": {
|
|
1679
|
+
type: "number",
|
|
1680
|
+
default: 15,
|
|
1681
|
+
ui: {
|
|
1682
|
+
tab: "context",
|
|
1683
|
+
label: "Call-Rate Window",
|
|
1684
|
+
description: "Minutes of recent calls considered by adaptive compaction",
|
|
1685
|
+
},
|
|
1686
|
+
},
|
|
1687
|
+
|
|
1688
|
+
"compaction.adaptive.minThresholdPercent": {
|
|
1689
|
+
type: "number",
|
|
1690
|
+
default: 50,
|
|
1691
|
+
ui: {
|
|
1692
|
+
tab: "context",
|
|
1693
|
+
label: "Adaptive Floor %",
|
|
1694
|
+
description: "Lowest context percentage adaptive compaction may lower the threshold to",
|
|
1695
|
+
},
|
|
1696
|
+
},
|
|
1697
|
+
|
|
1619
1698
|
"compaction.handoffSaveToDisk": {
|
|
1620
1699
|
type: "boolean",
|
|
1621
1700
|
default: false,
|
package/src/config/settings.ts
CHANGED
|
@@ -39,6 +39,7 @@ import { AgentStorage } from "../session/agent-storage";
|
|
|
39
39
|
import { type EditMode, normalizeEditMode } from "../utils/edit-mode";
|
|
40
40
|
import {
|
|
41
41
|
type AtomicYamlPatch,
|
|
42
|
+
AtomicYamlRetargetError,
|
|
42
43
|
applyAtomicYamlPatches,
|
|
43
44
|
applyAtomicYamlPatchesWithCurrent,
|
|
44
45
|
atomicYamlPathHash,
|
|
@@ -830,22 +831,28 @@ export class Settings implements NotificationSettingsReader {
|
|
|
830
831
|
if (this.#modified.size > 0 && !this.#pendingSaveSlot) this.#queueSave();
|
|
831
832
|
this.#releasePendingSaveSlot();
|
|
832
833
|
const observedSave = this.#savePromise;
|
|
834
|
+
let saveError: unknown;
|
|
833
835
|
try {
|
|
834
836
|
await observedSave;
|
|
835
|
-
} catch {
|
|
837
|
+
} catch (error) {
|
|
836
838
|
// Historical flush() behavior logs background failures but does not reject.
|
|
839
|
+
saveError = error;
|
|
837
840
|
}
|
|
841
|
+
if (saveError instanceof AtomicYamlRetargetError) return;
|
|
838
842
|
// A failed predecessor may settle just before a new mutation observes its
|
|
839
843
|
// still-reserved slot. Explicit flush owns one fresh attempt for remaining
|
|
840
844
|
// dirty patches instead of leaving them stranded or retrying forever.
|
|
841
845
|
if (this.#modified.size > 0 && this.#savePromise === observedSave) {
|
|
842
846
|
if (!this.#pendingSaveSlot) this.#queueSave();
|
|
843
847
|
this.#releasePendingSaveSlot();
|
|
848
|
+
let retryError: unknown;
|
|
844
849
|
try {
|
|
845
850
|
await this.#savePromise;
|
|
846
|
-
} catch {
|
|
851
|
+
} catch (error) {
|
|
847
852
|
// Keep dirty state for a later explicit flush or mutation.
|
|
853
|
+
retryError = error;
|
|
848
854
|
}
|
|
855
|
+
if (retryError instanceof AtomicYamlRetargetError) return;
|
|
849
856
|
}
|
|
850
857
|
await this.#refreshDurableSettings();
|
|
851
858
|
if (this.#modified.size > 0 && !this.#pendingSaveSlot) {
|
|
@@ -871,6 +878,7 @@ export class Settings implements NotificationSettingsReader {
|
|
|
871
878
|
saveError = error;
|
|
872
879
|
}
|
|
873
880
|
await this.#refreshDurableSettings();
|
|
881
|
+
if (saveError instanceof AtomicYamlRetargetError) throw saveError;
|
|
874
882
|
if (this.#modified.size > 0 && !this.#pendingSaveSlot) {
|
|
875
883
|
this.#queueSave();
|
|
876
884
|
this.#releasePendingSaveSlot();
|
|
@@ -1721,6 +1729,9 @@ export class Settings implements NotificationSettingsReader {
|
|
|
1721
1729
|
logger.warn("Settings: refresh after background save failure failed", { error: String(refreshError) });
|
|
1722
1730
|
}
|
|
1723
1731
|
throw error;
|
|
1732
|
+
})
|
|
1733
|
+
.finally(() => {
|
|
1734
|
+
if (this.#pendingSaveSlot === slot) this.#pendingSaveSlot = undefined;
|
|
1724
1735
|
});
|
|
1725
1736
|
this.#savePromise = save;
|
|
1726
1737
|
void save.catch(() => {});
|
package/src/dap/client.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
|
-
import { logger } from "@sayknow-cli/utils";
|
|
3
|
+
import { isKnownSinkPeerClosedError, logger } from "@sayknow-cli/utils";
|
|
4
4
|
import { formatCrashDiagnosticNotice, writeCrashReport } from "../debug/crash-diagnostics";
|
|
5
5
|
import { NON_INTERACTIVE_ENV } from "../exec/non-interactive-env";
|
|
6
6
|
import { type OwnedProcess, spawnOwnedProcess } from "../runtime/process-lifecycle";
|
|
@@ -62,9 +62,9 @@ function parseMessage(
|
|
|
62
62
|
|
|
63
63
|
async function writeMessage(sink: DapWriteSink, message: DapRequestMessage | DapResponseMessage): Promise<void> {
|
|
64
64
|
const content = JSON.stringify(message);
|
|
65
|
-
|
|
66
|
-
sink.write(
|
|
67
|
-
await sink.flush();
|
|
65
|
+
const frame = `Content-Length: ${Buffer.byteLength(content, "utf-8")}\r\n\r\n${content}`;
|
|
66
|
+
await Promise.resolve(sink.write(frame));
|
|
67
|
+
await Promise.resolve(sink.flush());
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
function toErrorMessage(value: unknown): string {
|
|
@@ -98,6 +98,9 @@ export class DapClient {
|
|
|
98
98
|
#messageBuffer = Buffer.alloc(0);
|
|
99
99
|
#isReading = false;
|
|
100
100
|
#disposed = false;
|
|
101
|
+
#terminalError: Error | undefined;
|
|
102
|
+
#disposePromise: Promise<void> | undefined;
|
|
103
|
+
#writeQueue: Promise<void> = Promise.resolve();
|
|
101
104
|
#lastActivity = Date.now();
|
|
102
105
|
#capabilities?: DapCapabilities;
|
|
103
106
|
#eventHandlers = new Map<string, Set<DapEventHandler>>();
|
|
@@ -119,6 +122,18 @@ export class DapClient {
|
|
|
119
122
|
this.#socket = options?.socket;
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
#queueWriteMessage(message: DapRequestMessage | DapResponseMessage): Promise<void> {
|
|
126
|
+
const write = this.#writeQueue
|
|
127
|
+
.catch(() => {})
|
|
128
|
+
.then(() => {
|
|
129
|
+
if (this.#terminalError) throw this.#terminalError;
|
|
130
|
+
if (this.#disposed) throw new Error(`DAP adapter ${this.adapter.name} is not running`);
|
|
131
|
+
return writeMessage(this.#writeSink, message);
|
|
132
|
+
});
|
|
133
|
+
this.#writeQueue = write.catch(() => {});
|
|
134
|
+
return write;
|
|
135
|
+
}
|
|
136
|
+
|
|
122
137
|
static async spawn({ adapter, cwd }: DapSpawnOptions): Promise<DapClient> {
|
|
123
138
|
if (adapter.connectMode === "socket") {
|
|
124
139
|
return DapClient.#spawnSocket({ adapter, cwd });
|
|
@@ -142,7 +157,7 @@ export class DapClient {
|
|
|
142
157
|
proc.exited.then(() => {
|
|
143
158
|
client.#handleProcessExit();
|
|
144
159
|
});
|
|
145
|
-
void client
|
|
160
|
+
void client.startMessageReader();
|
|
146
161
|
return client;
|
|
147
162
|
}
|
|
148
163
|
|
|
@@ -199,7 +214,7 @@ export class DapClient {
|
|
|
199
214
|
transport = await connectSocket({ unix: socketPath }, 10_000);
|
|
200
215
|
const client = new DapClient(adapter, cwd, owner, transport);
|
|
201
216
|
proc.exited.then(() => client.#handleProcessExit());
|
|
202
|
-
void client
|
|
217
|
+
void client.startMessageReader();
|
|
203
218
|
return client;
|
|
204
219
|
} catch (err) {
|
|
205
220
|
transport?.socket.end();
|
|
@@ -268,7 +283,7 @@ export class DapClient {
|
|
|
268
283
|
const { readable, writeSink, socket } = wrapBunSocket(rawSocket);
|
|
269
284
|
const client = new DapClient(adapter, cwd, owner, { readable, writeSink, socket });
|
|
270
285
|
proc.exited.then(() => client.#handleProcessExit());
|
|
271
|
-
void client
|
|
286
|
+
void client.startMessageReader();
|
|
272
287
|
return client;
|
|
273
288
|
}
|
|
274
289
|
|
|
@@ -364,6 +379,7 @@ export class DapClient {
|
|
|
364
379
|
signal?: AbortSignal,
|
|
365
380
|
timeoutMs: number = DEFAULT_REQUEST_TIMEOUT_MS,
|
|
366
381
|
): Promise<TBody> {
|
|
382
|
+
if (this.#terminalError) throw this.#terminalError;
|
|
367
383
|
if (signal?.aborted) {
|
|
368
384
|
throw signal.reason instanceof Error ? signal.reason : new ToolAbortError();
|
|
369
385
|
}
|
|
@@ -412,10 +428,11 @@ export class DapClient {
|
|
|
412
428
|
});
|
|
413
429
|
this.#lastActivity = Date.now();
|
|
414
430
|
try {
|
|
415
|
-
await
|
|
431
|
+
await this.#queueWriteMessage(request);
|
|
416
432
|
} catch (error) {
|
|
417
433
|
this.#pendingRequests.delete(requestSeq);
|
|
418
434
|
cleanup();
|
|
435
|
+
if (isKnownSinkPeerClosedError(error)) throw this.#terminalizeTransport(error);
|
|
419
436
|
throw error;
|
|
420
437
|
}
|
|
421
438
|
return promise;
|
|
@@ -431,37 +448,30 @@ export class DapClient {
|
|
|
431
448
|
...(message ? { message } : {}),
|
|
432
449
|
...(body !== undefined ? { body } : {}),
|
|
433
450
|
};
|
|
434
|
-
await
|
|
451
|
+
await this.#queueWriteMessage(response);
|
|
435
452
|
}
|
|
436
453
|
|
|
437
454
|
async dispose(): Promise<void> {
|
|
438
|
-
if (this.#disposed)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
this.#
|
|
443
|
-
} catch {
|
|
444
|
-
/* socket may already be closed */
|
|
445
|
-
}
|
|
446
|
-
try {
|
|
447
|
-
await this.#owner.dispose();
|
|
448
|
-
await this.#owner.awaitExit({ timeoutMs: 1_000 });
|
|
449
|
-
} catch (error) {
|
|
450
|
-
logger.debug("Failed to dispose DAP adapter", {
|
|
451
|
-
adapter: this.adapter.name,
|
|
452
|
-
error: toErrorMessage(error),
|
|
453
|
-
});
|
|
455
|
+
if (!this.#disposed) {
|
|
456
|
+
const error = new Error(`DAP adapter ${this.adapter.name} disposed`);
|
|
457
|
+
this.#disposed = true;
|
|
458
|
+
this.#terminalError = error;
|
|
459
|
+
this.#rejectPendingRequests(error);
|
|
454
460
|
}
|
|
461
|
+
await this.#beginOwnerDisposal();
|
|
455
462
|
}
|
|
456
463
|
|
|
457
|
-
async
|
|
464
|
+
async startMessageReader(): Promise<void> {
|
|
458
465
|
if (this.#isReading) return;
|
|
459
466
|
this.#isReading = true;
|
|
460
467
|
const reader = this.#readable.getReader();
|
|
461
468
|
try {
|
|
462
469
|
while (true) {
|
|
463
470
|
const { done, value } = await reader.read();
|
|
464
|
-
if (done)
|
|
471
|
+
if (done) {
|
|
472
|
+
this.#terminalizeTransport(new Error("DAP readable closed"));
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
465
475
|
const currentBuffer = Buffer.concat([this.#messageBuffer, value]);
|
|
466
476
|
this.#messageBuffer = currentBuffer;
|
|
467
477
|
let workingBuffer = currentBuffer;
|
|
@@ -482,7 +492,7 @@ export class DapClient {
|
|
|
482
492
|
this.#messageBuffer = workingBuffer;
|
|
483
493
|
}
|
|
484
494
|
} catch (error) {
|
|
485
|
-
this.#
|
|
495
|
+
this.#terminalizeTransport(error);
|
|
486
496
|
} finally {
|
|
487
497
|
reader.releaseLock();
|
|
488
498
|
this.#isReading = false;
|
|
@@ -523,9 +533,9 @@ export class DapClient {
|
|
|
523
533
|
try {
|
|
524
534
|
const handler = this.#reverseRequestHandlers.get(message.command);
|
|
525
535
|
if (handler) {
|
|
536
|
+
let body: unknown;
|
|
526
537
|
try {
|
|
527
|
-
|
|
528
|
-
await this.sendResponse(message, true, body);
|
|
538
|
+
body = await handler(message.arguments);
|
|
529
539
|
} catch (error) {
|
|
530
540
|
const errorMessage = toErrorMessage(error);
|
|
531
541
|
await this.sendResponse(
|
|
@@ -539,7 +549,9 @@ export class DapClient {
|
|
|
539
549
|
},
|
|
540
550
|
errorMessage,
|
|
541
551
|
);
|
|
552
|
+
return;
|
|
542
553
|
}
|
|
554
|
+
await this.sendResponse(message, true, body);
|
|
543
555
|
return;
|
|
544
556
|
}
|
|
545
557
|
const errorMessage = `Unsupported DAP request: ${message.command}`;
|
|
@@ -555,6 +567,10 @@ export class DapClient {
|
|
|
555
567
|
errorMessage,
|
|
556
568
|
);
|
|
557
569
|
} catch (error) {
|
|
570
|
+
if (isKnownSinkPeerClosedError(error)) {
|
|
571
|
+
this.#terminalizeTransport(error);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
558
574
|
logger.warn("Failed to answer DAP adapter request", {
|
|
559
575
|
adapter: this.adapter.name,
|
|
560
576
|
command: message.command,
|
|
@@ -586,9 +602,41 @@ export class DapClient {
|
|
|
586
602
|
? `DAP adapter exited (code ${exitCode}): ${stderr}${diagnosticSuffix}`
|
|
587
603
|
: `DAP adapter exited unexpectedly (code ${exitCode})${diagnosticSuffix}`,
|
|
588
604
|
);
|
|
605
|
+
this.#terminalError = error;
|
|
589
606
|
this.#rejectPendingRequests(error);
|
|
590
607
|
}
|
|
591
608
|
|
|
609
|
+
#terminalizeTransport(cause: unknown): Error {
|
|
610
|
+
if (this.#terminalError) return this.#terminalError;
|
|
611
|
+
const error = new Error(`DAP adapter ${this.adapter.name} transport closed`, { cause });
|
|
612
|
+
this.#terminalError = error;
|
|
613
|
+
this.#disposed = true;
|
|
614
|
+
this.#rejectPendingRequests(error);
|
|
615
|
+
void this.#beginOwnerDisposal();
|
|
616
|
+
return error;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
#beginOwnerDisposal(): Promise<void> {
|
|
620
|
+
if (this.#disposePromise) return this.#disposePromise;
|
|
621
|
+
this.#disposePromise = (async () => {
|
|
622
|
+
try {
|
|
623
|
+
this.#socket?.end();
|
|
624
|
+
} catch {
|
|
625
|
+
/* socket may already be closed */
|
|
626
|
+
}
|
|
627
|
+
try {
|
|
628
|
+
await this.#owner.dispose();
|
|
629
|
+
await this.#owner.awaitExit({ timeoutMs: 1_000 });
|
|
630
|
+
} catch (disposeError) {
|
|
631
|
+
logger.debug("Failed to dispose DAP adapter", {
|
|
632
|
+
adapter: this.adapter.name,
|
|
633
|
+
error: toErrorMessage(disposeError),
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
})();
|
|
637
|
+
return this.#disposePromise;
|
|
638
|
+
}
|
|
639
|
+
|
|
592
640
|
#rejectPendingRequests(error: Error): void {
|
|
593
641
|
for (const pending of this.#pendingRequests.values()) {
|
|
594
642
|
pending.reject(error);
|