@omercnet/paseo-omp 0.2.1 → 0.3.0-next.101.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/CHANGELOG.md +26 -0
- package/README.md +25 -13
- package/SUPPORT.md +7 -3
- package/TESTING.md +21 -18
- package/client/composer-pill-settings.tsx +157 -0
- package/client/external-url.ts +15 -0
- package/client/mcp-authorization.tsx +169 -0
- package/client/mcp-popover.tsx +155 -0
- package/client/memory-panel.tsx +8 -3
- package/client/memory-popover.tsx +8 -4
- package/client/omp-config-surface.tsx +189 -29
- package/client/omp-plugin-manager.tsx +302 -131
- package/client/omp-store-picker.tsx +89 -0
- package/client/omp-store-state.ts +45 -0
- package/client/paseo-types.ts +9 -0
- package/client/provider-diagnostics-state.ts +18 -7
- package/client/quota-popover.tsx +8 -3
- package/client/quota-state.ts +16 -7
- package/client/sessions-popover.tsx +8 -3
- package/docs/alpha-release-checklist.md +6 -8
- package/docs/configuration.md +8 -4
- package/docs/core-provider-issue-audit.md +3 -2
- package/docs/images/mcp-authorization-compact.png +0 -0
- package/docs/images/mcp-controls-wide.png +0 -0
- package/docs/images/plugin-manager.png +0 -0
- package/docs/images/workspace-settings.png +0 -0
- package/docs/installation.md +35 -19
- package/index.client.tsx +339 -123
- package/index.server.ts +44 -14
- package/package.json +7 -8
- package/paseo-plugin.json +2 -2
- package/scripts/prepare-dependencies.mjs +24 -0
- package/server/mcp-browser.ts +95 -0
- package/server/memory.ts +2 -2
- package/server/omp-config.ts +16 -7
- package/server/omp-plugins.ts +70 -21
- package/server/omp-settings.ts +232 -24
- package/server/paths.ts +128 -11
- package/server/provider/catalog.ts +3 -4
- package/server/provider/connection.ts +248 -17
- package/server/provider/host-tools.ts +294 -26
- package/server/provider/omp-rpc.ts +499 -72
- package/server/provider/profile-providers.ts +249 -0
- package/server/provider/registration.ts +11 -0
- package/server/provider/security.ts +8 -10
- package/server/provider/session-descriptors.ts +340 -1
- package/server/provider/session.ts +716 -249
- package/server/provider/subsessions.ts +25 -2
- package/server/provider/timeline-projector.ts +104 -44
- package/server/provider-diagnostics.ts +122 -36
- package/server/quota.ts +3 -2
- package/server/sessions.ts +2 -2
- package/shared/composer-pill-settings.ts +28 -0
- package/shared/external-url.ts +21 -0
- package/shared/hub.ts +3 -3
- package/shared/mcp.ts +47 -0
- package/shared/memory.ts +2 -1
- package/shared/omp-config.ts +5 -1
- package/shared/omp-plugins.ts +74 -33
- package/shared/omp-settings.ts +8 -1
- package/shared/omp-store.ts +58 -0
- package/shared/provider-diagnostics.ts +12 -3
- package/shared/quota.ts +2 -1
- package/shared/sessions.ts +2 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { homedir } from "node:os";
|
|
2
3
|
import { isAbsolute } from "node:path";
|
|
3
4
|
import {
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
ProviderInputSchema,
|
|
8
9
|
requireProviderCapabilities,
|
|
9
10
|
} from "@getpaseo/plugin/server/provider";
|
|
11
|
+
import type { OmpBrowserAuthorizationRegistry } from "../mcp-browser";
|
|
10
12
|
import { discoverOmpCatalog } from "./catalog";
|
|
11
13
|
import { normalizeOmpCatalogOptions } from "./config-normalization";
|
|
12
14
|
import type { OmpMcpConnector } from "./host-tools";
|
|
@@ -39,6 +41,7 @@ const SUPPORTED_CAPABILITIES: Readonly<Record<string, true>> = {
|
|
|
39
41
|
"session.subsession": true,
|
|
40
42
|
"session.revert.conversation": true,
|
|
41
43
|
permission: true,
|
|
44
|
+
"timeline.plugin": true,
|
|
42
45
|
};
|
|
43
46
|
const SUPPORTED_INPUTS: Readonly<Record<string, true>> = {
|
|
44
47
|
catalog: true,
|
|
@@ -55,6 +58,7 @@ const MAX_CONNECTION_SESSIONS = 32;
|
|
|
55
58
|
const MAX_ACTIVE_OPERATIONS = 128;
|
|
56
59
|
const MAX_PROVIDER_INPUT_BYTES = 2 * 1024 * 1024;
|
|
57
60
|
const MAX_NESTED_OPTION_BYTES = 256 * 1024;
|
|
61
|
+
const MAX_ENV_ENTRIES = 256;
|
|
58
62
|
const MAX_NATIVE_SESSION_RESERVATIONS = 256;
|
|
59
63
|
|
|
60
64
|
function hasOwnEntries(value: unknown): boolean {
|
|
@@ -65,6 +69,19 @@ function hasOwnEntries(value: unknown): boolean {
|
|
|
65
69
|
return false;
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
function providerOptionsExceedPreflightLimits(value: unknown): boolean {
|
|
73
|
+
if (
|
|
74
|
+
boundedJsonBytes(value, MAX_NESTED_OPTION_BYTES, MAX_ENV_ENTRIES) === Number.POSITIVE_INFINITY
|
|
75
|
+
) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
79
|
+
const unrelatedOptions = Object.fromEntries(
|
|
80
|
+
Object.entries(value).filter(([key]) => key !== "env" && key !== "inheritEnv"),
|
|
81
|
+
);
|
|
82
|
+
return boundedJsonBytes(unrelatedOptions, MAX_NESTED_OPTION_BYTES) === Number.POSITIVE_INFINITY;
|
|
83
|
+
}
|
|
84
|
+
|
|
68
85
|
function preflightProviderInput(input: unknown): void {
|
|
69
86
|
if (!input || typeof input !== "object") throw new OmpPublicError("Invalid provider request");
|
|
70
87
|
const record = input as Record<string, unknown>;
|
|
@@ -79,7 +96,16 @@ function preflightProviderInput(input: unknown): void {
|
|
|
79
96
|
throw new OmpPublicError("Session persistence input is too large");
|
|
80
97
|
}
|
|
81
98
|
const config = record.config as Record<string, unknown> | undefined;
|
|
82
|
-
|
|
99
|
+
if (
|
|
100
|
+
(config?.env !== undefined &&
|
|
101
|
+
boundedJsonBytes(config.env, MAX_NESTED_OPTION_BYTES, MAX_ENV_ENTRIES) ===
|
|
102
|
+
Number.POSITIVE_INFINITY) ||
|
|
103
|
+
(config?.providerOptions !== undefined &&
|
|
104
|
+
providerOptionsExceedPreflightLimits(config.providerOptions))
|
|
105
|
+
) {
|
|
106
|
+
throw new OmpPublicError("Session configuration is too large");
|
|
107
|
+
}
|
|
108
|
+
for (const value of [config?.mcpServers, config?.settings]) {
|
|
83
109
|
if (
|
|
84
110
|
value !== undefined &&
|
|
85
111
|
boundedJsonBytes(value, MAX_NESTED_OPTION_BYTES) === Number.POSITIVE_INFINITY
|
|
@@ -106,13 +132,17 @@ function preflightProviderInput(input: unknown): void {
|
|
|
106
132
|
}
|
|
107
133
|
}
|
|
108
134
|
if (record.type === "catalog" || record.type === "sessions") {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
|
|
135
|
+
if (
|
|
136
|
+
record.providerOptions !== undefined &&
|
|
137
|
+
providerOptionsExceedPreflightLimits(record.providerOptions)
|
|
138
|
+
) {
|
|
139
|
+
throw new OmpPublicError("Provider configuration is too large");
|
|
140
|
+
}
|
|
141
|
+
if (
|
|
142
|
+
record.settings !== undefined &&
|
|
143
|
+
boundedJsonBytes(record.settings, MAX_NESTED_OPTION_BYTES) === Number.POSITIVE_INFINITY
|
|
144
|
+
) {
|
|
145
|
+
throw new OmpPublicError("Provider configuration is too large");
|
|
116
146
|
}
|
|
117
147
|
}
|
|
118
148
|
if (record.type === "session.prompt") {
|
|
@@ -264,8 +294,164 @@ function validateInputEnvelope(input: unknown): asserts input is ProviderInput {
|
|
|
264
294
|
}
|
|
265
295
|
}
|
|
266
296
|
|
|
267
|
-
|
|
268
|
-
|
|
297
|
+
export interface OmpConnectionDiagnostic {
|
|
298
|
+
diagnosticId: string;
|
|
299
|
+
operation: string;
|
|
300
|
+
errorClass: "TypeError" | "RangeError" | "SyntaxError" | "ReferenceError" | "Error" | "NonError";
|
|
301
|
+
classification:
|
|
302
|
+
| "rpc-response-limit"
|
|
303
|
+
| "rpc-invalid-response"
|
|
304
|
+
| "rpc-timeout"
|
|
305
|
+
| "rpc-closed"
|
|
306
|
+
| "rpc-input-failed"
|
|
307
|
+
| "rpc-exit"
|
|
308
|
+
| "spawn-not-found"
|
|
309
|
+
| "spawn-not-runnable"
|
|
310
|
+
| "spawn-failed"
|
|
311
|
+
| "system-error"
|
|
312
|
+
| "database-error"
|
|
313
|
+
| "catalog-empty"
|
|
314
|
+
| "unexpected";
|
|
315
|
+
stage?: "spawn" | "rpc" | "storage";
|
|
316
|
+
code?: (typeof SYSTEM_ERROR_CODES)[number] | (typeof DATABASE_ERROR_CODES)[number];
|
|
317
|
+
exitCode?: number;
|
|
318
|
+
signal?: (typeof EXIT_SIGNALS)[number];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Match complete, locally authored messages only. Never log an arbitrary message, error name,
|
|
322
|
+
// stack, cause, request payload, or environment: each can contain credentials or prompt text.
|
|
323
|
+
const KNOWN_FAILURES = new Map<string, Pick<OmpConnectionDiagnostic, "classification" | "stage">>([
|
|
324
|
+
[
|
|
325
|
+
"OMP RPC response exceeded command limits",
|
|
326
|
+
{ classification: "rpc-response-limit", stage: "rpc" },
|
|
327
|
+
],
|
|
328
|
+
["OMP RPC response is invalid", { classification: "rpc-invalid-response", stage: "rpc" }],
|
|
329
|
+
["OMP RPC request timed out", { classification: "rpc-timeout", stage: "rpc" }],
|
|
330
|
+
["OMP RPC process is closed", { classification: "rpc-closed", stage: "rpc" }],
|
|
331
|
+
["OMP RPC process was closed", { classification: "rpc-closed", stage: "rpc" }],
|
|
332
|
+
["OMP RPC output channel closed", { classification: "rpc-closed", stage: "rpc" }],
|
|
333
|
+
["OMP RPC input channel failed", { classification: "rpc-input-failed", stage: "rpc" }],
|
|
334
|
+
["OMP reported no available models", { classification: "catalog-empty", stage: "rpc" }],
|
|
335
|
+
["OMP executable was not found", { classification: "spawn-not-found", stage: "spawn" }],
|
|
336
|
+
["OMP executable is not runnable", { classification: "spawn-not-runnable", stage: "spawn" }],
|
|
337
|
+
["OMP process could not be launched", { classification: "spawn-failed", stage: "spawn" }],
|
|
338
|
+
]);
|
|
339
|
+
|
|
340
|
+
// These are diagnostic vocabulary, not patterns: an unrecognized code or signal is never emitted.
|
|
341
|
+
const SYSTEM_ERROR_CODES = [
|
|
342
|
+
"ENOENT",
|
|
343
|
+
"EACCES",
|
|
344
|
+
"EPERM",
|
|
345
|
+
"ENOTDIR",
|
|
346
|
+
"EISDIR",
|
|
347
|
+
"ENOSPC",
|
|
348
|
+
"EMFILE",
|
|
349
|
+
"ENFILE",
|
|
350
|
+
"ETIMEDOUT",
|
|
351
|
+
"ECONNRESET",
|
|
352
|
+
"ECONNREFUSED",
|
|
353
|
+
"EPIPE",
|
|
354
|
+
"EIO",
|
|
355
|
+
] as const;
|
|
356
|
+
const DATABASE_ERROR_CODES = [
|
|
357
|
+
"SQLITE_ERROR",
|
|
358
|
+
"SQLITE_BUSY",
|
|
359
|
+
"SQLITE_LOCKED",
|
|
360
|
+
"SQLITE_CANTOPEN",
|
|
361
|
+
"SQLITE_CORRUPT",
|
|
362
|
+
"SQLITE_NOTADB",
|
|
363
|
+
"SQLITE_READONLY",
|
|
364
|
+
"SQLITE_FULL",
|
|
365
|
+
"SQLITE_IOERR",
|
|
366
|
+
"ERR_SQLITE_ERROR",
|
|
367
|
+
] as const;
|
|
368
|
+
const EXIT_SIGNALS = [
|
|
369
|
+
"SIGABRT",
|
|
370
|
+
"SIGALRM",
|
|
371
|
+
"SIGBUS",
|
|
372
|
+
"SIGCHLD",
|
|
373
|
+
"SIGCONT",
|
|
374
|
+
"SIGFPE",
|
|
375
|
+
"SIGHUP",
|
|
376
|
+
"SIGILL",
|
|
377
|
+
"SIGINT",
|
|
378
|
+
"SIGIO",
|
|
379
|
+
"SIGIOT",
|
|
380
|
+
"SIGKILL",
|
|
381
|
+
"SIGPIPE",
|
|
382
|
+
"SIGPOLL",
|
|
383
|
+
"SIGPROF",
|
|
384
|
+
"SIGPWR",
|
|
385
|
+
"SIGQUIT",
|
|
386
|
+
"SIGSEGV",
|
|
387
|
+
"SIGSTKFLT",
|
|
388
|
+
"SIGSTOP",
|
|
389
|
+
"SIGSYS",
|
|
390
|
+
"SIGTERM",
|
|
391
|
+
"SIGTRAP",
|
|
392
|
+
"SIGTSTP",
|
|
393
|
+
"SIGTTIN",
|
|
394
|
+
"SIGTTOU",
|
|
395
|
+
"SIGUNUSED",
|
|
396
|
+
"SIGURG",
|
|
397
|
+
"SIGUSR1",
|
|
398
|
+
"SIGUSR2",
|
|
399
|
+
"SIGVTALRM",
|
|
400
|
+
"SIGWINCH",
|
|
401
|
+
"SIGXCPU",
|
|
402
|
+
"SIGXFSZ",
|
|
403
|
+
"SIGBREAK",
|
|
404
|
+
"SIGLOST",
|
|
405
|
+
"SIGINFO",
|
|
406
|
+
"unknown",
|
|
407
|
+
] as const;
|
|
408
|
+
|
|
409
|
+
function classifyFailure(
|
|
410
|
+
error: unknown,
|
|
411
|
+
): Omit<OmpConnectionDiagnostic, "diagnosticId" | "operation"> {
|
|
412
|
+
const result: Omit<OmpConnectionDiagnostic, "diagnosticId" | "operation"> = {
|
|
413
|
+
errorClass:
|
|
414
|
+
error instanceof TypeError
|
|
415
|
+
? "TypeError"
|
|
416
|
+
: error instanceof RangeError
|
|
417
|
+
? "RangeError"
|
|
418
|
+
: error instanceof SyntaxError
|
|
419
|
+
? "SyntaxError"
|
|
420
|
+
: error instanceof ReferenceError
|
|
421
|
+
? "ReferenceError"
|
|
422
|
+
: error instanceof Error
|
|
423
|
+
? "Error"
|
|
424
|
+
: "NonError",
|
|
425
|
+
classification: "unexpected",
|
|
426
|
+
};
|
|
427
|
+
const message = error instanceof Error ? error.message : undefined;
|
|
428
|
+
if (typeof message === "string") {
|
|
429
|
+
const known = KNOWN_FAILURES.get(message);
|
|
430
|
+
if (known) return { ...result, ...known };
|
|
431
|
+
const exit = /^OMP RPC process exited \(code (-?(?:0|[1-9]\d{0,9}))\)$/.exec(message);
|
|
432
|
+
if (exit && exit[0] === message) {
|
|
433
|
+
const exitCode = Number(exit[1]);
|
|
434
|
+
if (exitCode >= -2147483648 && exitCode <= 4294967295) {
|
|
435
|
+
return { ...result, classification: "rpc-exit", stage: "rpc", exitCode };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const signal = EXIT_SIGNALS.find(
|
|
439
|
+
(value) => message === `OMP RPC process exited (signal ${value})`,
|
|
440
|
+
);
|
|
441
|
+
if (signal) return { ...result, classification: "rpc-exit", stage: "rpc", signal };
|
|
442
|
+
}
|
|
443
|
+
// Inspect data properties only; error-code getters may execute arbitrary application code.
|
|
444
|
+
const code =
|
|
445
|
+
error && typeof error === "object"
|
|
446
|
+
? Object.getOwnPropertyDescriptor(error, "code")?.value
|
|
447
|
+
: undefined;
|
|
448
|
+
const systemCode = SYSTEM_ERROR_CODES.find((value) => value === code);
|
|
449
|
+
if (systemCode) return { ...result, classification: "system-error", code: systemCode };
|
|
450
|
+
const databaseCode = DATABASE_ERROR_CODES.find((value) => value === code);
|
|
451
|
+
if (databaseCode) {
|
|
452
|
+
return { ...result, classification: "database-error", stage: "storage", code: databaseCode };
|
|
453
|
+
}
|
|
454
|
+
return result;
|
|
269
455
|
}
|
|
270
456
|
|
|
271
457
|
type NativeReservation = { owner: symbol; quarantined: boolean };
|
|
@@ -494,7 +680,22 @@ export function createOmpConnection(
|
|
|
494
680
|
mcpConnector?: OmpMcpConnector,
|
|
495
681
|
mcpInitializationTimeoutMs?: number,
|
|
496
682
|
replayTimeoutMs?: number,
|
|
683
|
+
browserAuthorizationRegistry?: OmpBrowserAuthorizationRegistry,
|
|
684
|
+
reportDiagnostic: (diagnostic: OmpConnectionDiagnostic) => void = (diagnostic) =>
|
|
685
|
+
console.error("OMP provider failure", diagnostic),
|
|
497
686
|
): ProviderConnection {
|
|
687
|
+
const errorDetails = (error: unknown, fallback: string): { message: string } => {
|
|
688
|
+
if (isOmpPublicError(error)) return { message: error.message };
|
|
689
|
+
// The generated ID is the only correlation value we log. It also travels in the public
|
|
690
|
+
// request failure, avoiding any assumption that a caller-supplied request ID is value-safe.
|
|
691
|
+
const diagnosticId = randomUUID();
|
|
692
|
+
try {
|
|
693
|
+
reportDiagnostic({ diagnosticId, operation: fallback, ...classifyFailure(error) });
|
|
694
|
+
} catch {
|
|
695
|
+
// A diagnostic sink must never prevent the request from settling or its cleanup.
|
|
696
|
+
}
|
|
697
|
+
return { message: `${fallback} (diagnostic ${diagnosticId})` };
|
|
698
|
+
};
|
|
498
699
|
const safeCapabilities = [...new Set(capabilities)].filter(
|
|
499
700
|
(capability) =>
|
|
500
701
|
SUPPORTED_CAPABILITIES[capability] &&
|
|
@@ -504,7 +705,12 @@ export function createOmpConnection(
|
|
|
504
705
|
const listeners = new Set<(event: ProviderEvent) => void>();
|
|
505
706
|
const sessions = new Map<
|
|
506
707
|
string,
|
|
507
|
-
{
|
|
708
|
+
{
|
|
709
|
+
token: symbol;
|
|
710
|
+
session: OmpProviderSession;
|
|
711
|
+
nativeSessionId?: string;
|
|
712
|
+
removeBrowserAuthorization?: () => void;
|
|
713
|
+
}
|
|
508
714
|
>();
|
|
509
715
|
const opening = new Map<
|
|
510
716
|
string,
|
|
@@ -525,6 +731,13 @@ export function createOmpConnection(
|
|
|
525
731
|
if (closed) return;
|
|
526
732
|
for (const listener of listeners) listener(event);
|
|
527
733
|
};
|
|
734
|
+
const deleteSession = (sessionId: string, token: symbol): boolean => {
|
|
735
|
+
const slot = sessions.get(sessionId);
|
|
736
|
+
if (slot?.token !== token) return false;
|
|
737
|
+
sessions.delete(sessionId);
|
|
738
|
+
slot.removeBrowserAuthorization?.();
|
|
739
|
+
return true;
|
|
740
|
+
};
|
|
528
741
|
|
|
529
742
|
const requestFailure = (
|
|
530
743
|
requestId: string,
|
|
@@ -696,7 +909,7 @@ export function createOmpConnection(
|
|
|
696
909
|
);
|
|
697
910
|
};
|
|
698
911
|
const retireRewindSession = () => {
|
|
699
|
-
|
|
912
|
+
deleteSession(input.sessionId, token);
|
|
700
913
|
};
|
|
701
914
|
session = await OmpProviderSession.open(
|
|
702
915
|
input,
|
|
@@ -735,19 +948,36 @@ export function createOmpConnection(
|
|
|
735
948
|
nativeReservations.release(nativeSessionId, token);
|
|
736
949
|
return;
|
|
737
950
|
}
|
|
738
|
-
|
|
951
|
+
const browserAgentId = input.config.env.PASEO_AGENT_ID?.trim() || input.sessionId;
|
|
952
|
+
const browserAuthorization = browserAuthorizationRegistry?.register(
|
|
953
|
+
browserAgentId,
|
|
954
|
+
session.openPaseoBrowser.bind(session),
|
|
955
|
+
);
|
|
956
|
+
session.setBrowserAuthorizationIssuer(browserAuthorization?.issue ?? null);
|
|
957
|
+
const removeBrowserAuthorization = browserAuthorization
|
|
958
|
+
? () => {
|
|
959
|
+
session?.setBrowserAuthorizationIssuer(null);
|
|
960
|
+
browserAuthorization.remove();
|
|
961
|
+
}
|
|
962
|
+
: undefined;
|
|
963
|
+
sessions.set(input.sessionId, {
|
|
964
|
+
token,
|
|
965
|
+
session,
|
|
966
|
+
nativeSessionId,
|
|
967
|
+
...(removeBrowserAuthorization ? { removeBrowserAuthorization } : {}),
|
|
968
|
+
});
|
|
739
969
|
await session.publishOpened(input.requestId);
|
|
740
970
|
if (
|
|
741
971
|
closing ||
|
|
742
972
|
controller.signal.aborted ||
|
|
743
973
|
opening.get(input.sessionId)?.token !== token
|
|
744
974
|
) {
|
|
745
|
-
|
|
975
|
+
deleteSession(input.sessionId, token);
|
|
746
976
|
await session.close();
|
|
747
977
|
nativeReservations.release(nativeSessionId, token);
|
|
748
978
|
}
|
|
749
979
|
} catch (error) {
|
|
750
|
-
|
|
980
|
+
deleteSession(input.sessionId, token);
|
|
751
981
|
let cleanupError: unknown;
|
|
752
982
|
if (session) {
|
|
753
983
|
try {
|
|
@@ -841,11 +1071,11 @@ export function createOmpConnection(
|
|
|
841
1071
|
try {
|
|
842
1072
|
await slot.session.close();
|
|
843
1073
|
} catch (error) {
|
|
844
|
-
|
|
1074
|
+
deleteSession(input.sessionId, slot.token);
|
|
845
1075
|
quarantineFailedCleanup(input.sessionId, slot.token, error, slot.nativeSessionId);
|
|
846
1076
|
throw new OmpPublicError("OMP session close failed");
|
|
847
1077
|
}
|
|
848
|
-
|
|
1078
|
+
deleteSession(input.sessionId, slot.token);
|
|
849
1079
|
nativeReservations.release(slot.nativeSessionId, slot.token);
|
|
850
1080
|
emit({ type: "request.completed", requestId: input.requestId });
|
|
851
1081
|
return;
|
|
@@ -862,6 +1092,7 @@ export function createOmpConnection(
|
|
|
862
1092
|
shutdown.abort(shutdownReason);
|
|
863
1093
|
for (const { controller } of opening.values()) controller.abort(shutdownReason);
|
|
864
1094
|
for (const { session } of sessions.values()) session.beginConnectionShutdown();
|
|
1095
|
+
for (const slot of sessions.values()) slot.removeBrowserAuthorization?.();
|
|
865
1096
|
const failures: unknown[] = [];
|
|
866
1097
|
const operationBatch = [...activeOperations];
|
|
867
1098
|
const operationResults = await Promise.allSettled(operationBatch);
|