@greppleai/grip 0.0.1 → 0.1.3
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 +93 -0
- package/README.md +13 -7
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js +4 -2
- package/dist/bun/cli.js.map +1 -1
- package/dist/bun/embedded-assets.d.ts +3 -0
- package/dist/bun/embedded-assets.d.ts.map +1 -0
- package/dist/bun/embedded-assets.js +52 -0
- package/dist/bun/embedded-assets.js.map +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +17 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/remote-workspace-protocol/contract.d.ts +13 -4
- package/dist/core/remote-workspace-protocol/contract.d.ts.map +1 -1
- package/dist/core/remote-workspace-protocol/contract.js +17 -1
- package/dist/core/remote-workspace-protocol/contract.js.map +1 -1
- package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts +16 -0
- package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-client.js +54 -0
- package/dist/core/remote-workspace-protocol/guest-shared-client.js.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts +23 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js +112 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts +30 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.js +243 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.js.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts +3 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.js +31 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.js.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts +9 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.js +89 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.js.map +1 -0
- package/dist/core/remote-workspace-protocol/session.d.ts +2 -0
- package/dist/core/remote-workspace-protocol/session.d.ts.map +1 -1
- package/dist/core/remote-workspace-protocol/session.js +3 -0
- package/dist/core/remote-workspace-protocol/session.js.map +1 -1
- package/dist/core/remote-workspace-protocol/shared-transport.d.ts +71 -0
- package/dist/core/remote-workspace-protocol/shared-transport.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/shared-transport.js +68 -0
- package/dist/core/remote-workspace-protocol/shared-transport.js.map +1 -0
- package/dist/core/sandbox/command.d.ts +2 -4
- package/dist/core/sandbox/command.d.ts.map +1 -1
- package/dist/core/sandbox/command.js +24 -46
- package/dist/core/sandbox/command.js.map +1 -1
- package/dist/core/sandbox/config.d.ts +91 -0
- package/dist/core/sandbox/config.d.ts.map +1 -0
- package/dist/core/sandbox/config.js +88 -0
- package/dist/core/sandbox/config.js.map +1 -0
- package/dist/core/sandbox/lima-guest-binary.d.ts +11 -0
- package/dist/core/sandbox/lima-guest-binary.d.ts.map +1 -0
- package/dist/core/sandbox/lima-guest-binary.js +140 -0
- package/dist/core/sandbox/lima-guest-binary.js.map +1 -0
- package/dist/core/sandbox/lima.d.ts +22 -3
- package/dist/core/sandbox/lima.d.ts.map +1 -1
- package/dist/core/sandbox/lima.js +434 -42
- package/dist/core/sandbox/lima.js.map +1 -1
- package/dist/core/sandbox/shared-lima-roots.d.ts +11 -0
- package/dist/core/sandbox/shared-lima-roots.d.ts.map +1 -0
- package/dist/core/sandbox/shared-lima-roots.js +25 -0
- package/dist/core/sandbox/shared-lima-roots.js.map +1 -0
- package/dist/core/sandbox/update-shared-guest-binary.d.ts +8 -0
- package/dist/core/sandbox/update-shared-guest-binary.d.ts.map +1 -0
- package/dist/core/sandbox/update-shared-guest-binary.js +100 -0
- package/dist/core/sandbox/update-shared-guest-binary.js.map +1 -0
- package/dist/core/settings-manager.d.ts +1 -5
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +25 -14
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts +2 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js +9 -0
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/operations.d.ts +13 -2
- package/dist/core/tools/operations.d.ts.map +1 -1
- package/dist/core/tools/operations.js +132 -36
- package/dist/core/tools/operations.js.map +1 -1
- package/dist/daemon/activity-budget.d.ts +10 -0
- package/dist/daemon/activity-budget.d.ts.map +1 -0
- package/dist/daemon/activity-budget.js +34 -0
- package/dist/daemon/activity-budget.js.map +1 -0
- package/dist/daemon/config.d.ts +2 -0
- package/dist/daemon/config.d.ts.map +1 -1
- package/dist/daemon/config.js +15 -0
- package/dist/daemon/config.js.map +1 -1
- package/dist/daemon/outbound-budget.d.ts +10 -0
- package/dist/daemon/outbound-budget.d.ts.map +1 -0
- package/dist/daemon/outbound-budget.js +30 -0
- package/dist/daemon/outbound-budget.js.map +1 -0
- package/dist/daemon/security.d.ts.map +1 -1
- package/dist/daemon/security.js +3 -1
- package/dist/daemon/security.js.map +1 -1
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +104 -24
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/shared-connection.d.ts +33 -0
- package/dist/daemon/shared-connection.d.ts.map +1 -0
- package/dist/daemon/shared-connection.js +271 -0
- package/dist/daemon/shared-connection.js.map +1 -0
- package/dist/daemon/shared-revision.d.ts +5 -0
- package/dist/daemon/shared-revision.d.ts.map +1 -0
- package/dist/daemon/shared-revision.js +61 -0
- package/dist/daemon/shared-revision.js.map +1 -0
- package/dist/daemon/shared-roots.d.ts +9 -0
- package/dist/daemon/shared-roots.d.ts.map +1 -0
- package/dist/daemon/shared-roots.js +33 -0
- package/dist/daemon/shared-roots.js.map +1 -0
- package/dist/daemon/workspace-runtime.d.ts +1 -1
- package/dist/daemon/workspace-runtime.d.ts.map +1 -1
- package/dist/daemon/workspace-runtime.js +42 -10
- package/dist/daemon/workspace-runtime.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +5 -3
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +13 -3
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts +4 -3
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +5 -4
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +4 -3
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +5 -4
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +4 -3
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/hook-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/hook-execution.js +3 -2
- package/dist/modes/interactive/components/hook-execution.js.map +1 -1
- package/dist/modes/interactive/components/message-card.d.ts +9 -0
- package/dist/modes/interactive/components/message-card.d.ts.map +1 -0
- package/dist/modes/interactive/components/message-card.js +24 -0
- package/dist/modes/interactive/components/message-card.js.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +4 -3
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +5 -4
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +27 -21
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +3 -2
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +0 -3
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +55 -44
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +3 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +10 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +5 -0
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/photon.d.ts.map +1 -1
- package/dist/utils/photon.js +1 -0
- package/dist/utils/photon.js.map +1 -1
- package/docs/daemon.md +6 -4
- package/docs/docs.json +1 -1
- package/docs/index.md +8 -12
- package/docs/packages.md +3 -3
- package/docs/quickstart.md +11 -19
- package/docs/sandbox.md +40 -117
- package/docs/settings.md +12 -11
- package/docs/termux.md +1 -18
- package/docs/usage.md +2 -2
- package/examples/README.md +0 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +5 -6
- package/dist/core/sandbox/docker.d.ts +0 -169
- package/dist/core/sandbox/docker.d.ts.map +0 -1
- package/dist/core/sandbox/docker.js +0 -633
- package/dist/core/sandbox/docker.js.map +0 -1
- package/examples/sandbox-docker/Dockerfile +0 -274
- package/examples/sandbox-docker/README.md +0 -88
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/core/remote-workspace-protocol/session.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, type Hash, randomBytes } from \"node:crypto\";\nimport {\n\tDEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS,\n\tdecodeCanonicalBase64,\n\tdecodeRemoteWorkspaceMessage,\n\tgetRemoteWorkspaceMethodKind,\n\thashRemoteWorkspaceCatalog,\n\tisRemoteWorkspaceMethod,\n\tnegotiateRemoteWorkspaceHandshake,\n\tparseRemoteWorkspaceClientMessage,\n\tparseRemoteWorkspaceRequestParams,\n\tparseRemoteWorkspaceResult,\n\tparseRemoteWorkspaceServerMessage,\n\tREMOTE_WORKSPACE_PROTOCOL_VERSIONS,\n\ttype RemoteWorkspaceCapability,\n\ttype RemoteWorkspaceCatalog,\n\ttype RemoteWorkspaceCatalogChanged,\n\ttype RemoteWorkspaceClientMessage,\n\ttype RemoteWorkspaceErrorCode,\n\ttype RemoteWorkspaceErrorMessage,\n\ttype RemoteWorkspaceExecutionState,\n\ttype RemoteWorkspaceHandshake,\n\ttype RemoteWorkspaceHandshakeAck,\n\ttype RemoteWorkspaceIdentity,\n\ttype RemoteWorkspaceMethod,\n\tRemoteWorkspaceNegotiationError,\n\ttype RemoteWorkspaceNegotiationOptions,\n\ttype RemoteWorkspaceOperationKind,\n\ttype RemoteWorkspaceProtocolError,\n\ttype RemoteWorkspaceProtocolLimits,\n\ttype RemoteWorkspaceRequest,\n\ttype RemoteWorkspaceResult,\n\ttype RemoteWorkspaceServerMessage,\n\ttype RemoteWorkspaceTransferChunk,\n\ttype RemoteWorkspaceTransferStart,\n\ttype RemoteWorkspaceUpdate,\n\tRemoteWorkspaceValidationError,\n\ttype RemoteWorkspaceVersionRange,\n\tvalidateRemoteWorkspaceCatalog,\n\tvalidateRemoteWorkspaceHandshakeAck,\n\tvalidateRemoteWorkspaceLocalToolSchemas,\n\tvalidateRemoteWorkspaceProtocolLimits,\n} from \"./contract.ts\";\n\nexport type RemoteWorkspaceProtocolCloseCode =\n\t| \"protocol_error\"\n\t| \"invalid_payload\"\n\t| \"message_too_large\"\n\t| \"policy_violation\"\n\t| \"normal\";\nexport type RemoteWorkspaceCancellationErrorCode = Extract<RemoteWorkspaceErrorCode, \"cancelled\" | \"deadline_exceeded\">;\n\nexport interface RemoteWorkspaceProtocolCloseReason {\n\tcode: RemoteWorkspaceProtocolCloseCode;\n\tmessage: string;\n}\n\n/** Transport framing is intentionally outside protocol dispatch. Inbound frames must call receive() with raw payload bytes. */\nexport interface RemoteWorkspaceProtocolTransport<Message> {\n\tsend(message: Message): Promise<void>;\n\tclose(reason: RemoteWorkspaceProtocolCloseReason): void | Promise<void>;\n}\n\nexport class RemoteWorkspaceRequestError extends Error {\n\treadonly code: RemoteWorkspaceErrorCode;\n\treadonly executionState: RemoteWorkspaceExecutionState;\n\treadonly retryable: boolean;\n\treadonly details: unknown;\n\n\tconstructor(error: RemoteWorkspaceProtocolError) {\n\t\tsuper(error.message);\n\t\tthis.name = \"RemoteWorkspaceRequestError\";\n\t\tthis.code = error.code;\n\t\tthis.executionState = error.executionState;\n\t\tthis.retryable = error.retryable;\n\t\tthis.details = error.details;\n\t}\n}\n\nexport class RemoteWorkspaceDisconnectedError extends Error {\n\treadonly executionState: RemoteWorkspaceExecutionState;\n\n\tconstructor(message: string, executionState: RemoteWorkspaceExecutionState) {\n\t\tsuper(message);\n\t\tthis.name = \"RemoteWorkspaceDisconnectedError\";\n\t\tthis.executionState = executionState;\n\t}\n}\n\nexport class RemoteWorkspaceTerminationError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RemoteWorkspaceTerminationError\";\n\t}\n}\n\nclass RemoteWorkspaceTransportError extends Error {\n\tconstructor(error: unknown) {\n\t\tsuper(error instanceof Error ? error.message : String(error));\n\t\tthis.name = \"RemoteWorkspaceTransportError\";\n\t}\n}\n\nexport interface RemoteWorkspaceTransferMetadata {\n\tlength: number;\n\tsha256: string;\n}\n\ntype RemoteWorkspaceRequestMessage =\n\t| RemoteWorkspaceUpdate\n\t| RemoteWorkspaceTransferStart\n\t| RemoteWorkspaceTransferChunk\n\t| RemoteWorkspaceErrorMessage\n\t| RemoteWorkspaceResult;\n\nexport type RemoteWorkspaceTypedRequest = RemoteWorkspaceRequest & { method: RemoteWorkspaceMethod };\n\nexport type RemoteWorkspaceUploadRequest = RemoteWorkspaceRequest & { method: \"transfer.upload\" };\n\nexport interface RemoteWorkspaceRequestOptions {\n\ttimeoutMs?: number;\n\tsignal?: AbortSignal;\n\tonUpdate?: (update: unknown) => void | Promise<void>;\n\tonTransferStart?: (metadata: RemoteWorkspaceTransferMetadata) => void | Promise<void>;\n\tonTransferChunk?: (chunk: Buffer) => void | Promise<void>;\n}\n\nexport interface RemoteWorkspaceRequestHandle {\n\treadonly id: string;\n\treadonly result: Promise<unknown>;\n\tsendTransferChunk(chunk: Uint8Array): Promise<void>;\n\tfinishTransfer(length: number, sha256: string): Promise<void>;\n\tcancel(reason?: string): Promise<void>;\n}\n\ninterface ClientUploadState {\n\texpectedLength: number;\n\texpectedSha256: string;\n\tbytes: number;\n\tpendingBytes: number;\n\tchunks: number;\n\tpendingChunks: number;\n\thash: Hash;\n\tfinished: boolean;\n\tqueue: Promise<void>;\n}\n\ninterface ClientDownloadState {\n\texpectedLength: number;\n\texpectedSha256: string;\n\tbytes: number;\n\tchunks: number;\n\thash: Hash;\n}\n\ninterface ClientPendingRequest {\n\tmethod: RemoteWorkspaceMethod;\n\tkind: RemoteWorkspaceOperationKind;\n\tresolve: (value: unknown) => void;\n\treject: (error: Error) => void;\n\tdeadlineTimer: NodeJS.Timeout;\n\tcancelTimer?: NodeJS.Timeout;\n\tcancelSent: boolean;\n\tsignal?: AbortSignal;\n\tonAbort?: () => void;\n\tonUpdate?: (update: unknown) => void;\n\tonTransferStart?: (metadata: RemoteWorkspaceTransferMetadata) => void;\n\tonTransferChunk?: (chunk: Buffer) => void;\n\tnextUpdateSequence: number;\n\tnextIncomingTransferSequence: number;\n\tnextOutgoingTransferSequence: number;\n\tupload?: ClientUploadState;\n\tdownload?: ClientDownloadState;\n}\n\nexport interface RemoteWorkspaceClientProtocolOptions {\n\tversions?: readonly RemoteWorkspaceVersionRange[];\n\trequiredCapabilities?: readonly RemoteWorkspaceCapability[];\n\toptionalCapabilities?: readonly RemoteWorkspaceCapability[];\n\treceiveLimits?: RemoteWorkspaceProtocolLimits;\n\tlocalToolSchemas?: ReadonlyMap<string, string>;\n\thandshakeTimeoutMs?: number;\n\ttombstoneMs?: number;\n\tmaxTombstones?: number;\n\tonCatalogChanged?: (event: RemoteWorkspaceCatalogChanged) => void;\n\tonCatalogRefreshed?: (catalog: RemoteWorkspaceCatalog) => void | Promise<void>;\n}\n\nfunction classifyValidationClose(error: unknown): RemoteWorkspaceProtocolCloseCode {\n\treturn error instanceof RemoteWorkspaceValidationError && error.message.includes(\"exceeds\")\n\t\t? \"message_too_large\"\n\t\t: \"invalid_payload\";\n}\n\ninterface PreparedProtocolMessage<Message> {\n\tmessage: Message;\n\tbytes: number;\n}\n\nfunction serializeProtocolMessage(value: unknown): string {\n\ttry {\n\t\tconst serialized = JSON.stringify(value);\n\t\tif (serialized === undefined) throw new Error(\"Value is not JSON-serializable\");\n\t\treturn serialized;\n\t} catch (error) {\n\t\tthrow new RemoteWorkspaceValidationError(\n\t\t\t`Protocol value is not JSON-serializable: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t}\n}\n\nfunction prepareClientOutbound(\n\tmessage: RemoteWorkspaceClientMessage,\n\tlimits: RemoteWorkspaceProtocolLimits,\n): PreparedProtocolMessage<RemoteWorkspaceClientMessage> {\n\tconst serialized = serializeProtocolMessage(message);\n\tconst decoded = decodeRemoteWorkspaceMessage(serialized, limits);\n\treturn { message: parseRemoteWorkspaceClientMessage(decoded, limits), bytes: Buffer.byteLength(serialized, \"utf8\") };\n}\n\nfunction prepareServerOutbound(\n\tmessage: RemoteWorkspaceServerMessage,\n\tlimits: RemoteWorkspaceProtocolLimits,\n): PreparedProtocolMessage<RemoteWorkspaceServerMessage> {\n\tconst serialized = serializeProtocolMessage(message);\n\tconst decoded = decodeRemoteWorkspaceMessage(serialized, limits);\n\treturn { message: parseRemoteWorkspaceServerMessage(decoded, limits), bytes: Buffer.byteLength(serialized, \"utf8\") };\n}\n\nfunction cloneCatalog(catalog: RemoteWorkspaceCatalog): RemoteWorkspaceCatalog {\n\tconst cloned = JSON.parse(serializeProtocolMessage(catalog)) as RemoteWorkspaceCatalog;\n\tvalidateRemoteWorkspaceCatalog(cloned);\n\treturn cloned;\n}\n\nfunction cloneHandshakeAck(ack: RemoteWorkspaceHandshakeAck): RemoteWorkspaceHandshakeAck {\n\treturn JSON.parse(serializeProtocolMessage(ack)) as RemoteWorkspaceHandshakeAck;\n}\n\nconst WORKSPACE_IDENTITY_RESULT_METHODS = new Set<RemoteWorkspaceMethod>([\n\t\"workspace.read\",\n\t\"workspace.stat\",\n\t\"workspace.readdir\",\n\t\"workspace.glob\",\n\t\"workspace.grep\",\n\t\"workspace.detect_image_mime\",\n\t\"resource.read\",\n]);\n\nfunction cloneWorkspaceIdentity(workspace: RemoteWorkspaceIdentity): RemoteWorkspaceIdentity {\n\treturn JSON.parse(serializeProtocolMessage(workspace)) as RemoteWorkspaceIdentity;\n}\n\nfunction assertResultWorkspaceIdentity(\n\tmethod: RemoteWorkspaceMethod,\n\tresult: unknown,\n\texpected: RemoteWorkspaceIdentity,\n): void {\n\tif (!WORKSPACE_IDENTITY_RESULT_METHODS.has(method)) return;\n\tconst actual = (result as { workspace: RemoteWorkspaceIdentity }).workspace;\n\tif (actual.id !== expected.id || actual.root !== expected.root || actual.pathFlavor !== expected.pathFlavor) {\n\t\tthrow new RemoteWorkspaceValidationError(`Result workspace identity does not match negotiation for ${method}`);\n\t}\n}\n\nfunction validateConfiguredInteger(value: number, label: string, minimum: number, maximum: number): number {\n\tif (!Number.isSafeInteger(value) || value < minimum || value > maximum) {\n\t\tthrow new RemoteWorkspaceValidationError(`${label} must be an integer from ${minimum} to ${maximum}`);\n\t}\n\treturn value;\n}\n\nfunction boundedCloseReason(reason: RemoteWorkspaceProtocolCloseReason): RemoteWorkspaceProtocolCloseReason {\n\tlet message = reason.message;\n\twhile (Buffer.byteLength(message, \"utf8\") > 120) message = message.slice(0, -1);\n\treturn { code: reason.code, message: message || reason.code };\n}\n\nfunction methodCapability(method: RemoteWorkspaceMethod): RemoteWorkspaceCapability | undefined {\n\tif (method === \"catalog.get\" || method === \"tool.invoke\") return undefined;\n\tif (method === \"lsp.status\") return \"lsp_status\";\n\tif (method === \"transfer.upload\" || method === \"transfer.download\") return \"file_transfer\";\n\tif (method === \"artifact.read\") return \"artifacts\";\n\treturn \"primitive_operations\";\n}\n\nasync function settlesWithinDeadline(promise: Promise<void>, timeoutMs: number): Promise<boolean> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => resolve(false), timeoutMs);\n\t\tpromise.then(\n\t\t\t() => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(true);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction isTransferMethod(method: RemoteWorkspaceMethod): boolean {\n\treturn method === \"transfer.upload\" || method === \"transfer.download\" || method === \"artifact.read\";\n}\n\nfunction payloadByteLength(payload: string | Uint8Array): number {\n\treturn typeof payload === \"string\" ? Buffer.byteLength(payload, \"utf8\") : payload.byteLength;\n}\n\nexport class RemoteWorkspaceClientProtocol {\n\tprivate readonly transport: RemoteWorkspaceProtocolTransport<RemoteWorkspaceClientMessage>;\n\tprivate readonly versions: readonly RemoteWorkspaceVersionRange[];\n\tprivate readonly requiredCapabilities: readonly RemoteWorkspaceCapability[];\n\tprivate readonly optionalCapabilities: readonly RemoteWorkspaceCapability[];\n\tprivate readonly receiveLimits: RemoteWorkspaceProtocolLimits;\n\tprivate localToolSchemas: ReadonlyMap<string, string>;\n\tprivate readonly handshakeTimeoutMs: number;\n\tprivate readonly tombstoneMs: number;\n\tprivate readonly maxTombstones: number;\n\tprivate readonly onCatalogChanged: ((event: RemoteWorkspaceCatalogChanged) => void) | undefined;\n\tprivate readonly onCatalogRefreshed: ((catalog: RemoteWorkspaceCatalog) => void | Promise<void>) | undefined;\n\tprivate readonly connectionPrefix = randomBytes(16).toString(\"base64url\");\n\tprivate readonly pending = new Map<string, ClientPendingRequest>();\n\tprivate readonly tombstones = new Map<string, number>();\n\tprivate nextId = 1;\n\tprivate handshakeId: string | undefined;\n\tprivate handshakeOffer: RemoteWorkspaceHandshake | undefined;\n\tprivate handshakeResolve: ((value: RemoteWorkspaceHandshakeAck) => void) | undefined;\n\tprivate handshakeReject: ((error: Error) => void) | undefined;\n\tprivate handshakeTimer: NodeJS.Timeout | undefined;\n\tprivate negotiated: RemoteWorkspaceHandshakeAck | undefined;\n\tprivate latestCatalogEvent: RemoteWorkspaceCatalogChanged | undefined;\n\tprivate catalogRefreshPromise: Promise<void> | undefined;\n\tprivate catalogRefreshing = false;\n\tprivate receiveQueue: Promise<void> = Promise.resolve();\n\tprivate sendQueue: Promise<void> = Promise.resolve();\n\tprivate pendingReceiveBytes = 0;\n\tprivate pendingReceiveMessages = 0;\n\tprivate pendingOutboundBytes = 0;\n\tprivate pendingOutboundMessages = 0;\n\tprivate pendingTransferBytes = 0;\n\tprivate closed = false;\n\n\tconstructor(\n\t\ttransport: RemoteWorkspaceProtocolTransport<RemoteWorkspaceClientMessage>,\n\t\toptions: RemoteWorkspaceClientProtocolOptions = {},\n\t) {\n\t\tthis.transport = transport;\n\t\tthis.versions = options.versions ?? REMOTE_WORKSPACE_PROTOCOL_VERSIONS;\n\t\tthis.requiredCapabilities = options.requiredCapabilities ?? [];\n\t\tthis.optionalCapabilities = options.optionalCapabilities ?? [];\n\t\tthis.receiveLimits = {\n\t\t\t...validateRemoteWorkspaceProtocolLimits(options.receiveLimits ?? DEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS),\n\t\t};\n\t\tthis.localToolSchemas = new Map(options.localToolSchemas ?? []);\n\t\tthis.handshakeTimeoutMs = validateConfiguredInteger(\n\t\t\toptions.handshakeTimeoutMs ?? 10_000,\n\t\t\t\"handshakeTimeoutMs\",\n\t\t\t1,\n\t\t\t5 * 60_000,\n\t\t);\n\t\tthis.tombstoneMs = validateConfiguredInteger(\n\t\t\toptions.tombstoneMs ?? 5 * 60 * 1000,\n\t\t\t\"tombstoneMs\",\n\t\t\t1,\n\t\t\t24 * 60 * 60 * 1000,\n\t\t);\n\t\tthis.maxTombstones = validateConfiguredInteger(options.maxTombstones ?? 65_536, \"maxTombstones\", 1, 1_000_000);\n\t\tthis.onCatalogChanged = options.onCatalogChanged;\n\t\tthis.onCatalogRefreshed = options.onCatalogRefreshed;\n\t}\n\n\tget handshake(): RemoteWorkspaceHandshakeAck | undefined {\n\t\treturn this.negotiated ? cloneHandshakeAck(this.negotiated) : undefined;\n\t}\n\n\tsetLocalToolSchemas(schemas: ReadonlyMap<string, string>): void {\n\t\tconst copy = new Map(schemas);\n\t\tif (this.negotiated) validateRemoteWorkspaceLocalToolSchemas(this.negotiated.catalog, copy);\n\t\tthis.localToolSchemas = copy;\n\t}\n\n\tasync start(): Promise<RemoteWorkspaceHandshakeAck> {\n\t\tif (this.closed) throw new Error(\"Remote workspace protocol client is closed\");\n\t\tif (this.negotiated) return cloneHandshakeAck(this.negotiated);\n\t\tif (this.handshakeResolve) throw new Error(\"Remote workspace protocol handshake is already in progress\");\n\t\tconst id = this.createId();\n\t\tconst offer: RemoteWorkspaceHandshake = {\n\t\t\ttype: \"handshake\",\n\t\t\tid,\n\t\t\tversions: this.versions.map((version) => ({ ...version })),\n\t\t\trequiredCapabilities: [...this.requiredCapabilities],\n\t\t\toptionalCapabilities: [...this.optionalCapabilities],\n\t\t\treceiveLimits: { ...this.receiveLimits },\n\t\t};\n\t\tthis.handshakeId = id;\n\t\tthis.handshakeOffer = offer;\n\t\tconst result = new Promise<RemoteWorkspaceHandshakeAck>((resolve, reject) => {\n\t\t\tthis.handshakeResolve = resolve;\n\t\t\tthis.handshakeReject = reject;\n\t\t\tthis.handshakeTimer = setTimeout(() => {\n\t\t\t\tthis.failHandshake(new Error(\"Remote workspace handshake timed out\"));\n\t\t\t\tvoid this.closeWithReason({ code: \"policy_violation\", message: \"Handshake timed out\" }).catch(\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t}, this.handshakeTimeoutMs);\n\t\t});\n\t\ttry {\n\t\t\tawait this.send(offer);\n\t\t} catch (error) {\n\t\t\tthis.failHandshake(error instanceof Error ? error : new Error(String(error)));\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: \"Handshake send failed\" });\n\t\t}\n\t\treturn result;\n\t}\n\n\twaitForCatalogRefresh(): Promise<void> {\n\t\treturn this.catalogRefreshPromise ?? Promise.resolve();\n\t}\n\n\trequest(\n\t\tmethod: RemoteWorkspaceMethod,\n\t\tparams: unknown,\n\t\toptions: RemoteWorkspaceRequestOptions = {},\n\t): Promise<unknown> {\n\t\tif (method === \"tool.invoke\" && this.catalogRefreshPromise) {\n\t\t\treturn this.catalogRefreshPromise.then(() => this.beginRequest(method, params, options).result);\n\t\t}\n\t\treturn this.beginRequest(method, params, options).result;\n\t}\n\n\tbeginRequest(\n\t\tmethod: RemoteWorkspaceMethod,\n\t\tparams: unknown,\n\t\toptions: RemoteWorkspaceRequestOptions = {},\n\t): RemoteWorkspaceRequestHandle {\n\t\tif (this.closed) throw new Error(\"Remote workspace protocol client is closed\");\n\t\tconst negotiated = this.negotiated;\n\t\tif (!negotiated) throw new Error(\"Remote workspace protocol handshake has not completed\");\n\t\tif (method === \"tool.invoke\" && this.catalogRefreshing) {\n\t\t\tthrow new Error(\"Remote workspace tool catalog refresh is in progress\");\n\t\t}\n\t\tparseRemoteWorkspaceRequestParams(method, params, negotiated.limits);\n\t\tthis.assertMethodAvailable(method, params, negotiated);\n\t\tif (this.pending.size >= negotiated.limits.maxActiveRequests) {\n\t\t\tthrow new Error(\"Remote workspace active request limit reached\");\n\t\t}\n\t\tif (isTransferMethod(method) && this.activeTransferCount() >= negotiated.limits.maxActiveTransfers) {\n\t\t\tthrow new Error(\"Remote workspace active transfer limit reached\");\n\t\t}\n\t\tconst timeoutMs = options.timeoutMs ?? negotiated.limits.maxRequestMs;\n\t\tif (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > negotiated.limits.maxRequestMs) {\n\t\t\tthrow new Error(`Invalid remote workspace timeout: ${timeoutMs}`);\n\t\t}\n\t\tif (options.signal?.aborted) throw new Error(\"Remote workspace request aborted before send\");\n\t\tconst id = this.createId();\n\t\tlet resolveResult: (value: unknown) => void = () => undefined;\n\t\tlet rejectResult: (error: Error) => void = () => undefined;\n\t\tconst result = new Promise<unknown>((resolve, reject) => {\n\t\t\tresolveResult = resolve;\n\t\t\trejectResult = reject;\n\t\t});\n\t\tconst kind = this.requestKind(method);\n\t\tconst pending: ClientPendingRequest = {\n\t\t\tmethod,\n\t\t\tkind,\n\t\t\tresolve: resolveResult,\n\t\t\treject: rejectResult,\n\t\t\tdeadlineTimer: setTimeout(() => {\n\t\t\t\tvoid this.requestCancellation(id, \"deadline_exceeded\", \"Remote workspace request timed out\");\n\t\t\t}, timeoutMs + 25),\n\t\t\tcancelSent: false,\n\t\t\tsignal: options.signal,\n\t\t\tonUpdate: options.onUpdate,\n\t\t\tonTransferStart: options.onTransferStart,\n\t\t\tonTransferChunk: options.onTransferChunk,\n\t\t\tnextUpdateSequence: 0,\n\t\t\tnextIncomingTransferSequence: 0,\n\t\t\tnextOutgoingTransferSequence: 0,\n\t\t\t...(method === \"transfer.upload\" ? { upload: this.createClientUploadState(params) } : {}),\n\t\t};\n\t\tif (options.signal) {\n\t\t\tpending.onAbort = () => {\n\t\t\t\tvoid this.requestCancellation(id, \"cancelled\", \"Remote workspace request cancelled\");\n\t\t\t};\n\t\t\toptions.signal.addEventListener(\"abort\", pending.onAbort, { once: true });\n\t\t}\n\t\tthis.pending.set(id, pending);\n\t\tconst request: RemoteWorkspaceRequest = { type: \"request\", id, method, timeoutMs, params };\n\t\tvoid this.send(request).catch((error: unknown) => this.handleTransportFailure(error));\n\t\treturn {\n\t\t\tid,\n\t\t\tresult,\n\t\t\tsendTransferChunk: (chunk) => this.queueUploadChunk(id, chunk),\n\t\t\tfinishTransfer: (length, sha256) => this.queueUploadFinish(id, length, sha256),\n\t\t\tcancel: (reason) => this.requestCancellation(id, \"cancelled\", reason ?? \"Remote workspace request cancelled\"),\n\t\t};\n\t}\n\n\treceive(payload: string | Uint8Array): Promise<void> {\n\t\tif (this.closed) return Promise.resolve();\n\t\tconst limits = this.negotiated?.limits ?? this.receiveLimits;\n\t\tconst bytes = payloadByteLength(payload);\n\t\tif (\n\t\t\tbytes > limits.maxMessageBytes ||\n\t\t\tthis.pendingReceiveMessages >= limits.maxPendingInboundMessages ||\n\t\t\tthis.pendingReceiveBytes + bytes > limits.maxPendingInboundBytes\n\t\t) {\n\t\t\treturn this.protocolFailure(\n\t\t\t\tnew Error(\"Remote workspace inbound queue exceeds negotiated limits\"),\n\t\t\t\t\"message_too_large\",\n\t\t\t);\n\t\t}\n\t\tconst queuedPayload = typeof payload === \"string\" ? payload : Uint8Array.from(payload);\n\t\tthis.pendingReceiveMessages++;\n\t\tthis.pendingReceiveBytes += bytes;\n\t\tconst operation = this.receiveQueue.then(() => this.receivePayload(queuedPayload));\n\t\tconst tracked = operation.finally(() => {\n\t\t\tthis.pendingReceiveMessages--;\n\t\t\tthis.pendingReceiveBytes -= bytes;\n\t\t});\n\t\tthis.receiveQueue = tracked.catch(() => undefined);\n\t\treturn tracked;\n\t}\n\n\tasync disconnect(message = \"Remote workspace connection closed\"): Promise<void> {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tthis.failHandshake(new RemoteWorkspaceDisconnectedError(message, \"not_started\"));\n\t\tfor (const [id, pending] of this.pending) {\n\t\t\tconst state = pending.kind === \"read\" ? \"not_started\" : \"indeterminate\";\n\t\t\tthis.rejectPending(id, new RemoteWorkspaceDisconnectedError(message, state), true);\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.closed) return;\n\t\tawait this.closeWithReason({ code: \"normal\", message: \"Client closed\" });\n\t}\n\n\tprivate async decodeServerMessage(payload: string | Uint8Array): Promise<RemoteWorkspaceServerMessage | undefined> {\n\t\ttry {\n\t\t\tconst limits = this.negotiated?.limits ?? this.receiveLimits;\n\t\t\tconst decoded = decodeRemoteWorkspaceMessage(payload, limits);\n\t\t\treturn parseRemoteWorkspaceServerMessage(decoded, limits);\n\t\t} catch (error) {\n\t\t\tawait this.protocolFailure(error, classifyValidationClose(error));\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async handleHandshakeAck(message: RemoteWorkspaceHandshakeAck): Promise<void> {\n\t\tif (!this.handshakeId || message.id !== this.handshakeId || !this.handshakeOffer || !this.handshakeResolve) {\n\t\t\tawait this.protocolFailure(new Error(\"Unexpected handshake response\"));\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst ack = validateRemoteWorkspaceHandshakeAck(message, this.handshakeOffer, this.localToolSchemas);\n\t\t\tthis.negotiated = cloneHandshakeAck(ack);\n\t\t\tconst resolve = this.handshakeResolve;\n\t\t\tthis.clearHandshake();\n\t\t\tthis.addTombstone(message.id);\n\t\t\tresolve(cloneHandshakeAck(ack));\n\t\t} catch (error) {\n\t\t\tthis.failHandshake(error instanceof Error ? error : new Error(String(error)));\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: \"Invalid handshake response\" });\n\t\t}\n\t}\n\n\tprivate async handleCatalogChanged(message: RemoteWorkspaceCatalogChanged): Promise<void> {\n\t\tif (!this.negotiated?.capabilities.includes(\"catalog_refresh\")) {\n\t\t\tawait this.protocolFailure(new Error(\"Unnegotiated catalog event\"));\n\t\t\treturn;\n\t\t}\n\t\tthis.catalogRefreshing = true;\n\t\ttry {\n\t\t\tthis.onCatalogChanged?.(message);\n\t\t} catch (error) {\n\t\t\tawait this.protocolFailure(error);\n\t\t\treturn;\n\t\t}\n\t\tthis.latestCatalogEvent = message;\n\t\tthis.scheduleCatalogRefresh();\n\t}\n\n\tprivate handleHandshakeError(message: RemoteWorkspaceErrorMessage): boolean {\n\t\tif (!this.handshakeId || message.id !== this.handshakeId) return false;\n\t\tthis.addTombstone(message.id);\n\t\tthis.failHandshake(new RemoteWorkspaceRequestError(message.error));\n\t\treturn true;\n\t}\n\n\tprivate async findPendingRequest(message: RemoteWorkspaceRequestMessage): Promise<ClientPendingRequest | undefined> {\n\t\tconst pending = this.pending.get(message.id);\n\t\tif (pending) return pending;\n\t\tthis.pruneTombstones();\n\t\tif (!this.tombstones.has(message.id)) {\n\t\t\tawait this.protocolFailure(new Error(`Response for unknown request ID: ${message.id}`));\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate async handleRequestUpdate(message: RemoteWorkspaceUpdate, pending: ClientPendingRequest): Promise<void> {\n\t\tif (!this.negotiated?.capabilities.includes(\"tool_updates\") || message.sequence !== pending.nextUpdateSequence) {\n\t\t\tawait this.protocolFailure(new Error(`Out-of-order or unnegotiated update for request ${message.id}`));\n\t\t\treturn;\n\t\t}\n\t\tpending.nextUpdateSequence++;\n\t\ttry {\n\t\t\tawait pending.onUpdate?.(message.update);\n\t\t} catch (error) {\n\t\t\tawait this.failClientCallback(message.id, error);\n\t\t}\n\t}\n\n\tprivate async handleTransferStart(\n\t\tmessage: RemoteWorkspaceTransferStart,\n\t\tpending: ClientPendingRequest,\n\t): Promise<void> {\n\t\tif (\n\t\t\t(pending.method !== \"transfer.download\" && pending.method !== \"artifact.read\") ||\n\t\t\tpending.download ||\n\t\t\tmessage.length > (this.negotiated?.limits.maxTransferBytes ?? 0)\n\t\t) {\n\t\t\tawait this.protocolFailure(new Error(`Invalid transfer start for ${pending.method}`));\n\t\t\treturn;\n\t\t}\n\t\tpending.download = {\n\t\t\texpectedLength: message.length,\n\t\t\texpectedSha256: message.sha256,\n\t\t\tbytes: 0,\n\t\t\tchunks: 0,\n\t\t\thash: createHash(\"sha256\"),\n\t\t};\n\t\ttry {\n\t\t\tawait pending.onTransferStart?.({ length: message.length, sha256: message.sha256 });\n\t\t} catch (error) {\n\t\t\tawait this.failClientCallback(message.id, error);\n\t\t}\n\t}\n\n\tprivate async handleTransferChunk(\n\t\tmessage: RemoteWorkspaceTransferChunk,\n\t\tpending: ClientPendingRequest,\n\t): Promise<void> {\n\t\tconst download = pending.download;\n\t\tif (!download || message.sequence !== pending.nextIncomingTransferSequence) {\n\t\t\tawait this.protocolFailure(new Error(`Out-of-order transfer chunk for request ${message.id}`));\n\t\t\treturn;\n\t\t}\n\t\tconst limits = this.negotiated?.limits ?? this.receiveLimits;\n\t\tlet chunk: Buffer;\n\t\ttry {\n\t\t\tchunk = decodeCanonicalBase64(message.dataBase64, limits.maxTransferChunkBytes);\n\t\t} catch (error) {\n\t\t\tawait this.protocolFailure(error, \"invalid_payload\");\n\t\t\treturn;\n\t\t}\n\t\tif (\n\t\t\tdownload.chunks + 1 > limits.maxTransferChunks ||\n\t\t\tdownload.bytes + chunk.byteLength > limits.maxTransferBytes ||\n\t\t\tdownload.bytes + chunk.byteLength > download.expectedLength\n\t\t) {\n\t\t\tawait this.protocolFailure(\n\t\t\t\tnew Error(`Transfer exceeds declared or negotiated limits for request ${message.id}`),\n\t\t\t\t\"message_too_large\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tdownload.chunks++;\n\t\tdownload.bytes += chunk.byteLength;\n\t\tdownload.hash.update(chunk);\n\t\tpending.nextIncomingTransferSequence++;\n\t\ttry {\n\t\t\tawait pending.onTransferChunk?.(chunk);\n\t\t} catch (error) {\n\t\t\tawait this.failClientCallback(message.id, error);\n\t\t}\n\t}\n\n\tprivate async handleRequestResult(message: RemoteWorkspaceResult, pending: ClientPendingRequest): Promise<void> {\n\t\ttry {\n\t\t\tconst limits = this.negotiated?.limits ?? this.receiveLimits;\n\t\t\tconst result = parseRemoteWorkspaceResult(pending.method, message.result, limits);\n\t\t\tassertResultWorkspaceIdentity(pending.method, result, this.negotiated!.workspace);\n\t\t\tthis.verifyClientTransferResult(pending, result);\n\t\t\tthis.resolvePending(message.id, result);\n\t\t} catch (error) {\n\t\t\tawait this.protocolFailure(error);\n\t\t}\n\t}\n\n\tprivate async handlePendingMessage(\n\t\tmessage: RemoteWorkspaceRequestMessage,\n\t\tpending: ClientPendingRequest,\n\t): Promise<void> {\n\t\tswitch (message.type) {\n\t\t\tcase \"update\":\n\t\t\t\tawait this.handleRequestUpdate(message, pending);\n\t\t\t\treturn;\n\t\t\tcase \"transfer_start\":\n\t\t\t\tawait this.handleTransferStart(message, pending);\n\t\t\t\treturn;\n\t\t\tcase \"transfer_chunk\":\n\t\t\t\tawait this.handleTransferChunk(message, pending);\n\t\t\t\treturn;\n\t\t\tcase \"error\":\n\t\t\t\tthis.rejectPending(message.id, new RemoteWorkspaceRequestError(message.error), true);\n\t\t\t\treturn;\n\t\t\tcase \"result\":\n\t\t\t\tawait this.handleRequestResult(message, pending);\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate async receivePayload(payload: string | Uint8Array): Promise<void> {\n\t\tif (this.closed) return;\n\t\tconst message = await this.decodeServerMessage(payload);\n\t\tif (!message) return;\n\t\tif (message.type === \"handshake_ack\") {\n\t\t\tawait this.handleHandshakeAck(message);\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"catalog_changed\") {\n\t\t\tawait this.handleCatalogChanged(message);\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"error\" && this.handleHandshakeError(message)) return;\n\t\tconst pending = await this.findPendingRequest(message);\n\t\tif (!pending) return;\n\t\tawait this.handlePendingMessage(message, pending);\n\t}\n\n\tprivate assertMethodAvailable(\n\t\tmethod: RemoteWorkspaceMethod,\n\t\tparams: unknown,\n\t\tack: RemoteWorkspaceHandshakeAck,\n\t): void {\n\t\tconst capabilities = new Set(ack.capabilities);\n\t\tconst capability = methodCapability(method);\n\t\tif (capability && !capabilities.has(capability)) {\n\t\t\tthrow new Error(`Remote workspace capability was not negotiated: ${capability}`);\n\t\t}\n\t\tif (method === \"artifact.read\" && !capabilities.has(\"file_transfer\")) {\n\t\t\tthrow new Error(\"Remote workspace artifact reads require file transfer capability\");\n\t\t}\n\t\tif (method !== \"catalog.get\" && method !== \"tool.invoke\" && !ack.catalog.operations.includes(method)) {\n\t\t\tthrow new Error(`Remote workspace operation is not available: ${method}`);\n\t\t}\n\t\tif (method !== \"tool.invoke\") return;\n\t\tconst invocation = params as { toolName: string; schemaHash: string };\n\t\tconst remoteTool = ack.catalog.tools.find((tool) => tool.name === invocation.toolName);\n\t\tconst localHash = this.localToolSchemas.get(invocation.toolName);\n\t\tif (!remoteTool || !localHash)\n\t\t\tthrow new Error(`Remote tool has no matching local canonical definition: ${invocation.toolName}`);\n\t\tfor (const featureFlag of remoteTool.featureFlags) {\n\t\t\tif (!capabilities.has(featureFlag)) {\n\t\t\t\tthrow new Error(`Remote tool requires an unnegotiated capability: ${featureFlag}`);\n\t\t\t}\n\t\t}\n\t\tif (remoteTool.schemaHash !== localHash || invocation.schemaHash !== localHash) {\n\t\t\tthrow new Error(`Remote tool schema does not match the local canonical definition: ${invocation.toolName}`);\n\t\t}\n\t}\n\n\tprivate requestKind(method: RemoteWorkspaceMethod): RemoteWorkspaceOperationKind {\n\t\treturn method === \"tool.invoke\" ? \"mutation\" : getRemoteWorkspaceMethodKind(method);\n\t}\n\n\tprivate createClientUploadState(params: unknown): ClientUploadState {\n\t\tconst upload = params as { length: number; sha256: string };\n\t\tif (upload.length > (this.negotiated?.limits.maxTransferBytes ?? 0)) {\n\t\t\tthrow new Error(\"Remote workspace upload exceeds negotiated total-byte limit\");\n\t\t}\n\t\treturn {\n\t\t\texpectedLength: upload.length,\n\t\t\texpectedSha256: upload.sha256,\n\t\t\tbytes: 0,\n\t\t\tpendingBytes: 0,\n\t\t\tchunks: 0,\n\t\t\tpendingChunks: 0,\n\t\t\thash: createHash(\"sha256\"),\n\t\t\tfinished: false,\n\t\t\tqueue: Promise.resolve(),\n\t\t};\n\t}\n\n\tprivate queueUploadChunk(id: string, chunk: Uint8Array): Promise<void> {\n\t\tconst pending = this.requireUploadRequest(id);\n\t\tconst upload = pending.upload!;\n\t\tconst limits = this.negotiated!.limits;\n\t\tif (chunk.byteLength > limits.maxTransferChunkBytes) {\n\t\t\tthrow new Error(\"Remote workspace transfer chunk exceeds limit\");\n\t\t}\n\t\tif (\n\t\t\tthis.pendingTransferBytes + chunk.byteLength > limits.maxPendingTransferBytes ||\n\t\t\tupload.pendingBytes + chunk.byteLength > limits.maxPendingTransferBytes ||\n\t\t\tupload.bytes + upload.pendingBytes + chunk.byteLength > limits.maxTransferBytes ||\n\t\t\tupload.bytes + upload.pendingBytes + chunk.byteLength > upload.expectedLength ||\n\t\t\tupload.chunks + upload.pendingChunks + 1 > limits.maxTransferChunks\n\t\t) {\n\t\t\tthrow new Error(\"Remote workspace pending upload exceeds negotiated limits\");\n\t\t}\n\t\tconst copiedChunk = Buffer.from(chunk);\n\t\tupload.pendingBytes += copiedChunk.byteLength;\n\t\tthis.pendingTransferBytes += copiedChunk.byteLength;\n\t\tupload.pendingChunks++;\n\t\tconst operation = upload.queue.then(async () => {\n\t\t\ttry {\n\t\t\t\tif (upload.finished) throw new Error(\"Remote workspace upload is already finished\");\n\t\t\t\tawait this.send({\n\t\t\t\t\ttype: \"transfer_chunk\",\n\t\t\t\t\tid,\n\t\t\t\t\tsequence: pending.nextOutgoingTransferSequence,\n\t\t\t\t\tdataBase64: copiedChunk.toString(\"base64\"),\n\t\t\t\t});\n\t\t\t\tpending.nextOutgoingTransferSequence++;\n\t\t\t\tupload.chunks++;\n\t\t\t\tupload.bytes += copiedChunk.byteLength;\n\t\t\t\tupload.hash.update(copiedChunk);\n\t\t\t} finally {\n\t\t\t\tupload.pendingBytes -= copiedChunk.byteLength;\n\t\t\t\tthis.pendingTransferBytes -= copiedChunk.byteLength;\n\t\t\t\tupload.pendingChunks--;\n\t\t\t}\n\t\t});\n\t\tupload.queue = operation.catch(() => undefined);\n\t\treturn operation.catch(async (error: unknown) => {\n\t\t\tif (error instanceof RemoteWorkspaceTransportError) await this.handleTransportFailure(error);\n\t\t\telse await this.requestCancellation(id, \"cancelled\", error instanceof Error ? error.message : String(error));\n\t\t\tthrow error;\n\t\t});\n\t}\n\n\tprivate queueUploadFinish(id: string, length: number, sha256: string): Promise<void> {\n\t\tconst pending = this.requireUploadRequest(id);\n\t\tconst upload = pending.upload!;\n\t\tconst operation = upload.queue.then(async () => {\n\t\t\tif (upload.finished) throw new Error(\"Remote workspace upload is already finished\");\n\t\t\tconst digest = upload.hash.digest(\"hex\");\n\t\t\tif (\n\t\t\t\tlength !== upload.expectedLength ||\n\t\t\t\tsha256 !== upload.expectedSha256 ||\n\t\t\t\tupload.bytes !== upload.expectedLength ||\n\t\t\t\tdigest !== upload.expectedSha256\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Remote workspace upload length or SHA-256 does not match its declaration\");\n\t\t\t}\n\t\t\tupload.finished = true;\n\t\t\tawait this.send({ type: \"transfer_finish\", id, length, sha256 });\n\t\t});\n\t\tupload.queue = operation.catch(() => undefined);\n\t\treturn operation.catch(async (error: unknown) => {\n\t\t\tif (error instanceof RemoteWorkspaceTransportError) await this.handleTransportFailure(error);\n\t\t\telse await this.requestCancellation(id, \"cancelled\", error instanceof Error ? error.message : String(error));\n\t\t\tthrow error;\n\t\t});\n\t}\n\n\tprivate verifyClientTransferResult(pending: ClientPendingRequest, result: unknown): void {\n\t\tif (pending.method === \"transfer.upload\") {\n\t\t\tconst upload = pending.upload;\n\t\t\tconst metadata = result as { length: number; sha256: string };\n\t\t\tif (\n\t\t\t\t!upload?.finished ||\n\t\t\t\tmetadata.length !== upload.expectedLength ||\n\t\t\t\tmetadata.sha256 !== upload.expectedSha256\n\t\t\t) {\n\t\t\t\tthrow new RemoteWorkspaceValidationError(\"Upload terminal result does not match the verified upload\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (pending.method !== \"transfer.download\" && pending.method !== \"artifact.read\") return;\n\t\tconst download = pending.download;\n\t\tconst metadata = result as { length: number; sha256: string };\n\t\tif (!download) throw new RemoteWorkspaceValidationError(\"Download completed without transfer metadata\");\n\t\tconst digest = download.hash.digest(\"hex\");\n\t\tif (\n\t\t\tdownload.bytes !== download.expectedLength ||\n\t\t\tdigest !== download.expectedSha256 ||\n\t\t\tmetadata.length !== download.expectedLength ||\n\t\t\tmetadata.sha256 !== download.expectedSha256\n\t\t) {\n\t\t\tthrow new RemoteWorkspaceValidationError(\"Download length or SHA-256 verification failed\");\n\t\t}\n\t}\n\n\tprivate scheduleCatalogRefresh(): void {\n\t\tif (this.catalogRefreshPromise || this.closed) return;\n\t\tthis.catalogRefreshPromise = Promise.resolve()\n\t\t\t.then(async () => {\n\t\t\t\twhile (this.latestCatalogEvent && !this.closed) {\n\t\t\t\t\tconst expected = this.latestCatalogEvent;\n\t\t\t\t\tthis.latestCatalogEvent = undefined;\n\t\t\t\t\tconst catalog = (await this.beginRequest(\"catalog.get\", {}).result) as RemoteWorkspaceCatalog;\n\t\t\t\t\tvalidateRemoteWorkspaceCatalog(catalog);\n\t\t\t\t\tvalidateRemoteWorkspaceLocalToolSchemas(catalog, this.localToolSchemas);\n\t\t\t\t\tconst catalogHash = hashRemoteWorkspaceCatalog(catalog);\n\t\t\t\t\tif (catalog.generation !== expected.generation || catalogHash !== expected.catalogHash) {\n\t\t\t\t\t\tif (this.latestCatalogEvent) continue;\n\t\t\t\t\t\tthrow new RemoteWorkspaceValidationError(\"Catalog refresh does not match the advertised generation\");\n\t\t\t\t\t}\n\t\t\t\t\tif (!this.negotiated) throw new Error(\"Remote workspace handshake disappeared during refresh\");\n\t\t\t\t\tthis.negotiated = { ...this.negotiated, catalog, catalogHash };\n\t\t\t\t\tawait this.onCatalogRefreshed?.(cloneCatalog(catalog));\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((error: unknown) => this.protocolFailure(error))\n\t\t\t.finally(() => {\n\t\t\t\tthis.catalogRefreshPromise = undefined;\n\t\t\t\tif (this.latestCatalogEvent && !this.closed) this.scheduleCatalogRefresh();\n\t\t\t\telse this.catalogRefreshing = false;\n\t\t\t});\n\t}\n\n\tprivate activeTransferCount(): number {\n\t\tlet count = 0;\n\t\tfor (const pending of this.pending.values()) if (isTransferMethod(pending.method)) count++;\n\t\treturn count;\n\t}\n\n\tprivate createId(): string {\n\t\treturn `${this.connectionPrefix}:${this.nextId++}`;\n\t}\n\n\tprivate requireUploadRequest(id: string): ClientPendingRequest {\n\t\tconst pending = this.pending.get(id);\n\t\tif (!pending?.upload || pending.method !== \"transfer.upload\") throw new Error(`No active upload request: ${id}`);\n\t\treturn pending;\n\t}\n\n\tprivate async requestCancellation(\n\t\tid: string,\n\t\tcode: RemoteWorkspaceCancellationErrorCode,\n\t\tmessage: string,\n\t): Promise<void> {\n\t\tconst pending = this.pending.get(id);\n\t\tif (!pending || pending.cancelSent) return;\n\t\tpending.cancelSent = true;\n\t\tconst cancellationMs = this.negotiated?.limits.maxCancellationMs ?? 1000;\n\t\tpending.cancelTimer = setTimeout(() => {\n\t\t\tconst executionState = pending.kind === \"read\" ? \"not_started\" : \"indeterminate\";\n\t\t\tthis.rejectPending(\n\t\t\t\tid,\n\t\t\t\tnew RemoteWorkspaceRequestError({ code, message, executionState, retryable: false }),\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\tvoid this.closeWithReason({\n\t\t\t\tcode: \"policy_violation\",\n\t\t\t\tmessage: `Remote workspace cancellation was not acknowledged: ${id}`,\n\t\t\t}).catch(() => this.disconnect(\"Remote workspace cancellation was not acknowledged\"));\n\t\t}, cancellationMs + 500);\n\t\ttry {\n\t\t\tconst sent = await settlesWithinDeadline(\n\t\t\t\tthis.send({ type: \"cancel\", id, reason: message }),\n\t\t\t\tcancellationMs + 250,\n\t\t\t);\n\t\t\tif (!sent) throw new RemoteWorkspaceTransportError(\"Cancellation send timed out\");\n\t\t} catch (error) {\n\t\t\tawait this.handleTransportFailure(error);\n\t\t\treturn;\n\t\t}\n\t\tif (!this.pending.has(id) && pending.cancelTimer) clearTimeout(pending.cancelTimer);\n\t}\n\n\tprivate async failClientCallback(id: string, error: unknown): Promise<void> {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconst executionState = this.pending.get(id)?.kind === \"read\" ? \"not_started\" : \"indeterminate\";\n\t\tawait this.requestCancellation(id, \"cancelled\", \"Client callback failed\");\n\t\tthis.rejectPending(\n\t\t\tid,\n\t\t\tnew RemoteWorkspaceRequestError({\n\t\t\t\tcode: \"internal_error\",\n\t\t\t\tmessage: `Remote workspace client callback failed: ${message}`,\n\t\t\t\texecutionState,\n\t\t\t\tretryable: false,\n\t\t\t}),\n\t\t\ttrue,\n\t\t);\n\t\tawait this.closeWithReason({\n\t\t\tcode: \"policy_violation\",\n\t\t\tmessage: `Remote workspace client callback failed for ${id}`,\n\t\t}).catch(() => this.disconnect(\"Remote workspace client callback failed\"));\n\t}\n\n\tprivate resolvePending(id: string, value: unknown): void {\n\t\tconst pending = this.takePending(id);\n\t\tif (pending) pending.resolve(value);\n\t}\n\n\tprivate rejectPending(id: string, error: Error, tombstone: boolean): void {\n\t\tconst pending = this.takePending(id, tombstone);\n\t\tif (pending) pending.reject(error);\n\t}\n\n\tprivate takePending(id: string, tombstone = true): ClientPendingRequest | undefined {\n\t\tconst pending = this.pending.get(id);\n\t\tif (!pending) return undefined;\n\t\tthis.pending.delete(id);\n\t\tclearTimeout(pending.deadlineTimer);\n\t\tif (pending.cancelTimer) clearTimeout(pending.cancelTimer);\n\t\tif (pending.signal && pending.onAbort) pending.signal.removeEventListener(\"abort\", pending.onAbort);\n\t\tif (tombstone) this.addTombstone(id);\n\t\treturn pending;\n\t}\n\n\tprivate addTombstone(id: string): void {\n\t\tthis.pruneTombstones();\n\t\tif (this.tombstones.size >= this.maxTombstones) {\n\t\t\tvoid this.protocolFailure(new Error(\"Remote workspace tombstone limit exceeded\"), \"policy_violation\").catch(\n\t\t\t\t() => undefined,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.tombstones.set(id, performance.now() + this.tombstoneMs);\n\t}\n\n\tprivate pruneTombstones(): void {\n\t\tconst now = performance.now();\n\t\tfor (const [id, expiresAt] of this.tombstones) if (expiresAt <= now) this.tombstones.delete(id);\n\t}\n\n\tprivate clearHandshake(): void {\n\t\tif (this.handshakeTimer) clearTimeout(this.handshakeTimer);\n\t\tthis.handshakeTimer = undefined;\n\t\tthis.handshakeId = undefined;\n\t\tthis.handshakeOffer = undefined;\n\t\tthis.handshakeResolve = undefined;\n\t\tthis.handshakeReject = undefined;\n\t}\n\n\tprivate failHandshake(error: Error): void {\n\t\tconst reject = this.handshakeReject;\n\t\tthis.clearHandshake();\n\t\treject?.(error);\n\t}\n\n\tprivate send(message: RemoteWorkspaceClientMessage): Promise<void> {\n\t\tconst limits = this.negotiated?.limits ?? this.receiveLimits;\n\t\tconst prepared = prepareClientOutbound(message, limits);\n\t\tconst estimatedBytes = prepared.bytes;\n\t\tif (\n\t\t\tthis.pendingOutboundMessages >= limits.maxPendingOutboundMessages ||\n\t\t\tthis.pendingOutboundBytes + estimatedBytes > limits.maxPendingOutboundBytes\n\t\t) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew RemoteWorkspaceValidationError(\"Remote workspace client outbound queue exceeds limits\"),\n\t\t\t);\n\t\t}\n\t\tthis.pendingOutboundMessages++;\n\t\tthis.pendingOutboundBytes += estimatedBytes;\n\t\tconst operation = this.sendQueue.then(async () => {\n\t\t\tif (this.closed) throw new RemoteWorkspaceTransportError(\"Remote workspace protocol client is closed\");\n\t\t\ttry {\n\t\t\t\tconst sent = await settlesWithinDeadline(this.transport.send(prepared.message), limits.maxTransportSendMs);\n\t\t\t\tif (!sent) throw new RemoteWorkspaceTransportError(\"Remote workspace client transport send timed out\");\n\t\t\t} catch (error) {\n\t\t\t\tthrow error instanceof RemoteWorkspaceTransportError ? error : new RemoteWorkspaceTransportError(error);\n\t\t\t}\n\t\t});\n\t\tconst tracked = operation.finally(() => {\n\t\t\tthis.pendingOutboundMessages--;\n\t\t\tthis.pendingOutboundBytes -= estimatedBytes;\n\t\t});\n\t\tthis.sendQueue = tracked.catch(() => undefined);\n\t\treturn tracked;\n\t}\n\n\tprivate async handleTransportFailure(error: unknown): Promise<void> {\n\t\tconst message = `Remote workspace transport failed: ${error instanceof Error ? error.message : String(error)}`;\n\t\ttry {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message });\n\t\t} catch {\n\t\t\tawait this.disconnect(message);\n\t\t}\n\t}\n\n\tprivate async protocolFailure(\n\t\terror: unknown,\n\t\tcode: RemoteWorkspaceProtocolCloseCode = \"protocol_error\",\n\t): Promise<void> {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\ttry {\n\t\t\tawait this.closeWithReason({ code, message });\n\t\t} catch {\n\t\t\tawait this.disconnect(message);\n\t\t}\n\t}\n\n\tprivate async closeWithReason(reason: RemoteWorkspaceProtocolCloseReason): Promise<void> {\n\t\tif (this.closed) return;\n\t\tawait this.disconnect(reason.message);\n\t\tconst sendDeadline = this.negotiated?.limits.maxTransportSendMs ?? this.receiveLimits.maxTransportSendMs;\n\t\tconst sendsStopped = await settlesWithinDeadline(this.sendQueue, sendDeadline);\n\t\tconst closed = await settlesWithinDeadline(\n\t\t\tPromise.resolve(this.transport.close(boundedCloseReason(reason))),\n\t\t\tsendDeadline,\n\t\t);\n\t\tif (!sendsStopped || !closed) {\n\t\t\tthrow new RemoteWorkspaceTerminationError(\"Remote workspace client transport shutdown timed out\");\n\t\t}\n\t}\n}\n\nexport interface RemoteWorkspaceServerRequestContext {\n\treadonly signal: AbortSignal;\n\treadonly request: RemoteWorkspaceTypedRequest;\n\tmarkSideEffectStarted(): void;\n\tmarkCommitted(): void;\n\tsendUpdate(update: unknown): Promise<void>;\n\tstartTransfer(length: number, sha256: string): Promise<void>;\n\tsendTransferChunk(chunk: Uint8Array): Promise<void>;\n}\n\nexport interface RemoteWorkspaceServerHandler {\n\thandleRequest(request: RemoteWorkspaceTypedRequest, context: RemoteWorkspaceServerRequestContext): Promise<unknown>;\n\thandleUploadChunk?(\n\t\trequest: RemoteWorkspaceUploadRequest,\n\t\tchunk: Buffer,\n\t\tsequence: number,\n\t\tcontext: RemoteWorkspaceServerRequestContext,\n\t): Promise<void>;\n\thandleUploadFinish?(\n\t\trequest: RemoteWorkspaceUploadRequest,\n\t\tmetadata: RemoteWorkspaceTransferMetadata,\n\t\tcontext: RemoteWorkspaceServerRequestContext,\n\t): Promise<void>;\n\tvalidateToolArguments(toolName: string, value: unknown): boolean;\n}\n\nexport interface RemoteWorkspaceServerProtocolOptions {\n\tworkspace: RemoteWorkspaceIdentity;\n\tcatalog: RemoteWorkspaceCatalog;\n\tcapabilities: readonly string[];\n\trequiredClientCapabilities?: readonly string[];\n\tversions?: readonly RemoteWorkspaceVersionRange[];\n\tlimits?: RemoteWorkspaceProtocolLimits;\n\tbackendLabel?: string;\n\tinvalidRequestStrikes?: number;\n\ttombstoneMs?: number;\n\tmaxTombstones?: number;\n}\n\ninterface ServerTransferState {\n\texpectedLength: number;\n\texpectedSha256: string;\n\tbytes: number;\n\tpendingBytes: number;\n\tchunks: number;\n\thash: Hash;\n\tfinishing: boolean;\n\tfinished: boolean;\n}\n\ninterface ServerActiveRequest {\n\trequest: RemoteWorkspaceTypedRequest;\n\tcontroller: AbortController;\n\tcontext: RemoteWorkspaceServerRequestContext;\n\ttimer: NodeJS.Timeout;\n\tkind: RemoteWorkspaceOperationKind;\n\tsideEffectStarted: boolean;\n\tcommitted: boolean;\n\tnextUpdateSequence: number;\n\tnextIncomingTransferSequence: number;\n\tnextOutgoingTransferSequence: number;\n\tpendingOutgoingTransferBytes: number;\n\tincomingTransfer?: ServerTransferState;\n\toutgoingTransfer?: ServerTransferState;\n\tinboundQueue: Promise<void>;\n\toutboundQueue: Promise<void>;\n\thandlerSettled: Promise<void>;\n\tresolveHandlerSettled: () => void;\n\thandlerDone: boolean;\n\tcancellationError?: RemoteWorkspaceProtocolError;\n\tsettled: boolean;\n}\n\nexport class RemoteWorkspaceServerProtocol {\n\tprivate readonly transport: RemoteWorkspaceProtocolTransport<RemoteWorkspaceServerMessage>;\n\tprivate readonly handler: RemoteWorkspaceServerHandler;\n\tprivate readonly workspace: RemoteWorkspaceIdentity;\n\tprivate readonly negotiation: RemoteWorkspaceNegotiationOptions;\n\tprivate readonly backendLabel: string | undefined;\n\tprivate readonly invalidRequestStrikes: number;\n\tprivate readonly tombstoneMs: number;\n\tprivate readonly maxTombstones: number;\n\tprivate readonly active = new Map<string, ServerActiveRequest>();\n\tprivate readonly tombstones = new Map<string, number>();\n\tprivate catalog: RemoteWorkspaceCatalog;\n\tprivate catalogHash: string;\n\tprivate limits: RemoteWorkspaceProtocolLimits;\n\tprivate negotiatedCapabilities = new Set<string>();\n\tprivate receiveQueue: Promise<void> = Promise.resolve();\n\tprivate sendQueue: Promise<void> = Promise.resolve();\n\tprivate disconnectPromise: Promise<void> | undefined;\n\tprivate transportFailurePromise: Promise<void> | undefined;\n\tprivate pendingReceiveBytes = 0;\n\tprivate pendingReceiveMessages = 0;\n\tprivate pendingOutboundBytes = 0;\n\tprivate pendingOutboundMessages = 0;\n\tprivate pendingTransferBytes = 0;\n\tprivate handshaken = false;\n\tprivate closed = false;\n\tprivate draining = false;\n\tprivate strikes = 0;\n\n\tconstructor(\n\t\ttransport: RemoteWorkspaceProtocolTransport<RemoteWorkspaceServerMessage>,\n\t\thandler: RemoteWorkspaceServerHandler,\n\t\toptions: RemoteWorkspaceServerProtocolOptions,\n\t) {\n\t\tconst catalog = cloneCatalog(options.catalog);\n\t\tthis.validateCatalogCapabilities(catalog, new Set(options.capabilities));\n\t\tthis.transport = transport;\n\t\tthis.handler = handler;\n\t\tthis.workspace = cloneWorkspaceIdentity(options.workspace);\n\t\tthis.catalog = catalog;\n\t\tthis.catalogHash = hashRemoteWorkspaceCatalog(catalog);\n\t\tthis.limits = {\n\t\t\t...validateRemoteWorkspaceProtocolLimits(options.limits ?? DEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS),\n\t\t};\n\t\tthis.negotiation = {\n\t\t\tserverVersions: options.versions ?? REMOTE_WORKSPACE_PROTOCOL_VERSIONS,\n\t\t\tserverCapabilities: options.capabilities,\n\t\t\trequiredClientCapabilities: options.requiredClientCapabilities,\n\t\t\tserverLimits: this.limits,\n\t\t};\n\t\tthis.backendLabel = options.backendLabel;\n\t\tthis.invalidRequestStrikes = validateConfiguredInteger(\n\t\t\toptions.invalidRequestStrikes ?? 3,\n\t\t\t\"invalidRequestStrikes\",\n\t\t\t1,\n\t\t\t100,\n\t\t);\n\t\tthis.tombstoneMs = validateConfiguredInteger(\n\t\t\toptions.tombstoneMs ?? 5 * 60 * 1000,\n\t\t\t\"tombstoneMs\",\n\t\t\t1,\n\t\t\t24 * 60 * 60 * 1000,\n\t\t);\n\t\tthis.maxTombstones = validateConfiguredInteger(options.maxTombstones ?? 65_536, \"maxTombstones\", 1, 1_000_000);\n\t}\n\n\tget handshakeComplete(): boolean {\n\t\treturn this.handshaken;\n\t}\n\n\treceive(payload: string | Uint8Array): Promise<void> {\n\t\tif (this.closed) return Promise.resolve();\n\t\tconst bytes = payloadByteLength(payload);\n\t\tif (\n\t\t\tbytes > this.limits.maxMessageBytes ||\n\t\t\tthis.pendingReceiveMessages >= this.limits.maxPendingInboundMessages ||\n\t\t\tthis.pendingReceiveBytes + bytes > this.limits.maxPendingInboundBytes\n\t\t) {\n\t\t\treturn this.closeWithReason({ code: \"message_too_large\", message: \"Inbound protocol queue exceeds limits\" });\n\t\t}\n\t\tconst queuedPayload = typeof payload === \"string\" ? payload : Uint8Array.from(payload);\n\t\tthis.pendingReceiveMessages++;\n\t\tthis.pendingReceiveBytes += bytes;\n\t\tconst operation = this.receiveQueue.then(() => this.receivePayload(queuedPayload));\n\t\tconst tracked = operation.finally(() => {\n\t\t\tthis.pendingReceiveMessages--;\n\t\t\tthis.pendingReceiveBytes -= bytes;\n\t\t});\n\t\tthis.receiveQueue = tracked.catch(() => undefined);\n\t\treturn tracked;\n\t}\n\n\tpublishCatalog(catalog: RemoteWorkspaceCatalog): Promise<void> {\n\t\tif (this.closed) return Promise.reject(new Error(\"Remote workspace protocol server is closed\"));\n\t\tconst clonedCatalog = cloneCatalog(catalog);\n\t\tconst operation = this.receiveQueue.then(() => this.publishCatalogNow(clonedCatalog));\n\t\tthis.receiveQueue = operation.catch(() => undefined);\n\t\treturn operation;\n\t}\n\n\tprivate async publishCatalogNow(catalog: RemoteWorkspaceCatalog): Promise<void> {\n\t\tif (this.closed) throw new Error(\"Remote workspace protocol server is closed\");\n\t\tthis.validateCatalogCapabilities(catalog, new Set(this.negotiation.serverCapabilities));\n\t\tif (catalog.generation <= this.catalog.generation)\n\t\t\tthrow new Error(\"Remote workspace catalog generation must increase\");\n\t\tfor (const [id, active] of Array.from(this.active)) {\n\t\t\tif (active.kind !== \"process\" && active.kind !== \"service\") continue;\n\t\t\tawait this.cancelActive(\n\t\t\t\tid,\n\t\t\t\tthis.createError(\"generation_retired\", \"Remote workspace generation retired\", \"not_started\", false),\n\t\t\t\ttrue,\n\t\t\t);\n\t\t}\n\t\tthis.catalog = catalog;\n\t\tthis.catalogHash = hashRemoteWorkspaceCatalog(catalog);\n\t\tif (this.handshaken && this.negotiatedCapabilities.has(\"catalog_refresh\")) {\n\t\t\tawait this.send({ type: \"catalog_changed\", generation: catalog.generation, catalogHash: this.catalogHash });\n\t\t}\n\t}\n\n\tasync beginDrain(message = \"Remote workspace connection is draining\"): Promise<void> {\n\t\tif (this.closed) return;\n\t\tthis.draining = true;\n\t\tawait Promise.all(\n\t\t\tArray.from(this.active.keys()).map((id) =>\n\t\t\t\tthis.cancelActive(id, this.createError(\"connection_draining\", message, \"not_started\", false), false),\n\t\t\t),\n\t\t);\n\t}\n\n\tdisconnect(): Promise<void> {\n\t\treturn this.disconnectInternal(true);\n\t}\n\n\tprivate disconnectInternal(observeReceiveQueue: boolean): Promise<void> {\n\t\tif (this.disconnectPromise) return this.disconnectPromise;\n\t\tif (this.closed) return Promise.resolve();\n\t\tthis.closed = true;\n\t\tconst activeRequests = Array.from(this.active.values());\n\t\tfor (const active of activeRequests) {\n\t\t\tclearTimeout(active.timer);\n\t\t\tactive.controller.abort();\n\t\t}\n\t\tconst queuedSends = this.sendQueue;\n\t\tconst queuedReceives = this.receiveQueue;\n\t\tthis.disconnectPromise = Promise.all([\n\t\t\t...activeRequests.map(async (active) => {\n\t\t\t\tconst termination = Promise.all([active.handlerSettled, active.inboundQueue, active.outboundQueue]).then(\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t\tconst stopped = await this.settlesWithin(termination, this.limits.maxCancellationMs);\n\t\t\t\tactive.settled = true;\n\t\t\t\tthis.active.delete(active.request.id);\n\t\t\t\treturn stopped;\n\t\t\t}),\n\t\t\tsettlesWithinDeadline(queuedSends, this.limits.maxTransportSendMs),\n\t\t\t...(observeReceiveQueue ? [settlesWithinDeadline(queuedReceives, this.limits.maxCancellationMs)] : []),\n\t\t]).then((results) => {\n\t\t\tif (results.some((stopped) => !stopped)) {\n\t\t\t\tthrow new RemoteWorkspaceTerminationError(\n\t\t\t\t\t\"Remote workspace handlers did not terminate before the disconnect deadline\",\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\treturn this.disconnectPromise;\n\t}\n\n\tprivate async receivePayload(payload: string | Uint8Array): Promise<void> {\n\t\tif (this.closed) return;\n\t\tlet decoded: unknown;\n\t\ttry {\n\t\t\tdecoded = decodeRemoteWorkspaceMessage(payload, this.limits);\n\t\t} catch (error) {\n\t\t\tawait this.closeWithReason({ code: classifyValidationClose(error), message: this.errorMessage(error) });\n\t\t\treturn;\n\t\t}\n\t\tlet message: RemoteWorkspaceClientMessage;\n\t\ttry {\n\t\t\tmessage = parseRemoteWorkspaceClientMessage(decoded, this.limits);\n\t\t} catch (error) {\n\t\t\tawait this.handleInvalidMessage(decoded, error);\n\t\t\treturn;\n\t\t}\n\t\tif (!this.handshaken) {\n\t\t\tif (message.type !== \"handshake\") {\n\t\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: \"Request received before handshake\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.handleHandshake(message);\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"handshake\") {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: \"Duplicate handshake\" });\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"cancel\") {\n\t\t\tawait this.handleCancel(message.id);\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"transfer_chunk\") {\n\t\t\tawait this.handleUploadChunk(message.id, message.sequence, message.dataBase64);\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"transfer_finish\") {\n\t\t\tawait this.handleUploadFinish(message.id, message.length, message.sha256);\n\t\t\treturn;\n\t\t}\n\t\tawait this.handleRequest(message);\n\t}\n\tprivate async handleRequestValidationFailure(requestId: string, error: unknown): Promise<void> {\n\t\tthis.addTombstone(requestId);\n\t\tconst semantic = error instanceof RemoteWorkspaceRequestError;\n\t\tif (!semantic) this.strikes++;\n\t\tconst protocolError = semantic\n\t\t\t? {\n\t\t\t\t\tcode: error.code,\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t\texecutionState: error.executionState,\n\t\t\t\t\tretryable: error.retryable,\n\t\t\t\t\t...(error.details === undefined ? {} : { details: error.details }),\n\t\t\t\t}\n\t\t\t: this.createError(\"invalid_request\", this.errorMessage(error), \"not_started\", false);\n\t\tawait this.send({ type: \"error\", id: requestId, error: protocolError });\n\t\tif (this.strikes >= this.invalidRequestStrikes) {\n\t\t\tawait this.closeWithReason({ code: \"policy_violation\", message: \"Too many invalid requests\" });\n\t\t}\n\t}\n\n\tprivate async handleHandshake(handshake: RemoteWorkspaceHandshake): Promise<void> {\n\t\tif (this.isDuplicateId(handshake.id)) {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Duplicate request ID: ${handshake.id}` });\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst negotiated = negotiateRemoteWorkspaceHandshake(handshake, this.negotiation);\n\t\t\tthis.limits = negotiated.limits;\n\t\t\tthis.negotiatedCapabilities = new Set(negotiated.capabilities);\n\t\t\tthis.handshaken = true;\n\t\t\tthis.addTombstone(handshake.id);\n\t\t\tawait this.send({\n\t\t\t\ttype: \"handshake_ack\",\n\t\t\t\tid: handshake.id,\n\t\t\t\tversion: negotiated.version,\n\t\t\t\tcapabilities: negotiated.capabilities,\n\t\t\t\tworkspace: this.workspace,\n\t\t\t\tlimits: negotiated.limits,\n\t\t\t\tcatalog: this.catalog,\n\t\t\t\tcatalogHash: this.catalogHash,\n\t\t\t\tbackendMetadata: {\n\t\t\t\t\tkind: \"remote-workspace\",\n\t\t\t\t\t...(this.backendLabel ? { label: this.backendLabel } : {}),\n\t\t\t\t},\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst protocolError =\n\t\t\t\terror instanceof RemoteWorkspaceNegotiationError\n\t\t\t\t\t? this.createError(error.code, error.message, \"not_started\", false)\n\t\t\t\t\t: this.createError(\"invalid_request\", this.errorMessage(error), \"not_started\", false);\n\t\t\tawait this.send({ type: \"error\", id: handshake.id, error: protocolError }).catch(() => undefined);\n\t\t\tawait this.closeWithReason({ code: \"policy_violation\", message: protocolError.message });\n\t\t}\n\t}\n\n\tprivate async handleRequest(request: RemoteWorkspaceRequest): Promise<void> {\n\t\tif (this.isDuplicateId(request.id)) {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Duplicate request ID: ${request.id}` });\n\t\t\treturn;\n\t\t}\n\t\tif (this.draining) {\n\t\t\tthis.addTombstone(request.id);\n\t\t\tawait this.sendError(request.id, \"connection_draining\", \"Connection is draining\", \"not_started\", false);\n\t\t\treturn;\n\t\t}\n\t\tif (!isRemoteWorkspaceMethod(request.method)) {\n\t\t\tthis.addTombstone(request.id);\n\t\t\tawait this.sendError(\n\t\t\t\trequest.id,\n\t\t\t\t\"method_not_supported\",\n\t\t\t\t`Unsupported method: ${request.method}`,\n\t\t\t\t\"not_started\",\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst typedRequest = request as RemoteWorkspaceTypedRequest;\n\t\ttry {\n\t\t\tparseRemoteWorkspaceRequestParams(typedRequest.method, typedRequest.params, this.limits);\n\t\t\tthis.assertMethodAvailable(typedRequest);\n\t\t\tthis.validateCatalogRequest(typedRequest);\n\t\t} catch (error) {\n\t\t\tawait this.handleRequestValidationFailure(request.id, error);\n\t\t\treturn;\n\t\t}\n\t\tif (this.active.size >= this.limits.maxActiveRequests) {\n\t\t\tthis.addTombstone(request.id);\n\t\t\tawait this.sendError(request.id, \"limit_exceeded\", \"Active request limit reached\", \"not_started\", true);\n\t\t\treturn;\n\t\t}\n\t\tif (isTransferMethod(typedRequest.method) && this.activeTransferCount() >= this.limits.maxActiveTransfers) {\n\t\t\tthis.addTombstone(request.id);\n\t\t\tawait this.sendError(request.id, \"limit_exceeded\", \"Active transfer limit reached\", \"not_started\", true);\n\t\t\treturn;\n\t\t}\n\t\tconst controller = new AbortController();\n\t\tconst active = this.createActiveRequest(\n\t\t\ttypedRequest,\n\t\t\tcontroller,\n\t\t\tMath.min(request.timeoutMs, this.limits.maxRequestMs),\n\t\t);\n\t\tthis.active.set(request.id, active);\n\t\tif (typedRequest.method === \"catalog.get\") {\n\t\t\tawait this.completeActive(request.id, this.catalog);\n\t\t\treturn;\n\t\t}\n\t\tthis.startHandler(active);\n\t}\n\n\tprivate createActiveRequest(\n\t\trequest: RemoteWorkspaceTypedRequest,\n\t\tcontroller: AbortController,\n\t\ttimeoutMs: number,\n\t): ServerActiveRequest {\n\t\tlet active: ServerActiveRequest;\n\t\tconst context: RemoteWorkspaceServerRequestContext = {\n\t\t\tsignal: controller.signal,\n\t\t\trequest,\n\t\t\tmarkSideEffectStarted: () => {\n\t\t\t\tif (!active.settled) active.sideEffectStarted = true;\n\t\t\t},\n\t\t\tmarkCommitted: () => {\n\t\t\t\tif (!active.settled) {\n\t\t\t\t\tactive.sideEffectStarted = true;\n\t\t\t\t\tactive.committed = true;\n\t\t\t\t}\n\t\t\t},\n\t\t\tsendUpdate: (update) => {\n\t\t\t\tif (!this.negotiatedCapabilities.has(\"tool_updates\")) {\n\t\t\t\t\treturn Promise.reject(\n\t\t\t\t\t\tnew RemoteWorkspaceRequestError(\n\t\t\t\t\t\t\tthis.createError(\"not_available\", \"Tool updates were not negotiated\", \"not_started\", false),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlet prepared: PreparedProtocolMessage<RemoteWorkspaceServerMessage>;\n\t\t\t\ttry {\n\t\t\t\t\tprepared = prepareServerOutbound(\n\t\t\t\t\t\t{ type: \"update\", id: request.id, sequence: active.nextUpdateSequence, update },\n\t\t\t\t\t\tthis.limits,\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn Promise.reject(\n\t\t\t\t\t\tnew RemoteWorkspaceRequestError(\n\t\t\t\t\t\t\tthis.createError(\n\t\t\t\t\t\t\t\t\"result_too_large\",\n\t\t\t\t\t\t\t\tthis.errorMessage(error),\n\t\t\t\t\t\t\t\tthis.executionStateFromActive(active),\n\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst queued = this.queueActiveOutbound(active, prepared.bytes, async () => {\n\t\t\t\t\t\tif (!active.settled) await this.sendPrepared(prepared, true);\n\t\t\t\t\t});\n\t\t\t\t\tactive.nextUpdateSequence++;\n\t\t\t\t\treturn queued;\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn Promise.reject(error);\n\t\t\t\t}\n\t\t\t},\n\t\t\tstartTransfer: (length, sha256) => {\n\t\t\t\tconst prepared = prepareServerOutbound(\n\t\t\t\t\t{ type: \"transfer_start\", id: request.id, length, sha256 },\n\t\t\t\t\tthis.limits,\n\t\t\t\t);\n\t\t\t\treturn this.queueActiveOutbound(active, prepared.bytes, async () => {\n\t\t\t\t\tif (active.settled || active.outgoingTransfer)\n\t\t\t\t\t\tthrow new Error(\"Transfer has already started or settled\");\n\t\t\t\t\tif (!this.negotiatedCapabilities.has(\"file_transfer\"))\n\t\t\t\t\t\tthrow new Error(\"File transfer was not negotiated\");\n\t\t\t\t\tif (length > this.limits.maxTransferBytes) throw new Error(\"Transfer exceeds total-byte limit\");\n\t\t\t\t\tactive.outgoingTransfer = {\n\t\t\t\t\t\texpectedLength: length,\n\t\t\t\t\t\texpectedSha256: sha256,\n\t\t\t\t\t\tbytes: 0,\n\t\t\t\t\t\tpendingBytes: 0,\n\t\t\t\t\t\tchunks: 0,\n\t\t\t\t\t\thash: createHash(\"sha256\"),\n\t\t\t\t\t\tfinishing: false,\n\t\t\t\t\t\tfinished: false,\n\t\t\t\t\t};\n\t\t\t\t\tawait this.sendPrepared(prepared, true);\n\t\t\t\t});\n\t\t\t},\n\t\t\tsendTransferChunk: (chunk) => {\n\t\t\t\tif (chunk.byteLength > this.limits.maxTransferChunkBytes) {\n\t\t\t\t\treturn Promise.reject(new Error(\"Transfer chunk exceeds limit\"));\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\tactive.pendingOutgoingTransferBytes + chunk.byteLength > this.limits.maxPendingTransferBytes ||\n\t\t\t\t\tthis.pendingTransferBytes + chunk.byteLength > this.limits.maxPendingTransferBytes\n\t\t\t\t) {\n\t\t\t\t\treturn Promise.reject(new Error(\"Pending transfer chunks exceed limit\"));\n\t\t\t\t}\n\t\t\t\tconst copiedChunk = Buffer.from(chunk);\n\t\t\t\tconst prepared = prepareServerOutbound(\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"transfer_chunk\",\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\tsequence: active.nextOutgoingTransferSequence,\n\t\t\t\t\t\tdataBase64: copiedChunk.toString(\"base64\"),\n\t\t\t\t\t},\n\t\t\t\t\tthis.limits,\n\t\t\t\t);\n\t\t\t\tactive.pendingOutgoingTransferBytes += copiedChunk.byteLength;\n\t\t\t\tthis.pendingTransferBytes += copiedChunk.byteLength;\n\t\t\t\ttry {\n\t\t\t\t\tconst queued = this.queueActiveOutbound(active, prepared.bytes, async () => {\n\t\t\t\t\t\tconst transfer = active.outgoingTransfer;\n\t\t\t\t\t\tif (active.settled || !transfer || transfer.finished)\n\t\t\t\t\t\t\tthrow new Error(\"Transfer has not started or has settled\");\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttransfer.chunks + 1 > this.limits.maxTransferChunks ||\n\t\t\t\t\t\t\ttransfer.bytes + copiedChunk.byteLength > this.limits.maxTransferBytes ||\n\t\t\t\t\t\t\ttransfer.bytes + copiedChunk.byteLength > transfer.expectedLength\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthrow new Error(\"Transfer exceeds declared or negotiated limits\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait this.sendPrepared(prepared, true);\n\t\t\t\t\t\ttransfer.chunks++;\n\t\t\t\t\t\ttransfer.bytes += copiedChunk.byteLength;\n\t\t\t\t\t\ttransfer.hash.update(copiedChunk);\n\t\t\t\t\t});\n\t\t\t\t\tactive.nextOutgoingTransferSequence++;\n\t\t\t\t\treturn queued.finally(() => {\n\t\t\t\t\t\tactive.pendingOutgoingTransferBytes -= copiedChunk.byteLength;\n\t\t\t\t\t\tthis.pendingTransferBytes -= copiedChunk.byteLength;\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tactive.pendingOutgoingTransferBytes -= copiedChunk.byteLength;\n\t\t\t\t\tthis.pendingTransferBytes -= copiedChunk.byteLength;\n\t\t\t\t\treturn Promise.reject(error);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t\tlet resolveHandlerSettled: () => void = () => undefined;\n\t\tconst handlerSettled = new Promise<void>((resolve) => {\n\t\t\tresolveHandlerSettled = resolve;\n\t\t});\n\t\tconst timer = setTimeout(() => {\n\t\t\tvoid this.cancelActive(\n\t\t\t\trequest.id,\n\t\t\t\tthis.createError(\"deadline_exceeded\", \"Remote workspace request deadline exceeded\", \"not_started\", false),\n\t\t\t\tfalse,\n\t\t\t).catch(() => undefined);\n\t\t}, timeoutMs);\n\t\tactive = {\n\t\t\trequest,\n\t\t\tcontroller,\n\t\t\tcontext,\n\t\t\ttimer,\n\t\t\tkind: this.requestKind(request),\n\t\t\tsideEffectStarted: false,\n\t\t\tcommitted: false,\n\t\t\tnextUpdateSequence: 0,\n\t\t\tnextIncomingTransferSequence: 0,\n\t\t\tnextOutgoingTransferSequence: 0,\n\t\t\tpendingOutgoingTransferBytes: 0,\n\t\t\toutboundQueue: Promise.resolve(),\n\t\t\tinboundQueue: Promise.resolve(),\n\t\t\thandlerSettled,\n\t\t\tresolveHandlerSettled,\n\t\t\thandlerDone: false,\n\t\t\t...(request.method === \"transfer.upload\"\n\t\t\t\t? { incomingTransfer: this.createServerUploadState(request.params) }\n\t\t\t\t: {}),\n\t\t\tsettled: false,\n\t\t};\n\t\treturn active;\n\t}\n\n\tprivate startHandler(active: ServerActiveRequest): void {\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst result = await this.handler.handleRequest(active.request, active.context);\n\t\t\t\tactive.handlerDone = true;\n\t\t\t\tactive.resolveHandlerSettled();\n\t\t\t\tif (active.settled) return;\n\t\t\t\tif (active.cancellationError) return;\n\t\t\t\tawait this.completeActive(active.request.id, result);\n\t\t\t} catch (error) {\n\t\t\t\tactive.handlerDone = true;\n\t\t\t\tactive.resolveHandlerSettled();\n\t\t\t\tif (active.settled) return;\n\t\t\t\tif (active.cancellationError) return;\n\t\t\t\tawait this.handleHandlerFailure(active.request.id, error);\n\t\t\t}\n\t\t})();\n\t}\n\n\tprivate queueActiveOutbound<T>(\n\t\tactive: ServerActiveRequest,\n\t\testimatedBytes: number,\n\t\toperation: () => Promise<T>,\n\t): Promise<T> {\n\t\tif (this.closed || active.settled || this.active.get(active.request.id) !== active) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\n\t\t\t\t\t\"connection_draining\",\n\t\t\t\t\t\"Remote workspace request is no longer active\",\n\t\t\t\t\tthis.executionStateFromActive(active),\n\t\t\t\t\tfalse,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tthis.pendingOutboundMessages >= this.limits.maxPendingOutboundMessages ||\n\t\t\tthis.pendingOutboundBytes + estimatedBytes > this.limits.maxPendingOutboundBytes\n\t\t) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"limit_exceeded\", \"Remote workspace outbound queue exceeds limits\", \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tthis.pendingOutboundMessages++;\n\t\tthis.pendingOutboundBytes += estimatedBytes;\n\t\tconst queued = active.outboundQueue.then(operation);\n\t\tconst result = queued.finally(() => {\n\t\t\tthis.pendingOutboundMessages--;\n\t\t\tthis.pendingOutboundBytes -= estimatedBytes;\n\t\t});\n\t\tactive.outboundQueue = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n\n\tprivate async cancelActive(\n\t\tid: string,\n\t\terror: RemoteWorkspaceProtocolError,\n\t\trequireTermination: boolean,\n\t): Promise<void> {\n\t\tconst active = this.active.get(id);\n\t\tif (!active || active.settled) return;\n\t\tactive.cancellationError ??= error;\n\t\tactive.controller.abort();\n\t\tconst termination = Promise.all([active.handlerSettled, active.inboundQueue, active.outboundQueue]).then(\n\t\t\t() => undefined,\n\t\t);\n\t\tconst handlerStopped = await this.settlesWithin(termination, this.limits.maxCancellationMs);\n\t\tif (handlerStopped) {\n\t\t\tawait this.finalizeCancellation(active);\n\t\t\treturn;\n\t\t}\n\t\tif (requireTermination) {\n\t\t\tawait this.closeUnconfirmedCancellation(id);\n\t\t\tthrow new Error(`Remote workspace request did not stop before generation retirement: ${id}`);\n\t\t}\n\t\tawait this.closeUnconfirmedCancellation(id);\n\t}\n\n\tprivate async finalizeCancellation(active: ServerActiveRequest): Promise<void> {\n\t\tif (active.settled || !active.cancellationError) return;\n\t\tawait active.outboundQueue;\n\t\tif (active.settled) return;\n\t\tawait this.failActive(active.request.id, {\n\t\t\t...active.cancellationError,\n\t\t\texecutionState: this.executionStateFromActive(active),\n\t\t});\n\t}\n\n\tprivate async closeUnconfirmedCancellation(id: string): Promise<void> {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tthis.draining = true;\n\t\tconst activeRequests = Array.from(this.active.values());\n\t\tfor (const active of activeRequests) {\n\t\t\tclearTimeout(active.timer);\n\t\t\tactive.controller.abort();\n\t\t\tactive.settled = true;\n\t\t}\n\t\tawait Promise.all([\n\t\t\tsettlesWithinDeadline(\n\t\t\t\tPromise.all(\n\t\t\t\t\tactiveRequests.flatMap((active) => [active.handlerSettled, active.inboundQueue, active.outboundQueue]),\n\t\t\t\t).then(() => undefined),\n\t\t\t\tthis.limits.maxCancellationMs,\n\t\t\t),\n\t\t\tsettlesWithinDeadline(this.sendQueue, this.limits.maxTransportSendMs),\n\t\t]);\n\t\tthis.active.clear();\n\t\tconst closed = await settlesWithinDeadline(\n\t\t\tPromise.resolve(\n\t\t\t\tthis.transport.close(\n\t\t\t\t\tboundedCloseReason({\n\t\t\t\t\t\tcode: \"policy_violation\",\n\t\t\t\t\t\tmessage: `Remote workspace request did not stop after cancellation: ${id}`,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t\tthis.limits.maxTransportSendMs,\n\t\t).catch(() => false);\n\t\tif (!closed) throw new RemoteWorkspaceTerminationError(\"Remote workspace transport close timed out\");\n\t\tthrow new RemoteWorkspaceTerminationError(`Remote workspace request did not terminate: ${id}`);\n\t}\n\n\tprivate settlesWithin(promise: Promise<void>, timeoutMs: number): Promise<boolean> {\n\t\treturn new Promise((resolve) => {\n\t\t\tlet settled = false;\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tresolve(false);\n\t\t\t}, timeoutMs);\n\t\t\tpromise.then(() => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(true);\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate assertMethodAvailable(request: RemoteWorkspaceTypedRequest): void {\n\t\tconst capability = methodCapability(request.method);\n\t\tif (capability && !this.negotiatedCapabilities.has(capability)) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"not_available\", `Capability was not negotiated: ${capability}`, \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tif (request.method === \"artifact.read\" && !this.negotiatedCapabilities.has(\"file_transfer\")) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"not_available\", \"Artifact reads require file transfer\", \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\trequest.method !== \"catalog.get\" &&\n\t\t\trequest.method !== \"tool.invoke\" &&\n\t\t\t!this.catalog.operations.includes(request.method)\n\t\t) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"not_available\", `Operation is not advertised: ${request.method}`, \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tif (request.method === \"transfer.upload\") {\n\t\t\tconst upload = request.params as { length: number };\n\t\t\tif (upload.length > this.limits.maxTransferBytes) {\n\t\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\t\tthis.createError(\"limit_exceeded\", \"Upload exceeds total-byte limit\", \"not_started\", false),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate validateCatalogRequest(request: RemoteWorkspaceTypedRequest): void {\n\t\tif (request.method !== \"tool.invoke\") return;\n\t\tconst params = request.params as {\n\t\t\tgeneration: number;\n\t\t\tcatalogHash: string;\n\t\t\ttoolName: string;\n\t\t\tschemaHash: string;\n\t\t\targuments: unknown;\n\t\t};\n\t\tif (params.generation !== this.catalog.generation || params.catalogHash !== this.catalogHash) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"stale_generation\", \"Tool catalog generation is stale\", \"not_started\", true),\n\t\t\t);\n\t\t}\n\t\tconst tool = this.catalog.tools.find((entry) => entry.name === params.toolName);\n\t\tif (!tool) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"not_available\", `Tool is not available: ${params.toolName}`, \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tfor (const featureFlag of tool.featureFlags) {\n\t\t\tif (!this.negotiatedCapabilities.has(featureFlag)) {\n\t\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\t\tthis.createError(\n\t\t\t\t\t\t\"not_available\",\n\t\t\t\t\t\t`Tool ${params.toolName} requires unnegotiated capability ${featureFlag}`,\n\t\t\t\t\t\t\"not_started\",\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tif (tool.schemaHash !== params.schemaHash) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"schema_mismatch\", `Schema mismatch for tool ${params.toolName}`, \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tif (!this.handler.validateToolArguments(params.toolName, params.arguments)) {\n\t\t\tthrow new RemoteWorkspaceValidationError(`Arguments do not match the canonical schema for ${params.toolName}`);\n\t\t}\n\t}\n\n\tprivate requestKind(request: RemoteWorkspaceTypedRequest): RemoteWorkspaceOperationKind {\n\t\tif (request.method !== \"tool.invoke\") return getRemoteWorkspaceMethodKind(request.method);\n\t\tconst params = request.params as { toolName: string };\n\t\treturn this.catalog.tools.find((tool) => tool.name === params.toolName)?.executionMode ?? \"service\";\n\t}\n\n\tprivate createServerUploadState(params: unknown): ServerTransferState {\n\t\tconst upload = params as { length: number; sha256: string };\n\t\tif (upload.length > this.limits.maxTransferBytes) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"limit_exceeded\", \"Upload exceeds total-byte limit\", \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\texpectedLength: upload.length,\n\t\t\texpectedSha256: upload.sha256,\n\t\t\tbytes: 0,\n\t\t\tpendingBytes: 0,\n\t\t\tchunks: 0,\n\t\t\thash: createHash(\"sha256\"),\n\t\t\tfinishing: false,\n\t\t\tfinished: false,\n\t\t};\n\t}\n\n\tprivate async handleUploadChunk(id: string, sequence: number, dataBase64: string): Promise<void> {\n\t\tconst active = this.active.get(id);\n\t\tif (!active) {\n\t\t\tawait this.handleUnknownCorrelatedId(id, \"upload chunk\");\n\t\t\treturn;\n\t\t}\n\t\tconst transfer = active.incomingTransfer;\n\t\tif (!transfer || transfer.finished || sequence !== active.nextIncomingTransferSequence) {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Out-of-order upload chunk for ${id}` });\n\t\t\treturn;\n\t\t}\n\t\tlet chunk: Buffer;\n\t\ttry {\n\t\t\tchunk = decodeCanonicalBase64(dataBase64, this.limits.maxTransferChunkBytes);\n\t\t\tif (\n\t\t\t\ttransfer.chunks + 1 > this.limits.maxTransferChunks ||\n\t\t\t\ttransfer.bytes + chunk.byteLength > this.limits.maxTransferBytes ||\n\t\t\t\ttransfer.bytes + chunk.byteLength > transfer.expectedLength ||\n\t\t\t\ttransfer.pendingBytes + chunk.byteLength > this.limits.maxPendingTransferBytes ||\n\t\t\t\tthis.pendingTransferBytes + chunk.byteLength > this.limits.maxPendingTransferBytes\n\t\t\t) {\n\t\t\t\tthrow new RemoteWorkspaceValidationError(\"Upload exceeds declared or negotiated limits\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tawait this.cancelActive(\n\t\t\t\tid,\n\t\t\t\tthis.createError(\"limit_exceeded\", this.errorMessage(error), \"not_started\", false),\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tactive.nextIncomingTransferSequence++;\n\t\ttransfer.chunks++;\n\t\ttransfer.bytes += chunk.byteLength;\n\t\ttransfer.pendingBytes += chunk.byteLength;\n\t\tthis.pendingTransferBytes += chunk.byteLength;\n\t\ttransfer.hash.update(chunk);\n\t\tconst operation = active.inboundQueue\n\t\t\t.then(async () => {\n\t\t\t\tif (active.settled) return;\n\t\t\t\tawait this.handler.handleUploadChunk?.(\n\t\t\t\t\tactive.request as RemoteWorkspaceUploadRequest,\n\t\t\t\t\tchunk,\n\t\t\t\t\tsequence,\n\t\t\t\t\tactive.context,\n\t\t\t\t);\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\ttransfer.pendingBytes -= chunk.byteLength;\n\t\t\t\tthis.pendingTransferBytes -= chunk.byteLength;\n\t\t\t});\n\t\tactive.inboundQueue = operation.catch(() => undefined);\n\t\tvoid operation.catch(async (error: unknown) => {\n\t\t\tif (this.active.get(id) === active) await this.handleHandlerFailure(id, error);\n\t\t});\n\t}\n\n\tprivate async handleUploadFinish(id: string, length: number, sha256: string): Promise<void> {\n\t\tconst active = this.active.get(id);\n\t\tif (!active) {\n\t\t\tawait this.handleUnknownCorrelatedId(id, \"upload finish\");\n\t\t\treturn;\n\t\t}\n\t\tconst transfer = active.incomingTransfer;\n\t\tif (!transfer || transfer.finishing || transfer.finished) {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Unexpected upload finish for ${id}` });\n\t\t\treturn;\n\t\t}\n\t\ttransfer.finishing = true;\n\t\tconst digest = transfer.hash.digest(\"hex\");\n\t\tif (\n\t\t\tlength !== transfer.expectedLength ||\n\t\t\tsha256 !== transfer.expectedSha256 ||\n\t\t\ttransfer.bytes !== transfer.expectedLength ||\n\t\t\tdigest !== transfer.expectedSha256\n\t\t) {\n\t\t\tawait this.cancelActive(\n\t\t\t\tid,\n\t\t\t\tthis.createError(\"invalid_request\", \"Upload length or SHA-256 verification failed\", \"not_started\", false),\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst operation = active.inboundQueue.then(async () => {\n\t\t\tif (active.settled) return;\n\t\t\tawait this.handler.handleUploadFinish?.(\n\t\t\t\tactive.request as RemoteWorkspaceUploadRequest,\n\t\t\t\t{ length, sha256 },\n\t\t\t\tactive.context,\n\t\t\t);\n\t\t\ttransfer.finished = true;\n\t\t});\n\t\tactive.inboundQueue = operation.catch(() => undefined);\n\t\tvoid operation.catch(async (error: unknown) => {\n\t\t\tif (this.active.get(id) === active) await this.handleHandlerFailure(id, error);\n\t\t});\n\t}\n\n\tprivate async handleCancel(id: string): Promise<void> {\n\t\tif (this.active.has(id)) {\n\t\t\tawait this.cancelActive(\n\t\t\t\tid,\n\t\t\t\tthis.createError(\"cancelled\", \"Remote workspace request cancelled\", \"not_started\", false),\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.pruneTombstones();\n\t\tif (this.tombstones.has(id)) return;\n\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Cancellation for unknown request ID: ${id}` });\n\t}\n\n\tprivate async completeActive(id: string, result: unknown): Promise<void> {\n\t\tconst active = this.active.get(id);\n\t\tif (!active || active.settled) return;\n\t\tawait Promise.all([active.outboundQueue, active.inboundQueue]);\n\t\tif (active.settled) return;\n\t\tif (active.cancellationError) return;\n\t\ttry {\n\t\t\tconst validated = parseRemoteWorkspaceResult(active.request.method, result, this.limits);\n\t\t\tassertResultWorkspaceIdentity(active.request.method, validated, this.workspace);\n\t\t\tthis.verifyServerTransferResult(active, validated);\n\t\t\tthis.takeActive(id);\n\t\t\ttry {\n\t\t\t\tawait this.send({ type: \"result\", id, result: validated });\n\t\t\t} catch (error) {\n\t\t\t\tif (!(error instanceof RemoteWorkspaceValidationError)) throw error;\n\t\t\t\tawait this.send({\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\tid,\n\t\t\t\t\terror: this.createError(\n\t\t\t\t\t\t\"result_too_large\",\n\t\t\t\t\t\terror.message,\n\t\t\t\t\t\tactive.kind === \"read\" ? \"not_started\" : active.committed ? \"completed\" : \"indeterminate\",\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tawait this.handleHandlerFailure(id, error);\n\t\t}\n\t}\n\n\tprivate verifyServerTransferResult(active: ServerActiveRequest, result: unknown): void {\n\t\tif (active.request.method === \"transfer.upload\") {\n\t\t\tconst transfer = active.incomingTransfer;\n\t\t\tconst metadata = result as { length: number; sha256: string };\n\t\t\tif (\n\t\t\t\t!transfer?.finished ||\n\t\t\t\tmetadata.length !== transfer.expectedLength ||\n\t\t\t\tmetadata.sha256 !== transfer.expectedSha256\n\t\t\t) {\n\t\t\t\tthrow new RemoteWorkspaceValidationError(\"Upload completed without verified terminal metadata\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (active.request.method !== \"transfer.download\" && active.request.method !== \"artifact.read\") return;\n\t\tconst transfer = active.outgoingTransfer;\n\t\tconst metadata = result as { length: number; sha256: string };\n\t\tif (!transfer) throw new RemoteWorkspaceValidationError(\"Download completed without transfer metadata\");\n\t\tconst digest = transfer.hash.digest(\"hex\");\n\t\ttransfer.finished = true;\n\t\tif (\n\t\t\ttransfer.bytes !== transfer.expectedLength ||\n\t\t\tdigest !== transfer.expectedSha256 ||\n\t\t\tmetadata.length !== transfer.expectedLength ||\n\t\t\tmetadata.sha256 !== transfer.expectedSha256\n\t\t) {\n\t\t\tthrow new RemoteWorkspaceValidationError(\"Download length or SHA-256 verification failed\");\n\t\t}\n\t}\n\n\tprivate async handleHandlerFailure(id: string, error: unknown): Promise<void> {\n\t\tif (error instanceof RemoteWorkspaceRequestError) {\n\t\t\tawait this.failActive(id, {\n\t\t\t\tcode: error.code,\n\t\t\t\tmessage: error.message,\n\t\t\t\texecutionState: this.reconcileExecutionState(id, error.executionState),\n\t\t\t\tretryable: error.retryable,\n\t\t\t\t...(error.details === undefined ? {} : { details: error.details }),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tawait this.failActive(\n\t\t\tid,\n\t\t\tthis.createError(\n\t\t\t\t\"internal_error\",\n\t\t\t\t\"Remote workspace request handler failed\",\n\t\t\t\tthis.executionStateFor(id),\n\t\t\t\tfalse,\n\t\t\t),\n\t\t);\n\t}\n\n\tprivate async failActive(id: string, error: RemoteWorkspaceProtocolError): Promise<void> {\n\t\tconst active = this.takeActive(id);\n\t\tif (!active) return;\n\t\tactive.controller.abort();\n\t\tconst [handlerStopped, outboundStopped, inboundStopped] = await Promise.all([\n\t\t\tsettlesWithinDeadline(active.handlerSettled, this.limits.maxCancellationMs),\n\t\t\tsettlesWithinDeadline(active.outboundQueue, this.limits.maxTransportSendMs),\n\t\t\tsettlesWithinDeadline(active.inboundQueue, this.limits.maxCancellationMs),\n\t\t]);\n\t\tif (!handlerStopped || !outboundStopped || !inboundStopped) {\n\t\t\tawait this.failServerTransport(new RemoteWorkspaceTransportError(\"Request callback queue did not drain\"));\n\t\t\treturn;\n\t\t}\n\t\tconst boundedError: RemoteWorkspaceProtocolError = {\n\t\t\t...error,\n\t\t\tmessage: error.message.slice(0, 4096) || error.code,\n\t\t};\n\t\ttry {\n\t\t\tawait this.send({ type: \"error\", id, error: boundedError });\n\t\t} catch (sendError) {\n\t\t\tif (sendError instanceof RemoteWorkspaceTransportError) return;\n\t\t\tawait this.send({\n\t\t\t\ttype: \"error\",\n\t\t\t\tid,\n\t\t\t\terror: this.createError(\n\t\t\t\t\t\"internal_error\",\n\t\t\t\t\t\"Remote workspace error details exceeded protocol limits\",\n\t\t\t\t\tboundedError.executionState,\n\t\t\t\t\tfalse,\n\t\t\t\t),\n\t\t\t}).catch(() => undefined);\n\t\t}\n\t}\n\n\tprivate takeActive(id: string): ServerActiveRequest | undefined {\n\t\tconst active = this.active.get(id);\n\t\tif (!active || active.settled) return undefined;\n\t\tactive.settled = true;\n\t\tthis.active.delete(id);\n\t\tclearTimeout(active.timer);\n\t\tthis.addTombstone(id);\n\t\treturn active;\n\t}\n\n\tprivate executionStateFor(id: string): RemoteWorkspaceExecutionState {\n\t\tconst active = this.active.get(id);\n\t\treturn active ? this.executionStateFromActive(active) : \"not_started\";\n\t}\n\n\tprivate executionStateFromActive(active: ServerActiveRequest): RemoteWorkspaceExecutionState {\n\t\tif (active.committed) return \"completed\";\n\t\tif (active.sideEffectStarted) return \"indeterminate\";\n\t\treturn \"not_started\";\n\t}\n\n\tprivate reconcileExecutionState(id: string, reported: RemoteWorkspaceExecutionState): RemoteWorkspaceExecutionState {\n\t\tconst active = this.active.get(id);\n\t\tif (!active) return reported;\n\t\tif (active.committed) return \"completed\";\n\t\tif (active.sideEffectStarted && reported === \"not_started\") return \"indeterminate\";\n\t\treturn reported;\n\t}\n\n\tprivate async handleInvalidMessage(value: unknown, error: unknown): Promise<void> {\n\t\tconst id = this.usableId(value);\n\t\tif (!id) {\n\t\t\tawait this.closeWithReason({ code: \"invalid_payload\", message: this.errorMessage(error) });\n\t\t\treturn;\n\t\t}\n\t\tif (this.isDuplicateId(id)) {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Duplicate request ID: ${id}` });\n\t\t\treturn;\n\t\t}\n\t\tthis.addTombstone(id);\n\t\tthis.strikes++;\n\t\tawait this.sendError(id, \"invalid_request\", this.errorMessage(error), \"not_started\", false).catch(\n\t\t\t() => undefined,\n\t\t);\n\t\tif (this.strikes >= this.invalidRequestStrikes) {\n\t\t\tawait this.closeWithReason({ code: \"policy_violation\", message: \"Too many invalid requests\" });\n\t\t}\n\t}\n\n\tprivate usableId(value: unknown): string | undefined {\n\t\tif (!value || typeof value !== \"object\" || !(\"id\" in value)) return undefined;\n\t\tconst id = (value as { id?: unknown }).id;\n\t\treturn typeof id === \"string\" && /^[A-Za-z0-9._:-]{1,128}$/.test(id) ? id : undefined;\n\t}\n\n\tprivate async handleUnknownCorrelatedId(id: string, label: string): Promise<void> {\n\t\tthis.pruneTombstones();\n\t\tif (this.tombstones.has(id)) return;\n\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `${label} for unknown request ID: ${id}` });\n\t}\n\n\tprivate isDuplicateId(id: string): boolean {\n\t\tthis.pruneTombstones();\n\t\treturn this.active.has(id) || this.tombstones.has(id);\n\t}\n\n\tprivate addTombstone(id: string): void {\n\t\tthis.pruneTombstones();\n\t\tif (this.tombstones.size >= this.maxTombstones) {\n\t\t\tvoid this.closeWithReason({\n\t\t\t\tcode: \"policy_violation\",\n\t\t\t\tmessage: \"Request tombstone limit exceeded\",\n\t\t\t}).catch(() => undefined);\n\t\t\treturn;\n\t\t}\n\t\tthis.tombstones.set(id, performance.now() + this.tombstoneMs);\n\t}\n\n\tprivate pruneTombstones(): void {\n\t\tconst now = performance.now();\n\t\tfor (const [id, expiresAt] of this.tombstones) if (expiresAt <= now) this.tombstones.delete(id);\n\t}\n\n\tprivate activeTransferCount(): number {\n\t\tlet count = 0;\n\t\tfor (const active of this.active.values()) if (isTransferMethod(active.request.method)) count++;\n\t\treturn count;\n\t}\n\n\tprivate validateCatalogCapabilities(catalog: RemoteWorkspaceCatalog, capabilities: ReadonlySet<string>): void {\n\t\tfor (const method of catalog.operations) {\n\t\t\tconst capability = methodCapability(method);\n\t\t\tif (capability && !capabilities.has(capability)) {\n\t\t\t\tthrow new RemoteWorkspaceValidationError(`Catalog operation ${method} requires capability ${capability}`);\n\t\t\t}\n\t\t\tif (method === \"artifact.read\" && !capabilities.has(\"file_transfer\")) {\n\t\t\t\tthrow new RemoteWorkspaceValidationError(\"Artifact reads require file transfer capability\");\n\t\t\t}\n\t\t}\n\t\tfor (const tool of catalog.tools) {\n\t\t\tfor (const featureFlag of tool.featureFlags) {\n\t\t\t\tif (!capabilities.has(featureFlag)) {\n\t\t\t\t\tthrow new RemoteWorkspaceValidationError(\n\t\t\t\t\t\t`Tool ${tool.name} requires unsupported capability ${featureFlag}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate createError(\n\t\tcode: RemoteWorkspaceErrorCode,\n\t\tmessage: string,\n\t\texecutionState: RemoteWorkspaceExecutionState,\n\t\tretryable: boolean,\n\t): RemoteWorkspaceProtocolError {\n\t\treturn { code, message: message.slice(0, 4096) || code, executionState, retryable };\n\t}\n\n\tprivate sendError(\n\t\tid: string,\n\t\tcode: RemoteWorkspaceErrorCode,\n\t\tmessage: string,\n\t\texecutionState: RemoteWorkspaceExecutionState,\n\t\tretryable: boolean,\n\t): Promise<void> {\n\t\treturn this.send({ type: \"error\", id, error: this.createError(code, message, executionState, retryable) });\n\t}\n\n\tprivate send(message: RemoteWorkspaceServerMessage, preReserved = false): Promise<void> {\n\t\tif (this.closed) return Promise.reject(new Error(\"Remote workspace protocol server is closed\"));\n\t\treturn this.sendPrepared(prepareServerOutbound(message, this.limits), preReserved);\n\t}\n\n\tprivate sendPrepared(\n\t\tprepared: PreparedProtocolMessage<RemoteWorkspaceServerMessage>,\n\t\tpreReserved = false,\n\t): Promise<void> {\n\t\tif (this.closed) return Promise.reject(new Error(\"Remote workspace protocol server is closed\"));\n\t\tconst estimatedBytes = prepared.bytes;\n\t\tif (\n\t\t\t!preReserved &&\n\t\t\t(this.pendingOutboundMessages >= this.limits.maxPendingOutboundMessages ||\n\t\t\t\tthis.pendingOutboundBytes + estimatedBytes > this.limits.maxPendingOutboundBytes)\n\t\t) {\n\t\t\tconst error = new RemoteWorkspaceTransportError(\"Remote workspace outbound queue exceeds negotiated limits\");\n\t\t\tvoid this.failServerTransport(error);\n\t\t\treturn Promise.reject(error);\n\t\t}\n\t\tif (!preReserved) {\n\t\t\tthis.pendingOutboundMessages++;\n\t\t\tthis.pendingOutboundBytes += estimatedBytes;\n\t\t}\n\t\tconst operation = this.sendQueue.then(async () => {\n\t\t\tif (this.closed) throw new RemoteWorkspaceTransportError(\"Remote workspace protocol server is closed\");\n\t\t\ttry {\n\t\t\t\tconst sent = await settlesWithinDeadline(\n\t\t\t\t\tthis.transport.send(prepared.message),\n\t\t\t\t\tthis.limits.maxTransportSendMs,\n\t\t\t\t);\n\t\t\t\tif (!sent) throw new RemoteWorkspaceTransportError(\"Remote workspace transport send timed out\");\n\t\t\t} catch (error) {\n\t\t\t\tthrow error instanceof RemoteWorkspaceTransportError ? error : new RemoteWorkspaceTransportError(error);\n\t\t\t}\n\t\t});\n\t\tconst tracked = operation.finally(() => {\n\t\t\tif (!preReserved) {\n\t\t\t\tthis.pendingOutboundMessages--;\n\t\t\t\tthis.pendingOutboundBytes -= estimatedBytes;\n\t\t\t}\n\t\t});\n\t\tthis.sendQueue = tracked.catch(() => undefined);\n\t\treturn tracked.catch(async (error: unknown) => {\n\t\t\tif (error instanceof RemoteWorkspaceTransportError) await this.failServerTransport(error);\n\t\t\tthrow error;\n\t\t});\n\t}\n\n\tprivate failServerTransport(error: RemoteWorkspaceTransportError): Promise<void> {\n\t\tthis.transportFailurePromise ??= this.failServerTransportNow(error);\n\t\treturn this.transportFailurePromise;\n\t}\n\n\tprivate async failServerTransportNow(error: RemoteWorkspaceTransportError): Promise<void> {\n\t\tconst activeRequests = Array.from(this.active.values());\n\t\tthis.closed = true;\n\t\tthis.draining = true;\n\t\tfor (const active of activeRequests) {\n\t\t\tclearTimeout(active.timer);\n\t\t\tactive.controller.abort();\n\t\t\tactive.settled = true;\n\t\t}\n\t\tawait settlesWithinDeadline(\n\t\t\tPromise.all(\n\t\t\t\tactiveRequests.flatMap((active) => [active.handlerSettled, active.inboundQueue, active.outboundQueue]),\n\t\t\t).then(() => undefined),\n\t\t\tthis.limits.maxCancellationMs,\n\t\t).catch(() => false);\n\t\tthis.active.clear();\n\t\tawait settlesWithinDeadline(\n\t\t\tPromise.resolve(this.transport.close(boundedCloseReason({ code: \"protocol_error\", message: error.message }))),\n\t\t\tthis.limits.maxTransportSendMs,\n\t\t).catch(() => false);\n\t}\n\n\tprivate errorMessage(error: unknown): string {\n\t\treturn error instanceof Error ? error.message : String(error);\n\t}\n\n\tprivate async closeWithReason(reason: RemoteWorkspaceProtocolCloseReason): Promise<void> {\n\t\tif (this.closed) return;\n\t\tlet terminationError: unknown;\n\t\ttry {\n\t\t\tawait this.disconnectInternal(false);\n\t\t} catch (error) {\n\t\t\tterminationError = error;\n\t\t}\n\t\tconst closed = await settlesWithinDeadline(\n\t\t\tPromise.resolve(this.transport.close(boundedCloseReason(reason))),\n\t\t\tthis.limits.maxTransportSendMs,\n\t\t).catch(() => false);\n\t\tif (!closed && !terminationError) {\n\t\t\tterminationError = new RemoteWorkspaceTerminationError(\"Remote workspace transport close timed out\");\n\t\t}\n\t\tif (terminationError) throw terminationError;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/core/remote-workspace-protocol/session.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, type Hash, randomBytes } from \"node:crypto\";\nimport {\n\tDEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS,\n\tdecodeCanonicalBase64,\n\tdecodeRemoteWorkspaceMessage,\n\tgetRemoteWorkspaceMethodKind,\n\thashRemoteWorkspaceCatalog,\n\tisRemoteWorkspaceMethod,\n\tnegotiateRemoteWorkspaceHandshake,\n\tparseRemoteWorkspaceClientMessage,\n\tparseRemoteWorkspaceRequestParams,\n\tparseRemoteWorkspaceResult,\n\tparseRemoteWorkspaceServerMessage,\n\tREMOTE_WORKSPACE_PROTOCOL_VERSIONS,\n\ttype RemoteWorkspaceCapability,\n\ttype RemoteWorkspaceCatalog,\n\ttype RemoteWorkspaceCatalogChanged,\n\ttype RemoteWorkspaceClientMessage,\n\ttype RemoteWorkspaceErrorCode,\n\ttype RemoteWorkspaceErrorMessage,\n\ttype RemoteWorkspaceExecutionState,\n\ttype RemoteWorkspaceHandshake,\n\ttype RemoteWorkspaceHandshakeAck,\n\ttype RemoteWorkspaceIdentity,\n\ttype RemoteWorkspaceMethod,\n\tRemoteWorkspaceNegotiationError,\n\ttype RemoteWorkspaceNegotiationOptions,\n\ttype RemoteWorkspaceOperationKind,\n\ttype RemoteWorkspaceProtocolError,\n\ttype RemoteWorkspaceProtocolLimits,\n\ttype RemoteWorkspaceRequest,\n\ttype RemoteWorkspaceResult,\n\ttype RemoteWorkspaceServerMessage,\n\ttype RemoteWorkspaceTransferChunk,\n\ttype RemoteWorkspaceTransferStart,\n\ttype RemoteWorkspaceUpdate,\n\tRemoteWorkspaceValidationError,\n\ttype RemoteWorkspaceVersionRange,\n\tvalidateRemoteWorkspaceCatalog,\n\tvalidateRemoteWorkspaceHandshakeAck,\n\tvalidateRemoteWorkspaceLocalToolSchemas,\n\tvalidateRemoteWorkspaceProtocolLimits,\n} from \"./contract.ts\";\n\nexport type RemoteWorkspaceProtocolCloseCode =\n\t| \"protocol_error\"\n\t| \"invalid_payload\"\n\t| \"message_too_large\"\n\t| \"policy_violation\"\n\t| \"normal\";\nexport type RemoteWorkspaceCancellationErrorCode = Extract<RemoteWorkspaceErrorCode, \"cancelled\" | \"deadline_exceeded\">;\n\nexport interface RemoteWorkspaceProtocolCloseReason {\n\tcode: RemoteWorkspaceProtocolCloseCode;\n\tmessage: string;\n}\n\n/** Transport framing is intentionally outside protocol dispatch. Inbound frames must call receive() with raw payload bytes. */\nexport interface RemoteWorkspaceProtocolTransport<Message> {\n\tsend(message: Message): Promise<void>;\n\tclose(reason: RemoteWorkspaceProtocolCloseReason): void | Promise<void>;\n}\n\nexport class RemoteWorkspaceRequestError extends Error {\n\treadonly code: RemoteWorkspaceErrorCode;\n\treadonly executionState: RemoteWorkspaceExecutionState;\n\treadonly retryable: boolean;\n\treadonly details: unknown;\n\n\tconstructor(error: RemoteWorkspaceProtocolError) {\n\t\tsuper(error.message);\n\t\tthis.name = \"RemoteWorkspaceRequestError\";\n\t\tthis.code = error.code;\n\t\tthis.executionState = error.executionState;\n\t\tthis.retryable = error.retryable;\n\t\tthis.details = error.details;\n\t}\n}\n\nexport class RemoteWorkspaceDisconnectedError extends Error {\n\treadonly executionState: RemoteWorkspaceExecutionState;\n\n\tconstructor(message: string, executionState: RemoteWorkspaceExecutionState) {\n\t\tsuper(message);\n\t\tthis.name = \"RemoteWorkspaceDisconnectedError\";\n\t\tthis.executionState = executionState;\n\t}\n}\n\nexport class RemoteWorkspaceTerminationError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RemoteWorkspaceTerminationError\";\n\t}\n}\n\nclass RemoteWorkspaceTransportError extends Error {\n\tconstructor(error: unknown) {\n\t\tsuper(error instanceof Error ? error.message : String(error));\n\t\tthis.name = \"RemoteWorkspaceTransportError\";\n\t}\n}\n\nexport interface RemoteWorkspaceTransferMetadata {\n\tlength: number;\n\tsha256: string;\n}\n\ntype RemoteWorkspaceRequestMessage =\n\t| RemoteWorkspaceUpdate\n\t| RemoteWorkspaceTransferStart\n\t| RemoteWorkspaceTransferChunk\n\t| RemoteWorkspaceErrorMessage\n\t| RemoteWorkspaceResult;\n\nexport type RemoteWorkspaceTypedRequest = RemoteWorkspaceRequest & { method: RemoteWorkspaceMethod };\n\nexport type RemoteWorkspaceUploadRequest = RemoteWorkspaceRequest & { method: \"transfer.upload\" };\n\nexport interface RemoteWorkspaceRequestOptions {\n\ttimeoutMs?: number;\n\tsignal?: AbortSignal;\n\tonUpdate?: (update: unknown) => void | Promise<void>;\n\tonTransferStart?: (metadata: RemoteWorkspaceTransferMetadata) => void | Promise<void>;\n\tonTransferChunk?: (chunk: Buffer) => void | Promise<void>;\n}\n\nexport interface RemoteWorkspaceRequestHandle {\n\treadonly id: string;\n\treadonly result: Promise<unknown>;\n\tsendTransferChunk(chunk: Uint8Array): Promise<void>;\n\tfinishTransfer(length: number, sha256: string): Promise<void>;\n\tcancel(reason?: string): Promise<void>;\n}\n\ninterface ClientUploadState {\n\texpectedLength: number;\n\texpectedSha256: string;\n\tbytes: number;\n\tpendingBytes: number;\n\tchunks: number;\n\tpendingChunks: number;\n\thash: Hash;\n\tfinished: boolean;\n\tqueue: Promise<void>;\n}\n\ninterface ClientDownloadState {\n\texpectedLength: number;\n\texpectedSha256: string;\n\tbytes: number;\n\tchunks: number;\n\thash: Hash;\n}\n\ninterface ClientPendingRequest {\n\tmethod: RemoteWorkspaceMethod;\n\tkind: RemoteWorkspaceOperationKind;\n\tresolve: (value: unknown) => void;\n\treject: (error: Error) => void;\n\tdeadlineTimer: NodeJS.Timeout;\n\tcancelTimer?: NodeJS.Timeout;\n\tcancelSent: boolean;\n\tsignal?: AbortSignal;\n\tonAbort?: () => void;\n\tonUpdate?: (update: unknown) => void;\n\tonTransferStart?: (metadata: RemoteWorkspaceTransferMetadata) => void;\n\tonTransferChunk?: (chunk: Buffer) => void;\n\tnextUpdateSequence: number;\n\tnextIncomingTransferSequence: number;\n\tnextOutgoingTransferSequence: number;\n\tupload?: ClientUploadState;\n\tdownload?: ClientDownloadState;\n}\n\nexport interface RemoteWorkspaceClientProtocolOptions {\n\tversions?: readonly RemoteWorkspaceVersionRange[];\n\trequiredCapabilities?: readonly RemoteWorkspaceCapability[];\n\toptionalCapabilities?: readonly RemoteWorkspaceCapability[];\n\treceiveLimits?: RemoteWorkspaceProtocolLimits;\n\tlocalToolSchemas?: ReadonlyMap<string, string>;\n\thandshakeTimeoutMs?: number;\n\ttombstoneMs?: number;\n\tmaxTombstones?: number;\n\tonCatalogChanged?: (event: RemoteWorkspaceCatalogChanged) => void;\n\tonCatalogRefreshed?: (catalog: RemoteWorkspaceCatalog) => void | Promise<void>;\n\tonDisconnect?: (message: string) => void;\n}\n\nfunction classifyValidationClose(error: unknown): RemoteWorkspaceProtocolCloseCode {\n\treturn error instanceof RemoteWorkspaceValidationError && error.message.includes(\"exceeds\")\n\t\t? \"message_too_large\"\n\t\t: \"invalid_payload\";\n}\n\ninterface PreparedProtocolMessage<Message> {\n\tmessage: Message;\n\tbytes: number;\n}\n\nfunction serializeProtocolMessage(value: unknown): string {\n\ttry {\n\t\tconst serialized = JSON.stringify(value);\n\t\tif (serialized === undefined) throw new Error(\"Value is not JSON-serializable\");\n\t\treturn serialized;\n\t} catch (error) {\n\t\tthrow new RemoteWorkspaceValidationError(\n\t\t\t`Protocol value is not JSON-serializable: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t}\n}\n\nfunction prepareClientOutbound(\n\tmessage: RemoteWorkspaceClientMessage,\n\tlimits: RemoteWorkspaceProtocolLimits,\n): PreparedProtocolMessage<RemoteWorkspaceClientMessage> {\n\tconst serialized = serializeProtocolMessage(message);\n\tconst decoded = decodeRemoteWorkspaceMessage(serialized, limits);\n\treturn { message: parseRemoteWorkspaceClientMessage(decoded, limits), bytes: Buffer.byteLength(serialized, \"utf8\") };\n}\n\nfunction prepareServerOutbound(\n\tmessage: RemoteWorkspaceServerMessage,\n\tlimits: RemoteWorkspaceProtocolLimits,\n): PreparedProtocolMessage<RemoteWorkspaceServerMessage> {\n\tconst serialized = serializeProtocolMessage(message);\n\tconst decoded = decodeRemoteWorkspaceMessage(serialized, limits);\n\treturn { message: parseRemoteWorkspaceServerMessage(decoded, limits), bytes: Buffer.byteLength(serialized, \"utf8\") };\n}\n\nfunction cloneCatalog(catalog: RemoteWorkspaceCatalog): RemoteWorkspaceCatalog {\n\tconst cloned = JSON.parse(serializeProtocolMessage(catalog)) as RemoteWorkspaceCatalog;\n\tvalidateRemoteWorkspaceCatalog(cloned);\n\treturn cloned;\n}\n\nfunction cloneHandshakeAck(ack: RemoteWorkspaceHandshakeAck): RemoteWorkspaceHandshakeAck {\n\treturn JSON.parse(serializeProtocolMessage(ack)) as RemoteWorkspaceHandshakeAck;\n}\n\nconst WORKSPACE_IDENTITY_RESULT_METHODS = new Set<RemoteWorkspaceMethod>([\n\t\"workspace.read\",\n\t\"workspace.stat\",\n\t\"workspace.readdir\",\n\t\"workspace.glob\",\n\t\"workspace.grep\",\n\t\"workspace.detect_image_mime\",\n\t\"resource.read\",\n]);\n\nfunction cloneWorkspaceIdentity(workspace: RemoteWorkspaceIdentity): RemoteWorkspaceIdentity {\n\treturn JSON.parse(serializeProtocolMessage(workspace)) as RemoteWorkspaceIdentity;\n}\n\nfunction assertResultWorkspaceIdentity(\n\tmethod: RemoteWorkspaceMethod,\n\tresult: unknown,\n\texpected: RemoteWorkspaceIdentity,\n): void {\n\tif (!WORKSPACE_IDENTITY_RESULT_METHODS.has(method)) return;\n\tconst actual = (result as { workspace: RemoteWorkspaceIdentity }).workspace;\n\tif (actual.id !== expected.id || actual.root !== expected.root || actual.pathFlavor !== expected.pathFlavor) {\n\t\tthrow new RemoteWorkspaceValidationError(`Result workspace identity does not match negotiation for ${method}`);\n\t}\n}\n\nfunction validateConfiguredInteger(value: number, label: string, minimum: number, maximum: number): number {\n\tif (!Number.isSafeInteger(value) || value < minimum || value > maximum) {\n\t\tthrow new RemoteWorkspaceValidationError(`${label} must be an integer from ${minimum} to ${maximum}`);\n\t}\n\treturn value;\n}\n\nfunction boundedCloseReason(reason: RemoteWorkspaceProtocolCloseReason): RemoteWorkspaceProtocolCloseReason {\n\tlet message = reason.message;\n\twhile (Buffer.byteLength(message, \"utf8\") > 120) message = message.slice(0, -1);\n\treturn { code: reason.code, message: message || reason.code };\n}\n\nfunction methodCapability(method: RemoteWorkspaceMethod): RemoteWorkspaceCapability | undefined {\n\tif (method === \"catalog.get\" || method === \"tool.invoke\") return undefined;\n\tif (method === \"lsp.status\") return \"lsp_status\";\n\tif (method === \"transfer.upload\" || method === \"transfer.download\") return \"file_transfer\";\n\tif (method === \"artifact.read\") return \"artifacts\";\n\treturn \"primitive_operations\";\n}\n\nasync function settlesWithinDeadline(promise: Promise<void>, timeoutMs: number): Promise<boolean> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => resolve(false), timeoutMs);\n\t\tpromise.then(\n\t\t\t() => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(true);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction isTransferMethod(method: RemoteWorkspaceMethod): boolean {\n\treturn method === \"transfer.upload\" || method === \"transfer.download\" || method === \"artifact.read\";\n}\n\nfunction payloadByteLength(payload: string | Uint8Array): number {\n\treturn typeof payload === \"string\" ? Buffer.byteLength(payload, \"utf8\") : payload.byteLength;\n}\n\nexport class RemoteWorkspaceClientProtocol {\n\tprivate readonly transport: RemoteWorkspaceProtocolTransport<RemoteWorkspaceClientMessage>;\n\tprivate readonly versions: readonly RemoteWorkspaceVersionRange[];\n\tprivate readonly requiredCapabilities: readonly RemoteWorkspaceCapability[];\n\tprivate readonly optionalCapabilities: readonly RemoteWorkspaceCapability[];\n\tprivate readonly receiveLimits: RemoteWorkspaceProtocolLimits;\n\tprivate localToolSchemas: ReadonlyMap<string, string>;\n\tprivate readonly handshakeTimeoutMs: number;\n\tprivate readonly tombstoneMs: number;\n\tprivate readonly maxTombstones: number;\n\tprivate readonly onCatalogChanged: ((event: RemoteWorkspaceCatalogChanged) => void) | undefined;\n\tprivate readonly onCatalogRefreshed: ((catalog: RemoteWorkspaceCatalog) => void | Promise<void>) | undefined;\n\tprivate readonly onDisconnect: ((message: string) => void) | undefined;\n\tprivate readonly connectionPrefix = randomBytes(16).toString(\"base64url\");\n\tprivate readonly pending = new Map<string, ClientPendingRequest>();\n\tprivate readonly tombstones = new Map<string, number>();\n\tprivate nextId = 1;\n\tprivate handshakeId: string | undefined;\n\tprivate handshakeOffer: RemoteWorkspaceHandshake | undefined;\n\tprivate handshakeResolve: ((value: RemoteWorkspaceHandshakeAck) => void) | undefined;\n\tprivate handshakeReject: ((error: Error) => void) | undefined;\n\tprivate handshakeTimer: NodeJS.Timeout | undefined;\n\tprivate negotiated: RemoteWorkspaceHandshakeAck | undefined;\n\tprivate latestCatalogEvent: RemoteWorkspaceCatalogChanged | undefined;\n\tprivate catalogRefreshPromise: Promise<void> | undefined;\n\tprivate catalogRefreshing = false;\n\tprivate receiveQueue: Promise<void> = Promise.resolve();\n\tprivate sendQueue: Promise<void> = Promise.resolve();\n\tprivate pendingReceiveBytes = 0;\n\tprivate pendingReceiveMessages = 0;\n\tprivate pendingOutboundBytes = 0;\n\tprivate pendingOutboundMessages = 0;\n\tprivate pendingTransferBytes = 0;\n\tprivate closed = false;\n\n\tconstructor(\n\t\ttransport: RemoteWorkspaceProtocolTransport<RemoteWorkspaceClientMessage>,\n\t\toptions: RemoteWorkspaceClientProtocolOptions = {},\n\t) {\n\t\tthis.transport = transport;\n\t\tthis.versions = options.versions ?? REMOTE_WORKSPACE_PROTOCOL_VERSIONS;\n\t\tthis.requiredCapabilities = options.requiredCapabilities ?? [];\n\t\tthis.optionalCapabilities = options.optionalCapabilities ?? [];\n\t\tthis.receiveLimits = {\n\t\t\t...validateRemoteWorkspaceProtocolLimits(options.receiveLimits ?? DEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS),\n\t\t};\n\t\tthis.localToolSchemas = new Map(options.localToolSchemas ?? []);\n\t\tthis.handshakeTimeoutMs = validateConfiguredInteger(\n\t\t\toptions.handshakeTimeoutMs ?? 10_000,\n\t\t\t\"handshakeTimeoutMs\",\n\t\t\t1,\n\t\t\t5 * 60_000,\n\t\t);\n\t\tthis.tombstoneMs = validateConfiguredInteger(\n\t\t\toptions.tombstoneMs ?? 5 * 60 * 1000,\n\t\t\t\"tombstoneMs\",\n\t\t\t1,\n\t\t\t24 * 60 * 60 * 1000,\n\t\t);\n\t\tthis.maxTombstones = validateConfiguredInteger(options.maxTombstones ?? 65_536, \"maxTombstones\", 1, 1_000_000);\n\t\tthis.onCatalogChanged = options.onCatalogChanged;\n\t\tthis.onCatalogRefreshed = options.onCatalogRefreshed;\n\t\tthis.onDisconnect = options.onDisconnect;\n\t}\n\n\tget handshake(): RemoteWorkspaceHandshakeAck | undefined {\n\t\treturn this.negotiated ? cloneHandshakeAck(this.negotiated) : undefined;\n\t}\n\n\tsetLocalToolSchemas(schemas: ReadonlyMap<string, string>): void {\n\t\tconst copy = new Map(schemas);\n\t\tif (this.negotiated) validateRemoteWorkspaceLocalToolSchemas(this.negotiated.catalog, copy);\n\t\tthis.localToolSchemas = copy;\n\t}\n\n\tasync start(): Promise<RemoteWorkspaceHandshakeAck> {\n\t\tif (this.closed) throw new Error(\"Remote workspace protocol client is closed\");\n\t\tif (this.negotiated) return cloneHandshakeAck(this.negotiated);\n\t\tif (this.handshakeResolve) throw new Error(\"Remote workspace protocol handshake is already in progress\");\n\t\tconst id = this.createId();\n\t\tconst offer: RemoteWorkspaceHandshake = {\n\t\t\ttype: \"handshake\",\n\t\t\tid,\n\t\t\tversions: this.versions.map((version) => ({ ...version })),\n\t\t\trequiredCapabilities: [...this.requiredCapabilities],\n\t\t\toptionalCapabilities: [...this.optionalCapabilities],\n\t\t\treceiveLimits: { ...this.receiveLimits },\n\t\t};\n\t\tthis.handshakeId = id;\n\t\tthis.handshakeOffer = offer;\n\t\tconst result = new Promise<RemoteWorkspaceHandshakeAck>((resolve, reject) => {\n\t\t\tthis.handshakeResolve = resolve;\n\t\t\tthis.handshakeReject = reject;\n\t\t\tthis.handshakeTimer = setTimeout(() => {\n\t\t\t\tthis.failHandshake(new Error(\"Remote workspace handshake timed out\"));\n\t\t\t\tvoid this.closeWithReason({ code: \"policy_violation\", message: \"Handshake timed out\" }).catch(\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t}, this.handshakeTimeoutMs);\n\t\t});\n\t\ttry {\n\t\t\tawait this.send(offer);\n\t\t} catch (error) {\n\t\t\tthis.failHandshake(error instanceof Error ? error : new Error(String(error)));\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: \"Handshake send failed\" });\n\t\t}\n\t\treturn result;\n\t}\n\n\twaitForCatalogRefresh(): Promise<void> {\n\t\treturn this.catalogRefreshPromise ?? Promise.resolve();\n\t}\n\n\trequest(\n\t\tmethod: RemoteWorkspaceMethod,\n\t\tparams: unknown,\n\t\toptions: RemoteWorkspaceRequestOptions = {},\n\t): Promise<unknown> {\n\t\tif (method === \"tool.invoke\" && this.catalogRefreshPromise) {\n\t\t\treturn this.catalogRefreshPromise.then(() => this.beginRequest(method, params, options).result);\n\t\t}\n\t\treturn this.beginRequest(method, params, options).result;\n\t}\n\n\tbeginRequest(\n\t\tmethod: RemoteWorkspaceMethod,\n\t\tparams: unknown,\n\t\toptions: RemoteWorkspaceRequestOptions = {},\n\t): RemoteWorkspaceRequestHandle {\n\t\tif (this.closed) throw new Error(\"Remote workspace protocol client is closed\");\n\t\tconst negotiated = this.negotiated;\n\t\tif (!negotiated) throw new Error(\"Remote workspace protocol handshake has not completed\");\n\t\tif (method === \"tool.invoke\" && this.catalogRefreshing) {\n\t\t\tthrow new Error(\"Remote workspace tool catalog refresh is in progress\");\n\t\t}\n\t\tparseRemoteWorkspaceRequestParams(method, params, negotiated.limits);\n\t\tthis.assertMethodAvailable(method, params, negotiated);\n\t\tif (this.pending.size >= negotiated.limits.maxActiveRequests) {\n\t\t\tthrow new Error(\"Remote workspace active request limit reached\");\n\t\t}\n\t\tif (isTransferMethod(method) && this.activeTransferCount() >= negotiated.limits.maxActiveTransfers) {\n\t\t\tthrow new Error(\"Remote workspace active transfer limit reached\");\n\t\t}\n\t\tconst timeoutMs = options.timeoutMs ?? negotiated.limits.maxRequestMs;\n\t\tif (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > negotiated.limits.maxRequestMs) {\n\t\t\tthrow new Error(`Invalid remote workspace timeout: ${timeoutMs}`);\n\t\t}\n\t\tif (options.signal?.aborted) throw new Error(\"Remote workspace request aborted before send\");\n\t\tconst id = this.createId();\n\t\tlet resolveResult: (value: unknown) => void = () => undefined;\n\t\tlet rejectResult: (error: Error) => void = () => undefined;\n\t\tconst result = new Promise<unknown>((resolve, reject) => {\n\t\t\tresolveResult = resolve;\n\t\t\trejectResult = reject;\n\t\t});\n\t\tconst kind = this.requestKind(method);\n\t\tconst pending: ClientPendingRequest = {\n\t\t\tmethod,\n\t\t\tkind,\n\t\t\tresolve: resolveResult,\n\t\t\treject: rejectResult,\n\t\t\tdeadlineTimer: setTimeout(() => {\n\t\t\t\tvoid this.requestCancellation(id, \"deadline_exceeded\", \"Remote workspace request timed out\");\n\t\t\t}, timeoutMs + 25),\n\t\t\tcancelSent: false,\n\t\t\tsignal: options.signal,\n\t\t\tonUpdate: options.onUpdate,\n\t\t\tonTransferStart: options.onTransferStart,\n\t\t\tonTransferChunk: options.onTransferChunk,\n\t\t\tnextUpdateSequence: 0,\n\t\t\tnextIncomingTransferSequence: 0,\n\t\t\tnextOutgoingTransferSequence: 0,\n\t\t\t...(method === \"transfer.upload\" ? { upload: this.createClientUploadState(params) } : {}),\n\t\t};\n\t\tif (options.signal) {\n\t\t\tpending.onAbort = () => {\n\t\t\t\tvoid this.requestCancellation(id, \"cancelled\", \"Remote workspace request cancelled\");\n\t\t\t};\n\t\t\toptions.signal.addEventListener(\"abort\", pending.onAbort, { once: true });\n\t\t}\n\t\tthis.pending.set(id, pending);\n\t\tconst request: RemoteWorkspaceRequest = { type: \"request\", id, method, timeoutMs, params };\n\t\tvoid this.send(request).catch((error: unknown) => this.handleTransportFailure(error));\n\t\treturn {\n\t\t\tid,\n\t\t\tresult,\n\t\t\tsendTransferChunk: (chunk) => this.queueUploadChunk(id, chunk),\n\t\t\tfinishTransfer: (length, sha256) => this.queueUploadFinish(id, length, sha256),\n\t\t\tcancel: (reason) => this.requestCancellation(id, \"cancelled\", reason ?? \"Remote workspace request cancelled\"),\n\t\t};\n\t}\n\n\treceive(payload: string | Uint8Array): Promise<void> {\n\t\tif (this.closed) return Promise.resolve();\n\t\tconst limits = this.negotiated?.limits ?? this.receiveLimits;\n\t\tconst bytes = payloadByteLength(payload);\n\t\tif (\n\t\t\tbytes > limits.maxMessageBytes ||\n\t\t\tthis.pendingReceiveMessages >= limits.maxPendingInboundMessages ||\n\t\t\tthis.pendingReceiveBytes + bytes > limits.maxPendingInboundBytes\n\t\t) {\n\t\t\treturn this.protocolFailure(\n\t\t\t\tnew Error(\"Remote workspace inbound queue exceeds negotiated limits\"),\n\t\t\t\t\"message_too_large\",\n\t\t\t);\n\t\t}\n\t\tconst queuedPayload = typeof payload === \"string\" ? payload : Uint8Array.from(payload);\n\t\tthis.pendingReceiveMessages++;\n\t\tthis.pendingReceiveBytes += bytes;\n\t\tconst operation = this.receiveQueue.then(() => this.receivePayload(queuedPayload));\n\t\tconst tracked = operation.finally(() => {\n\t\t\tthis.pendingReceiveMessages--;\n\t\t\tthis.pendingReceiveBytes -= bytes;\n\t\t});\n\t\tthis.receiveQueue = tracked.catch(() => undefined);\n\t\treturn tracked;\n\t}\n\n\tasync disconnect(message = \"Remote workspace connection closed\"): Promise<void> {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tthis.onDisconnect?.(message);\n\t\tthis.failHandshake(new RemoteWorkspaceDisconnectedError(message, \"not_started\"));\n\t\tfor (const [id, pending] of this.pending) {\n\t\t\tconst state = pending.kind === \"read\" ? \"not_started\" : \"indeterminate\";\n\t\t\tthis.rejectPending(id, new RemoteWorkspaceDisconnectedError(message, state), true);\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.closed) return;\n\t\tawait this.closeWithReason({ code: \"normal\", message: \"Client closed\" });\n\t}\n\n\tprivate async decodeServerMessage(payload: string | Uint8Array): Promise<RemoteWorkspaceServerMessage | undefined> {\n\t\ttry {\n\t\t\tconst limits = this.negotiated?.limits ?? this.receiveLimits;\n\t\t\tconst decoded = decodeRemoteWorkspaceMessage(payload, limits);\n\t\t\treturn parseRemoteWorkspaceServerMessage(decoded, limits);\n\t\t} catch (error) {\n\t\t\tawait this.protocolFailure(error, classifyValidationClose(error));\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async handleHandshakeAck(message: RemoteWorkspaceHandshakeAck): Promise<void> {\n\t\tif (!this.handshakeId || message.id !== this.handshakeId || !this.handshakeOffer || !this.handshakeResolve) {\n\t\t\tawait this.protocolFailure(new Error(\"Unexpected handshake response\"));\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst ack = validateRemoteWorkspaceHandshakeAck(message, this.handshakeOffer, this.localToolSchemas);\n\t\t\tthis.negotiated = cloneHandshakeAck(ack);\n\t\t\tconst resolve = this.handshakeResolve;\n\t\t\tthis.clearHandshake();\n\t\t\tthis.addTombstone(message.id);\n\t\t\tresolve(cloneHandshakeAck(ack));\n\t\t} catch (error) {\n\t\t\tthis.failHandshake(error instanceof Error ? error : new Error(String(error)));\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: \"Invalid handshake response\" });\n\t\t}\n\t}\n\n\tprivate async handleCatalogChanged(message: RemoteWorkspaceCatalogChanged): Promise<void> {\n\t\tif (!this.negotiated?.capabilities.includes(\"catalog_refresh\")) {\n\t\t\tawait this.protocolFailure(new Error(\"Unnegotiated catalog event\"));\n\t\t\treturn;\n\t\t}\n\t\tthis.catalogRefreshing = true;\n\t\ttry {\n\t\t\tthis.onCatalogChanged?.(message);\n\t\t} catch (error) {\n\t\t\tawait this.protocolFailure(error);\n\t\t\treturn;\n\t\t}\n\t\tthis.latestCatalogEvent = message;\n\t\tthis.scheduleCatalogRefresh();\n\t}\n\n\tprivate handleHandshakeError(message: RemoteWorkspaceErrorMessage): boolean {\n\t\tif (!this.handshakeId || message.id !== this.handshakeId) return false;\n\t\tthis.addTombstone(message.id);\n\t\tthis.failHandshake(new RemoteWorkspaceRequestError(message.error));\n\t\treturn true;\n\t}\n\n\tprivate async findPendingRequest(message: RemoteWorkspaceRequestMessage): Promise<ClientPendingRequest | undefined> {\n\t\tconst pending = this.pending.get(message.id);\n\t\tif (pending) return pending;\n\t\tthis.pruneTombstones();\n\t\tif (!this.tombstones.has(message.id)) {\n\t\t\tawait this.protocolFailure(new Error(`Response for unknown request ID: ${message.id}`));\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate async handleRequestUpdate(message: RemoteWorkspaceUpdate, pending: ClientPendingRequest): Promise<void> {\n\t\tif (!this.negotiated?.capabilities.includes(\"tool_updates\") || message.sequence !== pending.nextUpdateSequence) {\n\t\t\tawait this.protocolFailure(new Error(`Out-of-order or unnegotiated update for request ${message.id}`));\n\t\t\treturn;\n\t\t}\n\t\tpending.nextUpdateSequence++;\n\t\ttry {\n\t\t\tawait pending.onUpdate?.(message.update);\n\t\t} catch (error) {\n\t\t\tawait this.failClientCallback(message.id, error);\n\t\t}\n\t}\n\n\tprivate async handleTransferStart(\n\t\tmessage: RemoteWorkspaceTransferStart,\n\t\tpending: ClientPendingRequest,\n\t): Promise<void> {\n\t\tif (\n\t\t\t(pending.method !== \"transfer.download\" && pending.method !== \"artifact.read\") ||\n\t\t\tpending.download ||\n\t\t\tmessage.length > (this.negotiated?.limits.maxTransferBytes ?? 0)\n\t\t) {\n\t\t\tawait this.protocolFailure(new Error(`Invalid transfer start for ${pending.method}`));\n\t\t\treturn;\n\t\t}\n\t\tpending.download = {\n\t\t\texpectedLength: message.length,\n\t\t\texpectedSha256: message.sha256,\n\t\t\tbytes: 0,\n\t\t\tchunks: 0,\n\t\t\thash: createHash(\"sha256\"),\n\t\t};\n\t\ttry {\n\t\t\tawait pending.onTransferStart?.({ length: message.length, sha256: message.sha256 });\n\t\t} catch (error) {\n\t\t\tawait this.failClientCallback(message.id, error);\n\t\t}\n\t}\n\n\tprivate async handleTransferChunk(\n\t\tmessage: RemoteWorkspaceTransferChunk,\n\t\tpending: ClientPendingRequest,\n\t): Promise<void> {\n\t\tconst download = pending.download;\n\t\tif (!download || message.sequence !== pending.nextIncomingTransferSequence) {\n\t\t\tawait this.protocolFailure(new Error(`Out-of-order transfer chunk for request ${message.id}`));\n\t\t\treturn;\n\t\t}\n\t\tconst limits = this.negotiated?.limits ?? this.receiveLimits;\n\t\tlet chunk: Buffer;\n\t\ttry {\n\t\t\tchunk = decodeCanonicalBase64(message.dataBase64, limits.maxTransferChunkBytes);\n\t\t} catch (error) {\n\t\t\tawait this.protocolFailure(error, \"invalid_payload\");\n\t\t\treturn;\n\t\t}\n\t\tif (\n\t\t\tdownload.chunks + 1 > limits.maxTransferChunks ||\n\t\t\tdownload.bytes + chunk.byteLength > limits.maxTransferBytes ||\n\t\t\tdownload.bytes + chunk.byteLength > download.expectedLength\n\t\t) {\n\t\t\tawait this.protocolFailure(\n\t\t\t\tnew Error(`Transfer exceeds declared or negotiated limits for request ${message.id}`),\n\t\t\t\t\"message_too_large\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tdownload.chunks++;\n\t\tdownload.bytes += chunk.byteLength;\n\t\tdownload.hash.update(chunk);\n\t\tpending.nextIncomingTransferSequence++;\n\t\ttry {\n\t\t\tawait pending.onTransferChunk?.(chunk);\n\t\t} catch (error) {\n\t\t\tawait this.failClientCallback(message.id, error);\n\t\t}\n\t}\n\n\tprivate async handleRequestResult(message: RemoteWorkspaceResult, pending: ClientPendingRequest): Promise<void> {\n\t\ttry {\n\t\t\tconst limits = this.negotiated?.limits ?? this.receiveLimits;\n\t\t\tconst result = parseRemoteWorkspaceResult(pending.method, message.result, limits);\n\t\t\tassertResultWorkspaceIdentity(pending.method, result, this.negotiated!.workspace);\n\t\t\tthis.verifyClientTransferResult(pending, result);\n\t\t\tthis.resolvePending(message.id, result);\n\t\t} catch (error) {\n\t\t\tawait this.protocolFailure(error);\n\t\t}\n\t}\n\n\tprivate async handlePendingMessage(\n\t\tmessage: RemoteWorkspaceRequestMessage,\n\t\tpending: ClientPendingRequest,\n\t): Promise<void> {\n\t\tswitch (message.type) {\n\t\t\tcase \"update\":\n\t\t\t\tawait this.handleRequestUpdate(message, pending);\n\t\t\t\treturn;\n\t\t\tcase \"transfer_start\":\n\t\t\t\tawait this.handleTransferStart(message, pending);\n\t\t\t\treturn;\n\t\t\tcase \"transfer_chunk\":\n\t\t\t\tawait this.handleTransferChunk(message, pending);\n\t\t\t\treturn;\n\t\t\tcase \"error\":\n\t\t\t\tthis.rejectPending(message.id, new RemoteWorkspaceRequestError(message.error), true);\n\t\t\t\treturn;\n\t\t\tcase \"result\":\n\t\t\t\tawait this.handleRequestResult(message, pending);\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate async receivePayload(payload: string | Uint8Array): Promise<void> {\n\t\tif (this.closed) return;\n\t\tconst message = await this.decodeServerMessage(payload);\n\t\tif (!message) return;\n\t\tif (message.type === \"handshake_ack\") {\n\t\t\tawait this.handleHandshakeAck(message);\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"catalog_changed\") {\n\t\t\tawait this.handleCatalogChanged(message);\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"error\" && this.handleHandshakeError(message)) return;\n\t\tconst pending = await this.findPendingRequest(message);\n\t\tif (!pending) return;\n\t\tawait this.handlePendingMessage(message, pending);\n\t}\n\n\tprivate assertMethodAvailable(\n\t\tmethod: RemoteWorkspaceMethod,\n\t\tparams: unknown,\n\t\tack: RemoteWorkspaceHandshakeAck,\n\t): void {\n\t\tconst capabilities = new Set(ack.capabilities);\n\t\tconst capability = methodCapability(method);\n\t\tif (capability && !capabilities.has(capability)) {\n\t\t\tthrow new Error(`Remote workspace capability was not negotiated: ${capability}`);\n\t\t}\n\t\tif (method === \"artifact.read\" && !capabilities.has(\"file_transfer\")) {\n\t\t\tthrow new Error(\"Remote workspace artifact reads require file transfer capability\");\n\t\t}\n\t\tif (method !== \"catalog.get\" && method !== \"tool.invoke\" && !ack.catalog.operations.includes(method)) {\n\t\t\tthrow new Error(`Remote workspace operation is not available: ${method}`);\n\t\t}\n\t\tif (method !== \"tool.invoke\") return;\n\t\tconst invocation = params as { toolName: string; schemaHash: string };\n\t\tconst remoteTool = ack.catalog.tools.find((tool) => tool.name === invocation.toolName);\n\t\tconst localHash = this.localToolSchemas.get(invocation.toolName);\n\t\tif (!remoteTool || !localHash)\n\t\t\tthrow new Error(`Remote tool has no matching local canonical definition: ${invocation.toolName}`);\n\t\tfor (const featureFlag of remoteTool.featureFlags) {\n\t\t\tif (!capabilities.has(featureFlag)) {\n\t\t\t\tthrow new Error(`Remote tool requires an unnegotiated capability: ${featureFlag}`);\n\t\t\t}\n\t\t}\n\t\tif (remoteTool.schemaHash !== localHash || invocation.schemaHash !== localHash) {\n\t\t\tthrow new Error(`Remote tool schema does not match the local canonical definition: ${invocation.toolName}`);\n\t\t}\n\t}\n\n\tprivate requestKind(method: RemoteWorkspaceMethod): RemoteWorkspaceOperationKind {\n\t\treturn method === \"tool.invoke\" ? \"mutation\" : getRemoteWorkspaceMethodKind(method);\n\t}\n\n\tprivate createClientUploadState(params: unknown): ClientUploadState {\n\t\tconst upload = params as { length: number; sha256: string };\n\t\tif (upload.length > (this.negotiated?.limits.maxTransferBytes ?? 0)) {\n\t\t\tthrow new Error(\"Remote workspace upload exceeds negotiated total-byte limit\");\n\t\t}\n\t\treturn {\n\t\t\texpectedLength: upload.length,\n\t\t\texpectedSha256: upload.sha256,\n\t\t\tbytes: 0,\n\t\t\tpendingBytes: 0,\n\t\t\tchunks: 0,\n\t\t\tpendingChunks: 0,\n\t\t\thash: createHash(\"sha256\"),\n\t\t\tfinished: false,\n\t\t\tqueue: Promise.resolve(),\n\t\t};\n\t}\n\n\tprivate queueUploadChunk(id: string, chunk: Uint8Array): Promise<void> {\n\t\tconst pending = this.requireUploadRequest(id);\n\t\tconst upload = pending.upload!;\n\t\tconst limits = this.negotiated!.limits;\n\t\tif (chunk.byteLength > limits.maxTransferChunkBytes) {\n\t\t\tthrow new Error(\"Remote workspace transfer chunk exceeds limit\");\n\t\t}\n\t\tif (\n\t\t\tthis.pendingTransferBytes + chunk.byteLength > limits.maxPendingTransferBytes ||\n\t\t\tupload.pendingBytes + chunk.byteLength > limits.maxPendingTransferBytes ||\n\t\t\tupload.bytes + upload.pendingBytes + chunk.byteLength > limits.maxTransferBytes ||\n\t\t\tupload.bytes + upload.pendingBytes + chunk.byteLength > upload.expectedLength ||\n\t\t\tupload.chunks + upload.pendingChunks + 1 > limits.maxTransferChunks\n\t\t) {\n\t\t\tthrow new Error(\"Remote workspace pending upload exceeds negotiated limits\");\n\t\t}\n\t\tconst copiedChunk = Buffer.from(chunk);\n\t\tupload.pendingBytes += copiedChunk.byteLength;\n\t\tthis.pendingTransferBytes += copiedChunk.byteLength;\n\t\tupload.pendingChunks++;\n\t\tconst operation = upload.queue.then(async () => {\n\t\t\ttry {\n\t\t\t\tif (upload.finished) throw new Error(\"Remote workspace upload is already finished\");\n\t\t\t\tawait this.send({\n\t\t\t\t\ttype: \"transfer_chunk\",\n\t\t\t\t\tid,\n\t\t\t\t\tsequence: pending.nextOutgoingTransferSequence,\n\t\t\t\t\tdataBase64: copiedChunk.toString(\"base64\"),\n\t\t\t\t});\n\t\t\t\tpending.nextOutgoingTransferSequence++;\n\t\t\t\tupload.chunks++;\n\t\t\t\tupload.bytes += copiedChunk.byteLength;\n\t\t\t\tupload.hash.update(copiedChunk);\n\t\t\t} finally {\n\t\t\t\tupload.pendingBytes -= copiedChunk.byteLength;\n\t\t\t\tthis.pendingTransferBytes -= copiedChunk.byteLength;\n\t\t\t\tupload.pendingChunks--;\n\t\t\t}\n\t\t});\n\t\tupload.queue = operation.catch(() => undefined);\n\t\treturn operation.catch(async (error: unknown) => {\n\t\t\tif (error instanceof RemoteWorkspaceTransportError) await this.handleTransportFailure(error);\n\t\t\telse await this.requestCancellation(id, \"cancelled\", error instanceof Error ? error.message : String(error));\n\t\t\tthrow error;\n\t\t});\n\t}\n\n\tprivate queueUploadFinish(id: string, length: number, sha256: string): Promise<void> {\n\t\tconst pending = this.requireUploadRequest(id);\n\t\tconst upload = pending.upload!;\n\t\tconst operation = upload.queue.then(async () => {\n\t\t\tif (upload.finished) throw new Error(\"Remote workspace upload is already finished\");\n\t\t\tconst digest = upload.hash.digest(\"hex\");\n\t\t\tif (\n\t\t\t\tlength !== upload.expectedLength ||\n\t\t\t\tsha256 !== upload.expectedSha256 ||\n\t\t\t\tupload.bytes !== upload.expectedLength ||\n\t\t\t\tdigest !== upload.expectedSha256\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Remote workspace upload length or SHA-256 does not match its declaration\");\n\t\t\t}\n\t\t\tupload.finished = true;\n\t\t\tawait this.send({ type: \"transfer_finish\", id, length, sha256 });\n\t\t});\n\t\tupload.queue = operation.catch(() => undefined);\n\t\treturn operation.catch(async (error: unknown) => {\n\t\t\tif (error instanceof RemoteWorkspaceTransportError) await this.handleTransportFailure(error);\n\t\t\telse await this.requestCancellation(id, \"cancelled\", error instanceof Error ? error.message : String(error));\n\t\t\tthrow error;\n\t\t});\n\t}\n\n\tprivate verifyClientTransferResult(pending: ClientPendingRequest, result: unknown): void {\n\t\tif (pending.method === \"transfer.upload\") {\n\t\t\tconst upload = pending.upload;\n\t\t\tconst metadata = result as { length: number; sha256: string };\n\t\t\tif (\n\t\t\t\t!upload?.finished ||\n\t\t\t\tmetadata.length !== upload.expectedLength ||\n\t\t\t\tmetadata.sha256 !== upload.expectedSha256\n\t\t\t) {\n\t\t\t\tthrow new RemoteWorkspaceValidationError(\"Upload terminal result does not match the verified upload\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (pending.method !== \"transfer.download\" && pending.method !== \"artifact.read\") return;\n\t\tconst download = pending.download;\n\t\tconst metadata = result as { length: number; sha256: string };\n\t\tif (!download) throw new RemoteWorkspaceValidationError(\"Download completed without transfer metadata\");\n\t\tconst digest = download.hash.digest(\"hex\");\n\t\tif (\n\t\t\tdownload.bytes !== download.expectedLength ||\n\t\t\tdigest !== download.expectedSha256 ||\n\t\t\tmetadata.length !== download.expectedLength ||\n\t\t\tmetadata.sha256 !== download.expectedSha256\n\t\t) {\n\t\t\tthrow new RemoteWorkspaceValidationError(\"Download length or SHA-256 verification failed\");\n\t\t}\n\t}\n\n\tprivate scheduleCatalogRefresh(): void {\n\t\tif (this.catalogRefreshPromise || this.closed) return;\n\t\tthis.catalogRefreshPromise = Promise.resolve()\n\t\t\t.then(async () => {\n\t\t\t\twhile (this.latestCatalogEvent && !this.closed) {\n\t\t\t\t\tconst expected = this.latestCatalogEvent;\n\t\t\t\t\tthis.latestCatalogEvent = undefined;\n\t\t\t\t\tconst catalog = (await this.beginRequest(\"catalog.get\", {}).result) as RemoteWorkspaceCatalog;\n\t\t\t\t\tvalidateRemoteWorkspaceCatalog(catalog);\n\t\t\t\t\tvalidateRemoteWorkspaceLocalToolSchemas(catalog, this.localToolSchemas);\n\t\t\t\t\tconst catalogHash = hashRemoteWorkspaceCatalog(catalog);\n\t\t\t\t\tif (catalog.generation !== expected.generation || catalogHash !== expected.catalogHash) {\n\t\t\t\t\t\tif (this.latestCatalogEvent) continue;\n\t\t\t\t\t\tthrow new RemoteWorkspaceValidationError(\"Catalog refresh does not match the advertised generation\");\n\t\t\t\t\t}\n\t\t\t\t\tif (!this.negotiated) throw new Error(\"Remote workspace handshake disappeared during refresh\");\n\t\t\t\t\tthis.negotiated = { ...this.negotiated, catalog, catalogHash };\n\t\t\t\t\tawait this.onCatalogRefreshed?.(cloneCatalog(catalog));\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((error: unknown) => this.protocolFailure(error))\n\t\t\t.finally(() => {\n\t\t\t\tthis.catalogRefreshPromise = undefined;\n\t\t\t\tif (this.latestCatalogEvent && !this.closed) this.scheduleCatalogRefresh();\n\t\t\t\telse this.catalogRefreshing = false;\n\t\t\t});\n\t}\n\n\tprivate activeTransferCount(): number {\n\t\tlet count = 0;\n\t\tfor (const pending of this.pending.values()) if (isTransferMethod(pending.method)) count++;\n\t\treturn count;\n\t}\n\n\tprivate createId(): string {\n\t\treturn `${this.connectionPrefix}:${this.nextId++}`;\n\t}\n\n\tprivate requireUploadRequest(id: string): ClientPendingRequest {\n\t\tconst pending = this.pending.get(id);\n\t\tif (!pending?.upload || pending.method !== \"transfer.upload\") throw new Error(`No active upload request: ${id}`);\n\t\treturn pending;\n\t}\n\n\tprivate async requestCancellation(\n\t\tid: string,\n\t\tcode: RemoteWorkspaceCancellationErrorCode,\n\t\tmessage: string,\n\t): Promise<void> {\n\t\tconst pending = this.pending.get(id);\n\t\tif (!pending || pending.cancelSent) return;\n\t\tpending.cancelSent = true;\n\t\tconst cancellationMs = this.negotiated?.limits.maxCancellationMs ?? 1000;\n\t\tpending.cancelTimer = setTimeout(() => {\n\t\t\tconst executionState = pending.kind === \"read\" ? \"not_started\" : \"indeterminate\";\n\t\t\tthis.rejectPending(\n\t\t\t\tid,\n\t\t\t\tnew RemoteWorkspaceRequestError({ code, message, executionState, retryable: false }),\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\tvoid this.closeWithReason({\n\t\t\t\tcode: \"policy_violation\",\n\t\t\t\tmessage: `Remote workspace cancellation was not acknowledged: ${id}`,\n\t\t\t}).catch(() => this.disconnect(\"Remote workspace cancellation was not acknowledged\"));\n\t\t}, cancellationMs + 500);\n\t\ttry {\n\t\t\tconst sent = await settlesWithinDeadline(\n\t\t\t\tthis.send({ type: \"cancel\", id, reason: message }),\n\t\t\t\tcancellationMs + 250,\n\t\t\t);\n\t\t\tif (!sent) throw new RemoteWorkspaceTransportError(\"Cancellation send timed out\");\n\t\t} catch (error) {\n\t\t\tawait this.handleTransportFailure(error);\n\t\t\treturn;\n\t\t}\n\t\tif (!this.pending.has(id) && pending.cancelTimer) clearTimeout(pending.cancelTimer);\n\t}\n\n\tprivate async failClientCallback(id: string, error: unknown): Promise<void> {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconst executionState = this.pending.get(id)?.kind === \"read\" ? \"not_started\" : \"indeterminate\";\n\t\tawait this.requestCancellation(id, \"cancelled\", \"Client callback failed\");\n\t\tthis.rejectPending(\n\t\t\tid,\n\t\t\tnew RemoteWorkspaceRequestError({\n\t\t\t\tcode: \"internal_error\",\n\t\t\t\tmessage: `Remote workspace client callback failed: ${message}`,\n\t\t\t\texecutionState,\n\t\t\t\tretryable: false,\n\t\t\t}),\n\t\t\ttrue,\n\t\t);\n\t\tawait this.closeWithReason({\n\t\t\tcode: \"policy_violation\",\n\t\t\tmessage: `Remote workspace client callback failed for ${id}`,\n\t\t}).catch(() => this.disconnect(\"Remote workspace client callback failed\"));\n\t}\n\n\tprivate resolvePending(id: string, value: unknown): void {\n\t\tconst pending = this.takePending(id);\n\t\tif (pending) pending.resolve(value);\n\t}\n\n\tprivate rejectPending(id: string, error: Error, tombstone: boolean): void {\n\t\tconst pending = this.takePending(id, tombstone);\n\t\tif (pending) pending.reject(error);\n\t}\n\n\tprivate takePending(id: string, tombstone = true): ClientPendingRequest | undefined {\n\t\tconst pending = this.pending.get(id);\n\t\tif (!pending) return undefined;\n\t\tthis.pending.delete(id);\n\t\tclearTimeout(pending.deadlineTimer);\n\t\tif (pending.cancelTimer) clearTimeout(pending.cancelTimer);\n\t\tif (pending.signal && pending.onAbort) pending.signal.removeEventListener(\"abort\", pending.onAbort);\n\t\tif (tombstone) this.addTombstone(id);\n\t\treturn pending;\n\t}\n\n\tprivate addTombstone(id: string): void {\n\t\tthis.pruneTombstones();\n\t\tif (this.tombstones.size >= this.maxTombstones) {\n\t\t\tvoid this.protocolFailure(new Error(\"Remote workspace tombstone limit exceeded\"), \"policy_violation\").catch(\n\t\t\t\t() => undefined,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.tombstones.set(id, performance.now() + this.tombstoneMs);\n\t}\n\n\tprivate pruneTombstones(): void {\n\t\tconst now = performance.now();\n\t\tfor (const [id, expiresAt] of this.tombstones) if (expiresAt <= now) this.tombstones.delete(id);\n\t}\n\n\tprivate clearHandshake(): void {\n\t\tif (this.handshakeTimer) clearTimeout(this.handshakeTimer);\n\t\tthis.handshakeTimer = undefined;\n\t\tthis.handshakeId = undefined;\n\t\tthis.handshakeOffer = undefined;\n\t\tthis.handshakeResolve = undefined;\n\t\tthis.handshakeReject = undefined;\n\t}\n\n\tprivate failHandshake(error: Error): void {\n\t\tconst reject = this.handshakeReject;\n\t\tthis.clearHandshake();\n\t\treject?.(error);\n\t}\n\n\tprivate send(message: RemoteWorkspaceClientMessage): Promise<void> {\n\t\tconst limits = this.negotiated?.limits ?? this.receiveLimits;\n\t\tconst prepared = prepareClientOutbound(message, limits);\n\t\tconst estimatedBytes = prepared.bytes;\n\t\tif (\n\t\t\tthis.pendingOutboundMessages >= limits.maxPendingOutboundMessages ||\n\t\t\tthis.pendingOutboundBytes + estimatedBytes > limits.maxPendingOutboundBytes\n\t\t) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew RemoteWorkspaceValidationError(\"Remote workspace client outbound queue exceeds limits\"),\n\t\t\t);\n\t\t}\n\t\tthis.pendingOutboundMessages++;\n\t\tthis.pendingOutboundBytes += estimatedBytes;\n\t\tconst operation = this.sendQueue.then(async () => {\n\t\t\tif (this.closed) throw new RemoteWorkspaceTransportError(\"Remote workspace protocol client is closed\");\n\t\t\ttry {\n\t\t\t\tconst sent = await settlesWithinDeadline(this.transport.send(prepared.message), limits.maxTransportSendMs);\n\t\t\t\tif (!sent) throw new RemoteWorkspaceTransportError(\"Remote workspace client transport send timed out\");\n\t\t\t} catch (error) {\n\t\t\t\tthrow error instanceof RemoteWorkspaceTransportError ? error : new RemoteWorkspaceTransportError(error);\n\t\t\t}\n\t\t});\n\t\tconst tracked = operation.finally(() => {\n\t\t\tthis.pendingOutboundMessages--;\n\t\t\tthis.pendingOutboundBytes -= estimatedBytes;\n\t\t});\n\t\tthis.sendQueue = tracked.catch(() => undefined);\n\t\treturn tracked;\n\t}\n\n\tprivate async handleTransportFailure(error: unknown): Promise<void> {\n\t\tconst message = `Remote workspace transport failed: ${error instanceof Error ? error.message : String(error)}`;\n\t\ttry {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message });\n\t\t} catch {\n\t\t\tawait this.disconnect(message);\n\t\t}\n\t}\n\n\tprivate async protocolFailure(\n\t\terror: unknown,\n\t\tcode: RemoteWorkspaceProtocolCloseCode = \"protocol_error\",\n\t): Promise<void> {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\ttry {\n\t\t\tawait this.closeWithReason({ code, message });\n\t\t} catch {\n\t\t\tawait this.disconnect(message);\n\t\t}\n\t}\n\n\tprivate async closeWithReason(reason: RemoteWorkspaceProtocolCloseReason): Promise<void> {\n\t\tif (this.closed) return;\n\t\tawait this.disconnect(reason.message);\n\t\tconst sendDeadline = this.negotiated?.limits.maxTransportSendMs ?? this.receiveLimits.maxTransportSendMs;\n\t\tconst sendsStopped = await settlesWithinDeadline(this.sendQueue, sendDeadline);\n\t\tconst closed = await settlesWithinDeadline(\n\t\t\tPromise.resolve(this.transport.close(boundedCloseReason(reason))),\n\t\t\tsendDeadline,\n\t\t);\n\t\tif (!sendsStopped || !closed) {\n\t\t\tthrow new RemoteWorkspaceTerminationError(\"Remote workspace client transport shutdown timed out\");\n\t\t}\n\t}\n}\n\nexport interface RemoteWorkspaceServerRequestContext {\n\treadonly signal: AbortSignal;\n\treadonly request: RemoteWorkspaceTypedRequest;\n\tmarkSideEffectStarted(): void;\n\tmarkCommitted(): void;\n\tsendUpdate(update: unknown): Promise<void>;\n\tstartTransfer(length: number, sha256: string): Promise<void>;\n\tsendTransferChunk(chunk: Uint8Array): Promise<void>;\n}\n\nexport interface RemoteWorkspaceServerHandler {\n\thandleRequest(request: RemoteWorkspaceTypedRequest, context: RemoteWorkspaceServerRequestContext): Promise<unknown>;\n\thandleUploadChunk?(\n\t\trequest: RemoteWorkspaceUploadRequest,\n\t\tchunk: Buffer,\n\t\tsequence: number,\n\t\tcontext: RemoteWorkspaceServerRequestContext,\n\t): Promise<void>;\n\thandleUploadFinish?(\n\t\trequest: RemoteWorkspaceUploadRequest,\n\t\tmetadata: RemoteWorkspaceTransferMetadata,\n\t\tcontext: RemoteWorkspaceServerRequestContext,\n\t): Promise<void>;\n\tvalidateToolArguments(toolName: string, value: unknown): boolean;\n}\n\nexport interface RemoteWorkspaceServerProtocolOptions {\n\tworkspace: RemoteWorkspaceIdentity;\n\tcatalog: RemoteWorkspaceCatalog;\n\tcapabilities: readonly string[];\n\trequiredClientCapabilities?: readonly string[];\n\tversions?: readonly RemoteWorkspaceVersionRange[];\n\tlimits?: RemoteWorkspaceProtocolLimits;\n\tbackendLabel?: string;\n\tinvalidRequestStrikes?: number;\n\ttombstoneMs?: number;\n\tmaxTombstones?: number;\n}\n\ninterface ServerTransferState {\n\texpectedLength: number;\n\texpectedSha256: string;\n\tbytes: number;\n\tpendingBytes: number;\n\tchunks: number;\n\thash: Hash;\n\tfinishing: boolean;\n\tfinished: boolean;\n}\n\ninterface ServerActiveRequest {\n\trequest: RemoteWorkspaceTypedRequest;\n\tcontroller: AbortController;\n\tcontext: RemoteWorkspaceServerRequestContext;\n\ttimer: NodeJS.Timeout;\n\tkind: RemoteWorkspaceOperationKind;\n\tsideEffectStarted: boolean;\n\tcommitted: boolean;\n\tnextUpdateSequence: number;\n\tnextIncomingTransferSequence: number;\n\tnextOutgoingTransferSequence: number;\n\tpendingOutgoingTransferBytes: number;\n\tincomingTransfer?: ServerTransferState;\n\toutgoingTransfer?: ServerTransferState;\n\tinboundQueue: Promise<void>;\n\toutboundQueue: Promise<void>;\n\thandlerSettled: Promise<void>;\n\tresolveHandlerSettled: () => void;\n\thandlerDone: boolean;\n\tcancellationError?: RemoteWorkspaceProtocolError;\n\tsettled: boolean;\n}\n\nexport class RemoteWorkspaceServerProtocol {\n\tprivate readonly transport: RemoteWorkspaceProtocolTransport<RemoteWorkspaceServerMessage>;\n\tprivate readonly handler: RemoteWorkspaceServerHandler;\n\tprivate readonly workspace: RemoteWorkspaceIdentity;\n\tprivate readonly negotiation: RemoteWorkspaceNegotiationOptions;\n\tprivate readonly backendLabel: string | undefined;\n\tprivate readonly invalidRequestStrikes: number;\n\tprivate readonly tombstoneMs: number;\n\tprivate readonly maxTombstones: number;\n\tprivate readonly active = new Map<string, ServerActiveRequest>();\n\tprivate readonly tombstones = new Map<string, number>();\n\tprivate catalog: RemoteWorkspaceCatalog;\n\tprivate catalogHash: string;\n\tprivate limits: RemoteWorkspaceProtocolLimits;\n\tprivate negotiatedCapabilities = new Set<string>();\n\tprivate receiveQueue: Promise<void> = Promise.resolve();\n\tprivate sendQueue: Promise<void> = Promise.resolve();\n\tprivate disconnectPromise: Promise<void> | undefined;\n\tprivate transportFailurePromise: Promise<void> | undefined;\n\tprivate pendingReceiveBytes = 0;\n\tprivate pendingReceiveMessages = 0;\n\tprivate pendingOutboundBytes = 0;\n\tprivate pendingOutboundMessages = 0;\n\tprivate pendingTransferBytes = 0;\n\tprivate handshaken = false;\n\tprivate closed = false;\n\tprivate draining = false;\n\tprivate strikes = 0;\n\n\tconstructor(\n\t\ttransport: RemoteWorkspaceProtocolTransport<RemoteWorkspaceServerMessage>,\n\t\thandler: RemoteWorkspaceServerHandler,\n\t\toptions: RemoteWorkspaceServerProtocolOptions,\n\t) {\n\t\tconst catalog = cloneCatalog(options.catalog);\n\t\tthis.validateCatalogCapabilities(catalog, new Set(options.capabilities));\n\t\tthis.transport = transport;\n\t\tthis.handler = handler;\n\t\tthis.workspace = cloneWorkspaceIdentity(options.workspace);\n\t\tthis.catalog = catalog;\n\t\tthis.catalogHash = hashRemoteWorkspaceCatalog(catalog);\n\t\tthis.limits = {\n\t\t\t...validateRemoteWorkspaceProtocolLimits(options.limits ?? DEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS),\n\t\t};\n\t\tthis.negotiation = {\n\t\t\tserverVersions: options.versions ?? REMOTE_WORKSPACE_PROTOCOL_VERSIONS,\n\t\t\tserverCapabilities: options.capabilities,\n\t\t\trequiredClientCapabilities: options.requiredClientCapabilities,\n\t\t\tserverLimits: this.limits,\n\t\t};\n\t\tthis.backendLabel = options.backendLabel;\n\t\tthis.invalidRequestStrikes = validateConfiguredInteger(\n\t\t\toptions.invalidRequestStrikes ?? 3,\n\t\t\t\"invalidRequestStrikes\",\n\t\t\t1,\n\t\t\t100,\n\t\t);\n\t\tthis.tombstoneMs = validateConfiguredInteger(\n\t\t\toptions.tombstoneMs ?? 5 * 60 * 1000,\n\t\t\t\"tombstoneMs\",\n\t\t\t1,\n\t\t\t24 * 60 * 60 * 1000,\n\t\t);\n\t\tthis.maxTombstones = validateConfiguredInteger(options.maxTombstones ?? 65_536, \"maxTombstones\", 1, 1_000_000);\n\t}\n\n\tget handshakeComplete(): boolean {\n\t\treturn this.handshaken;\n\t}\n\n\treceive(payload: string | Uint8Array): Promise<void> {\n\t\tif (this.closed) return Promise.resolve();\n\t\tconst bytes = payloadByteLength(payload);\n\t\tif (\n\t\t\tbytes > this.limits.maxMessageBytes ||\n\t\t\tthis.pendingReceiveMessages >= this.limits.maxPendingInboundMessages ||\n\t\t\tthis.pendingReceiveBytes + bytes > this.limits.maxPendingInboundBytes\n\t\t) {\n\t\t\treturn this.closeWithReason({ code: \"message_too_large\", message: \"Inbound protocol queue exceeds limits\" });\n\t\t}\n\t\tconst queuedPayload = typeof payload === \"string\" ? payload : Uint8Array.from(payload);\n\t\tthis.pendingReceiveMessages++;\n\t\tthis.pendingReceiveBytes += bytes;\n\t\tconst operation = this.receiveQueue.then(() => this.receivePayload(queuedPayload));\n\t\tconst tracked = operation.finally(() => {\n\t\t\tthis.pendingReceiveMessages--;\n\t\t\tthis.pendingReceiveBytes -= bytes;\n\t\t});\n\t\tthis.receiveQueue = tracked.catch(() => undefined);\n\t\treturn tracked;\n\t}\n\n\tpublishCatalog(catalog: RemoteWorkspaceCatalog): Promise<void> {\n\t\tif (this.closed) return Promise.reject(new Error(\"Remote workspace protocol server is closed\"));\n\t\tconst clonedCatalog = cloneCatalog(catalog);\n\t\tconst operation = this.receiveQueue.then(() => this.publishCatalogNow(clonedCatalog));\n\t\tthis.receiveQueue = operation.catch(() => undefined);\n\t\treturn operation;\n\t}\n\n\tprivate async publishCatalogNow(catalog: RemoteWorkspaceCatalog): Promise<void> {\n\t\tif (this.closed) throw new Error(\"Remote workspace protocol server is closed\");\n\t\tthis.validateCatalogCapabilities(catalog, new Set(this.negotiation.serverCapabilities));\n\t\tif (catalog.generation <= this.catalog.generation)\n\t\t\tthrow new Error(\"Remote workspace catalog generation must increase\");\n\t\tfor (const [id, active] of Array.from(this.active)) {\n\t\t\tif (active.kind !== \"process\" && active.kind !== \"service\") continue;\n\t\t\tawait this.cancelActive(\n\t\t\t\tid,\n\t\t\t\tthis.createError(\"generation_retired\", \"Remote workspace generation retired\", \"not_started\", false),\n\t\t\t\ttrue,\n\t\t\t);\n\t\t}\n\t\tthis.catalog = catalog;\n\t\tthis.catalogHash = hashRemoteWorkspaceCatalog(catalog);\n\t\tif (this.handshaken && this.negotiatedCapabilities.has(\"catalog_refresh\")) {\n\t\t\tawait this.send({ type: \"catalog_changed\", generation: catalog.generation, catalogHash: this.catalogHash });\n\t\t}\n\t}\n\n\tasync beginDrain(message = \"Remote workspace connection is draining\"): Promise<void> {\n\t\tif (this.closed) return;\n\t\tthis.draining = true;\n\t\tawait Promise.all(\n\t\t\tArray.from(this.active.keys()).map((id) =>\n\t\t\t\tthis.cancelActive(id, this.createError(\"connection_draining\", message, \"not_started\", false), false),\n\t\t\t),\n\t\t);\n\t}\n\n\tdisconnect(): Promise<void> {\n\t\treturn this.disconnectInternal(true);\n\t}\n\n\tprivate disconnectInternal(observeReceiveQueue: boolean): Promise<void> {\n\t\tif (this.disconnectPromise) return this.disconnectPromise;\n\t\tif (this.closed) return Promise.resolve();\n\t\tthis.closed = true;\n\t\tconst activeRequests = Array.from(this.active.values());\n\t\tfor (const active of activeRequests) {\n\t\t\tclearTimeout(active.timer);\n\t\t\tactive.controller.abort();\n\t\t}\n\t\tconst queuedSends = this.sendQueue;\n\t\tconst queuedReceives = this.receiveQueue;\n\t\tthis.disconnectPromise = Promise.all([\n\t\t\t...activeRequests.map(async (active) => {\n\t\t\t\tconst termination = Promise.all([active.handlerSettled, active.inboundQueue, active.outboundQueue]).then(\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t\tconst stopped = await this.settlesWithin(termination, this.limits.maxCancellationMs);\n\t\t\t\tactive.settled = true;\n\t\t\t\tthis.active.delete(active.request.id);\n\t\t\t\treturn stopped;\n\t\t\t}),\n\t\t\tsettlesWithinDeadline(queuedSends, this.limits.maxTransportSendMs),\n\t\t\t...(observeReceiveQueue ? [settlesWithinDeadline(queuedReceives, this.limits.maxCancellationMs)] : []),\n\t\t]).then((results) => {\n\t\t\tif (results.some((stopped) => !stopped)) {\n\t\t\t\tthrow new RemoteWorkspaceTerminationError(\n\t\t\t\t\t\"Remote workspace handlers did not terminate before the disconnect deadline\",\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\treturn this.disconnectPromise;\n\t}\n\n\tprivate async receivePayload(payload: string | Uint8Array): Promise<void> {\n\t\tif (this.closed) return;\n\t\tlet decoded: unknown;\n\t\ttry {\n\t\t\tdecoded = decodeRemoteWorkspaceMessage(payload, this.limits);\n\t\t} catch (error) {\n\t\t\tawait this.closeWithReason({ code: classifyValidationClose(error), message: this.errorMessage(error) });\n\t\t\treturn;\n\t\t}\n\t\tlet message: RemoteWorkspaceClientMessage;\n\t\ttry {\n\t\t\tmessage = parseRemoteWorkspaceClientMessage(decoded, this.limits);\n\t\t} catch (error) {\n\t\t\tawait this.handleInvalidMessage(decoded, error);\n\t\t\treturn;\n\t\t}\n\t\tif (!this.handshaken) {\n\t\t\tif (message.type !== \"handshake\") {\n\t\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: \"Request received before handshake\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.handleHandshake(message);\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"handshake\") {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: \"Duplicate handshake\" });\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"cancel\") {\n\t\t\tawait this.handleCancel(message.id);\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"transfer_chunk\") {\n\t\t\tawait this.handleUploadChunk(message.id, message.sequence, message.dataBase64);\n\t\t\treturn;\n\t\t}\n\t\tif (message.type === \"transfer_finish\") {\n\t\t\tawait this.handleUploadFinish(message.id, message.length, message.sha256);\n\t\t\treturn;\n\t\t}\n\t\tawait this.handleRequest(message);\n\t}\n\tprivate async handleRequestValidationFailure(requestId: string, error: unknown): Promise<void> {\n\t\tthis.addTombstone(requestId);\n\t\tconst semantic = error instanceof RemoteWorkspaceRequestError;\n\t\tif (!semantic) this.strikes++;\n\t\tconst protocolError = semantic\n\t\t\t? {\n\t\t\t\t\tcode: error.code,\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t\texecutionState: error.executionState,\n\t\t\t\t\tretryable: error.retryable,\n\t\t\t\t\t...(error.details === undefined ? {} : { details: error.details }),\n\t\t\t\t}\n\t\t\t: this.createError(\"invalid_request\", this.errorMessage(error), \"not_started\", false);\n\t\tawait this.send({ type: \"error\", id: requestId, error: protocolError });\n\t\tif (this.strikes >= this.invalidRequestStrikes) {\n\t\t\tawait this.closeWithReason({ code: \"policy_violation\", message: \"Too many invalid requests\" });\n\t\t}\n\t}\n\n\tprivate async handleHandshake(handshake: RemoteWorkspaceHandshake): Promise<void> {\n\t\tif (this.isDuplicateId(handshake.id)) {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Duplicate request ID: ${handshake.id}` });\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst negotiated = negotiateRemoteWorkspaceHandshake(handshake, this.negotiation);\n\t\t\tthis.limits = negotiated.limits;\n\t\t\tthis.negotiatedCapabilities = new Set(negotiated.capabilities);\n\t\t\tthis.handshaken = true;\n\t\t\tthis.addTombstone(handshake.id);\n\t\t\tawait this.send({\n\t\t\t\ttype: \"handshake_ack\",\n\t\t\t\tid: handshake.id,\n\t\t\t\tversion: negotiated.version,\n\t\t\t\tcapabilities: negotiated.capabilities,\n\t\t\t\tworkspace: this.workspace,\n\t\t\t\tlimits: negotiated.limits,\n\t\t\t\tcatalog: this.catalog,\n\t\t\t\tcatalogHash: this.catalogHash,\n\t\t\t\tbackendMetadata: {\n\t\t\t\t\tkind: \"remote-workspace\",\n\t\t\t\t\t...(this.backendLabel ? { label: this.backendLabel } : {}),\n\t\t\t\t},\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst protocolError =\n\t\t\t\terror instanceof RemoteWorkspaceNegotiationError\n\t\t\t\t\t? this.createError(error.code, error.message, \"not_started\", false)\n\t\t\t\t\t: this.createError(\"invalid_request\", this.errorMessage(error), \"not_started\", false);\n\t\t\tawait this.send({ type: \"error\", id: handshake.id, error: protocolError }).catch(() => undefined);\n\t\t\tawait this.closeWithReason({ code: \"policy_violation\", message: protocolError.message });\n\t\t}\n\t}\n\n\tprivate async handleRequest(request: RemoteWorkspaceRequest): Promise<void> {\n\t\tif (this.isDuplicateId(request.id)) {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Duplicate request ID: ${request.id}` });\n\t\t\treturn;\n\t\t}\n\t\tif (this.draining) {\n\t\t\tthis.addTombstone(request.id);\n\t\t\tawait this.sendError(request.id, \"connection_draining\", \"Connection is draining\", \"not_started\", false);\n\t\t\treturn;\n\t\t}\n\t\tif (!isRemoteWorkspaceMethod(request.method)) {\n\t\t\tthis.addTombstone(request.id);\n\t\t\tawait this.sendError(\n\t\t\t\trequest.id,\n\t\t\t\t\"method_not_supported\",\n\t\t\t\t`Unsupported method: ${request.method}`,\n\t\t\t\t\"not_started\",\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst typedRequest = request as RemoteWorkspaceTypedRequest;\n\t\ttry {\n\t\t\tparseRemoteWorkspaceRequestParams(typedRequest.method, typedRequest.params, this.limits);\n\t\t\tthis.assertMethodAvailable(typedRequest);\n\t\t\tthis.validateCatalogRequest(typedRequest);\n\t\t} catch (error) {\n\t\t\tawait this.handleRequestValidationFailure(request.id, error);\n\t\t\treturn;\n\t\t}\n\t\tif (this.active.size >= this.limits.maxActiveRequests) {\n\t\t\tthis.addTombstone(request.id);\n\t\t\tawait this.sendError(request.id, \"limit_exceeded\", \"Active request limit reached\", \"not_started\", true);\n\t\t\treturn;\n\t\t}\n\t\tif (isTransferMethod(typedRequest.method) && this.activeTransferCount() >= this.limits.maxActiveTransfers) {\n\t\t\tthis.addTombstone(request.id);\n\t\t\tawait this.sendError(request.id, \"limit_exceeded\", \"Active transfer limit reached\", \"not_started\", true);\n\t\t\treturn;\n\t\t}\n\t\tconst controller = new AbortController();\n\t\tconst active = this.createActiveRequest(\n\t\t\ttypedRequest,\n\t\t\tcontroller,\n\t\t\tMath.min(request.timeoutMs, this.limits.maxRequestMs),\n\t\t);\n\t\tthis.active.set(request.id, active);\n\t\tif (typedRequest.method === \"catalog.get\") {\n\t\t\tawait this.completeActive(request.id, this.catalog);\n\t\t\treturn;\n\t\t}\n\t\tthis.startHandler(active);\n\t}\n\n\tprivate createActiveRequest(\n\t\trequest: RemoteWorkspaceTypedRequest,\n\t\tcontroller: AbortController,\n\t\ttimeoutMs: number,\n\t): ServerActiveRequest {\n\t\tlet active: ServerActiveRequest;\n\t\tconst context: RemoteWorkspaceServerRequestContext = {\n\t\t\tsignal: controller.signal,\n\t\t\trequest,\n\t\t\tmarkSideEffectStarted: () => {\n\t\t\t\tif (!active.settled) active.sideEffectStarted = true;\n\t\t\t},\n\t\t\tmarkCommitted: () => {\n\t\t\t\tif (!active.settled) {\n\t\t\t\t\tactive.sideEffectStarted = true;\n\t\t\t\t\tactive.committed = true;\n\t\t\t\t}\n\t\t\t},\n\t\t\tsendUpdate: (update) => {\n\t\t\t\tif (!this.negotiatedCapabilities.has(\"tool_updates\")) {\n\t\t\t\t\treturn Promise.reject(\n\t\t\t\t\t\tnew RemoteWorkspaceRequestError(\n\t\t\t\t\t\t\tthis.createError(\"not_available\", \"Tool updates were not negotiated\", \"not_started\", false),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlet prepared: PreparedProtocolMessage<RemoteWorkspaceServerMessage>;\n\t\t\t\ttry {\n\t\t\t\t\tprepared = prepareServerOutbound(\n\t\t\t\t\t\t{ type: \"update\", id: request.id, sequence: active.nextUpdateSequence, update },\n\t\t\t\t\t\tthis.limits,\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn Promise.reject(\n\t\t\t\t\t\tnew RemoteWorkspaceRequestError(\n\t\t\t\t\t\t\tthis.createError(\n\t\t\t\t\t\t\t\t\"result_too_large\",\n\t\t\t\t\t\t\t\tthis.errorMessage(error),\n\t\t\t\t\t\t\t\tthis.executionStateFromActive(active),\n\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst queued = this.queueActiveOutbound(active, prepared.bytes, async () => {\n\t\t\t\t\t\tif (!active.settled) await this.sendPrepared(prepared, true);\n\t\t\t\t\t});\n\t\t\t\t\tactive.nextUpdateSequence++;\n\t\t\t\t\treturn queued;\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn Promise.reject(error);\n\t\t\t\t}\n\t\t\t},\n\t\t\tstartTransfer: (length, sha256) => {\n\t\t\t\tconst prepared = prepareServerOutbound(\n\t\t\t\t\t{ type: \"transfer_start\", id: request.id, length, sha256 },\n\t\t\t\t\tthis.limits,\n\t\t\t\t);\n\t\t\t\treturn this.queueActiveOutbound(active, prepared.bytes, async () => {\n\t\t\t\t\tif (active.settled || active.outgoingTransfer)\n\t\t\t\t\t\tthrow new Error(\"Transfer has already started or settled\");\n\t\t\t\t\tif (!this.negotiatedCapabilities.has(\"file_transfer\"))\n\t\t\t\t\t\tthrow new Error(\"File transfer was not negotiated\");\n\t\t\t\t\tif (length > this.limits.maxTransferBytes) throw new Error(\"Transfer exceeds total-byte limit\");\n\t\t\t\t\tactive.outgoingTransfer = {\n\t\t\t\t\t\texpectedLength: length,\n\t\t\t\t\t\texpectedSha256: sha256,\n\t\t\t\t\t\tbytes: 0,\n\t\t\t\t\t\tpendingBytes: 0,\n\t\t\t\t\t\tchunks: 0,\n\t\t\t\t\t\thash: createHash(\"sha256\"),\n\t\t\t\t\t\tfinishing: false,\n\t\t\t\t\t\tfinished: false,\n\t\t\t\t\t};\n\t\t\t\t\tawait this.sendPrepared(prepared, true);\n\t\t\t\t});\n\t\t\t},\n\t\t\tsendTransferChunk: (chunk) => {\n\t\t\t\tif (chunk.byteLength > this.limits.maxTransferChunkBytes) {\n\t\t\t\t\treturn Promise.reject(new Error(\"Transfer chunk exceeds limit\"));\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\tactive.pendingOutgoingTransferBytes + chunk.byteLength > this.limits.maxPendingTransferBytes ||\n\t\t\t\t\tthis.pendingTransferBytes + chunk.byteLength > this.limits.maxPendingTransferBytes\n\t\t\t\t) {\n\t\t\t\t\treturn Promise.reject(new Error(\"Pending transfer chunks exceed limit\"));\n\t\t\t\t}\n\t\t\t\tconst copiedChunk = Buffer.from(chunk);\n\t\t\t\tconst prepared = prepareServerOutbound(\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"transfer_chunk\",\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\tsequence: active.nextOutgoingTransferSequence,\n\t\t\t\t\t\tdataBase64: copiedChunk.toString(\"base64\"),\n\t\t\t\t\t},\n\t\t\t\t\tthis.limits,\n\t\t\t\t);\n\t\t\t\tactive.pendingOutgoingTransferBytes += copiedChunk.byteLength;\n\t\t\t\tthis.pendingTransferBytes += copiedChunk.byteLength;\n\t\t\t\ttry {\n\t\t\t\t\tconst queued = this.queueActiveOutbound(active, prepared.bytes, async () => {\n\t\t\t\t\t\tconst transfer = active.outgoingTransfer;\n\t\t\t\t\t\tif (active.settled || !transfer || transfer.finished)\n\t\t\t\t\t\t\tthrow new Error(\"Transfer has not started or has settled\");\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttransfer.chunks + 1 > this.limits.maxTransferChunks ||\n\t\t\t\t\t\t\ttransfer.bytes + copiedChunk.byteLength > this.limits.maxTransferBytes ||\n\t\t\t\t\t\t\ttransfer.bytes + copiedChunk.byteLength > transfer.expectedLength\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthrow new Error(\"Transfer exceeds declared or negotiated limits\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait this.sendPrepared(prepared, true);\n\t\t\t\t\t\ttransfer.chunks++;\n\t\t\t\t\t\ttransfer.bytes += copiedChunk.byteLength;\n\t\t\t\t\t\ttransfer.hash.update(copiedChunk);\n\t\t\t\t\t});\n\t\t\t\t\tactive.nextOutgoingTransferSequence++;\n\t\t\t\t\treturn queued.finally(() => {\n\t\t\t\t\t\tactive.pendingOutgoingTransferBytes -= copiedChunk.byteLength;\n\t\t\t\t\t\tthis.pendingTransferBytes -= copiedChunk.byteLength;\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tactive.pendingOutgoingTransferBytes -= copiedChunk.byteLength;\n\t\t\t\t\tthis.pendingTransferBytes -= copiedChunk.byteLength;\n\t\t\t\t\treturn Promise.reject(error);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t\tlet resolveHandlerSettled: () => void = () => undefined;\n\t\tconst handlerSettled = new Promise<void>((resolve) => {\n\t\t\tresolveHandlerSettled = resolve;\n\t\t});\n\t\tconst timer = setTimeout(() => {\n\t\t\tvoid this.cancelActive(\n\t\t\t\trequest.id,\n\t\t\t\tthis.createError(\"deadline_exceeded\", \"Remote workspace request deadline exceeded\", \"not_started\", false),\n\t\t\t\tfalse,\n\t\t\t).catch(() => undefined);\n\t\t}, timeoutMs);\n\t\tactive = {\n\t\t\trequest,\n\t\t\tcontroller,\n\t\t\tcontext,\n\t\t\ttimer,\n\t\t\tkind: this.requestKind(request),\n\t\t\tsideEffectStarted: false,\n\t\t\tcommitted: false,\n\t\t\tnextUpdateSequence: 0,\n\t\t\tnextIncomingTransferSequence: 0,\n\t\t\tnextOutgoingTransferSequence: 0,\n\t\t\tpendingOutgoingTransferBytes: 0,\n\t\t\toutboundQueue: Promise.resolve(),\n\t\t\tinboundQueue: Promise.resolve(),\n\t\t\thandlerSettled,\n\t\t\tresolveHandlerSettled,\n\t\t\thandlerDone: false,\n\t\t\t...(request.method === \"transfer.upload\"\n\t\t\t\t? { incomingTransfer: this.createServerUploadState(request.params) }\n\t\t\t\t: {}),\n\t\t\tsettled: false,\n\t\t};\n\t\treturn active;\n\t}\n\n\tprivate startHandler(active: ServerActiveRequest): void {\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst result = await this.handler.handleRequest(active.request, active.context);\n\t\t\t\tactive.handlerDone = true;\n\t\t\t\tactive.resolveHandlerSettled();\n\t\t\t\tif (active.settled) return;\n\t\t\t\tif (active.cancellationError) return;\n\t\t\t\tawait this.completeActive(active.request.id, result);\n\t\t\t} catch (error) {\n\t\t\t\tactive.handlerDone = true;\n\t\t\t\tactive.resolveHandlerSettled();\n\t\t\t\tif (active.settled) return;\n\t\t\t\tif (active.cancellationError) return;\n\t\t\t\tawait this.handleHandlerFailure(active.request.id, error);\n\t\t\t}\n\t\t})();\n\t}\n\n\tprivate queueActiveOutbound<T>(\n\t\tactive: ServerActiveRequest,\n\t\testimatedBytes: number,\n\t\toperation: () => Promise<T>,\n\t): Promise<T> {\n\t\tif (this.closed || active.settled || this.active.get(active.request.id) !== active) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\n\t\t\t\t\t\"connection_draining\",\n\t\t\t\t\t\"Remote workspace request is no longer active\",\n\t\t\t\t\tthis.executionStateFromActive(active),\n\t\t\t\t\tfalse,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tthis.pendingOutboundMessages >= this.limits.maxPendingOutboundMessages ||\n\t\t\tthis.pendingOutboundBytes + estimatedBytes > this.limits.maxPendingOutboundBytes\n\t\t) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"limit_exceeded\", \"Remote workspace outbound queue exceeds limits\", \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tthis.pendingOutboundMessages++;\n\t\tthis.pendingOutboundBytes += estimatedBytes;\n\t\tconst queued = active.outboundQueue.then(operation);\n\t\tconst result = queued.finally(() => {\n\t\t\tthis.pendingOutboundMessages--;\n\t\t\tthis.pendingOutboundBytes -= estimatedBytes;\n\t\t});\n\t\tactive.outboundQueue = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n\n\tprivate async cancelActive(\n\t\tid: string,\n\t\terror: RemoteWorkspaceProtocolError,\n\t\trequireTermination: boolean,\n\t): Promise<void> {\n\t\tconst active = this.active.get(id);\n\t\tif (!active || active.settled) return;\n\t\tactive.cancellationError ??= error;\n\t\tactive.controller.abort();\n\t\tconst termination = Promise.all([active.handlerSettled, active.inboundQueue, active.outboundQueue]).then(\n\t\t\t() => undefined,\n\t\t);\n\t\tconst handlerStopped = await this.settlesWithin(termination, this.limits.maxCancellationMs);\n\t\tif (handlerStopped) {\n\t\t\tawait this.finalizeCancellation(active);\n\t\t\treturn;\n\t\t}\n\t\tif (requireTermination) {\n\t\t\tawait this.closeUnconfirmedCancellation(id);\n\t\t\tthrow new Error(`Remote workspace request did not stop before generation retirement: ${id}`);\n\t\t}\n\t\tawait this.closeUnconfirmedCancellation(id);\n\t}\n\n\tprivate async finalizeCancellation(active: ServerActiveRequest): Promise<void> {\n\t\tif (active.settled || !active.cancellationError) return;\n\t\tawait active.outboundQueue;\n\t\tif (active.settled) return;\n\t\tawait this.failActive(active.request.id, {\n\t\t\t...active.cancellationError,\n\t\t\texecutionState: this.executionStateFromActive(active),\n\t\t});\n\t}\n\n\tprivate async closeUnconfirmedCancellation(id: string): Promise<void> {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tthis.draining = true;\n\t\tconst activeRequests = Array.from(this.active.values());\n\t\tfor (const active of activeRequests) {\n\t\t\tclearTimeout(active.timer);\n\t\t\tactive.controller.abort();\n\t\t\tactive.settled = true;\n\t\t}\n\t\tawait Promise.all([\n\t\t\tsettlesWithinDeadline(\n\t\t\t\tPromise.all(\n\t\t\t\t\tactiveRequests.flatMap((active) => [active.handlerSettled, active.inboundQueue, active.outboundQueue]),\n\t\t\t\t).then(() => undefined),\n\t\t\t\tthis.limits.maxCancellationMs,\n\t\t\t),\n\t\t\tsettlesWithinDeadline(this.sendQueue, this.limits.maxTransportSendMs),\n\t\t]);\n\t\tthis.active.clear();\n\t\tconst closed = await settlesWithinDeadline(\n\t\t\tPromise.resolve(\n\t\t\t\tthis.transport.close(\n\t\t\t\t\tboundedCloseReason({\n\t\t\t\t\t\tcode: \"policy_violation\",\n\t\t\t\t\t\tmessage: `Remote workspace request did not stop after cancellation: ${id}`,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t\tthis.limits.maxTransportSendMs,\n\t\t).catch(() => false);\n\t\tif (!closed) throw new RemoteWorkspaceTerminationError(\"Remote workspace transport close timed out\");\n\t\tthrow new RemoteWorkspaceTerminationError(`Remote workspace request did not terminate: ${id}`);\n\t}\n\n\tprivate settlesWithin(promise: Promise<void>, timeoutMs: number): Promise<boolean> {\n\t\treturn new Promise((resolve) => {\n\t\t\tlet settled = false;\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tresolve(false);\n\t\t\t}, timeoutMs);\n\t\t\tpromise.then(() => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(true);\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate assertMethodAvailable(request: RemoteWorkspaceTypedRequest): void {\n\t\tconst capability = methodCapability(request.method);\n\t\tif (capability && !this.negotiatedCapabilities.has(capability)) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"not_available\", `Capability was not negotiated: ${capability}`, \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tif (request.method === \"artifact.read\" && !this.negotiatedCapabilities.has(\"file_transfer\")) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"not_available\", \"Artifact reads require file transfer\", \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\trequest.method !== \"catalog.get\" &&\n\t\t\trequest.method !== \"tool.invoke\" &&\n\t\t\t!this.catalog.operations.includes(request.method)\n\t\t) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"not_available\", `Operation is not advertised: ${request.method}`, \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tif (request.method === \"transfer.upload\") {\n\t\t\tconst upload = request.params as { length: number };\n\t\t\tif (upload.length > this.limits.maxTransferBytes) {\n\t\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\t\tthis.createError(\"limit_exceeded\", \"Upload exceeds total-byte limit\", \"not_started\", false),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate validateCatalogRequest(request: RemoteWorkspaceTypedRequest): void {\n\t\tif (request.method !== \"tool.invoke\") return;\n\t\tconst params = request.params as {\n\t\t\tgeneration: number;\n\t\t\tcatalogHash: string;\n\t\t\ttoolName: string;\n\t\t\tschemaHash: string;\n\t\t\targuments: unknown;\n\t\t};\n\t\tif (params.generation !== this.catalog.generation || params.catalogHash !== this.catalogHash) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"stale_generation\", \"Tool catalog generation is stale\", \"not_started\", true),\n\t\t\t);\n\t\t}\n\t\tconst tool = this.catalog.tools.find((entry) => entry.name === params.toolName);\n\t\tif (!tool) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"not_available\", `Tool is not available: ${params.toolName}`, \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tfor (const featureFlag of tool.featureFlags) {\n\t\t\tif (!this.negotiatedCapabilities.has(featureFlag)) {\n\t\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\t\tthis.createError(\n\t\t\t\t\t\t\"not_available\",\n\t\t\t\t\t\t`Tool ${params.toolName} requires unnegotiated capability ${featureFlag}`,\n\t\t\t\t\t\t\"not_started\",\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tif (tool.schemaHash !== params.schemaHash) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"schema_mismatch\", `Schema mismatch for tool ${params.toolName}`, \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\tif (!this.handler.validateToolArguments(params.toolName, params.arguments)) {\n\t\t\tthrow new RemoteWorkspaceValidationError(`Arguments do not match the canonical schema for ${params.toolName}`);\n\t\t}\n\t}\n\n\tprivate requestKind(request: RemoteWorkspaceTypedRequest): RemoteWorkspaceOperationKind {\n\t\tif (request.method !== \"tool.invoke\") return getRemoteWorkspaceMethodKind(request.method);\n\t\tconst params = request.params as { toolName: string };\n\t\treturn this.catalog.tools.find((tool) => tool.name === params.toolName)?.executionMode ?? \"service\";\n\t}\n\n\tprivate createServerUploadState(params: unknown): ServerTransferState {\n\t\tconst upload = params as { length: number; sha256: string };\n\t\tif (upload.length > this.limits.maxTransferBytes) {\n\t\t\tthrow new RemoteWorkspaceRequestError(\n\t\t\t\tthis.createError(\"limit_exceeded\", \"Upload exceeds total-byte limit\", \"not_started\", false),\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\texpectedLength: upload.length,\n\t\t\texpectedSha256: upload.sha256,\n\t\t\tbytes: 0,\n\t\t\tpendingBytes: 0,\n\t\t\tchunks: 0,\n\t\t\thash: createHash(\"sha256\"),\n\t\t\tfinishing: false,\n\t\t\tfinished: false,\n\t\t};\n\t}\n\n\tprivate async handleUploadChunk(id: string, sequence: number, dataBase64: string): Promise<void> {\n\t\tconst active = this.active.get(id);\n\t\tif (!active) {\n\t\t\tawait this.handleUnknownCorrelatedId(id, \"upload chunk\");\n\t\t\treturn;\n\t\t}\n\t\tconst transfer = active.incomingTransfer;\n\t\tif (!transfer || transfer.finished || sequence !== active.nextIncomingTransferSequence) {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Out-of-order upload chunk for ${id}` });\n\t\t\treturn;\n\t\t}\n\t\tlet chunk: Buffer;\n\t\ttry {\n\t\t\tchunk = decodeCanonicalBase64(dataBase64, this.limits.maxTransferChunkBytes);\n\t\t\tif (\n\t\t\t\ttransfer.chunks + 1 > this.limits.maxTransferChunks ||\n\t\t\t\ttransfer.bytes + chunk.byteLength > this.limits.maxTransferBytes ||\n\t\t\t\ttransfer.bytes + chunk.byteLength > transfer.expectedLength ||\n\t\t\t\ttransfer.pendingBytes + chunk.byteLength > this.limits.maxPendingTransferBytes ||\n\t\t\t\tthis.pendingTransferBytes + chunk.byteLength > this.limits.maxPendingTransferBytes\n\t\t\t) {\n\t\t\t\tthrow new RemoteWorkspaceValidationError(\"Upload exceeds declared or negotiated limits\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tawait this.cancelActive(\n\t\t\t\tid,\n\t\t\t\tthis.createError(\"limit_exceeded\", this.errorMessage(error), \"not_started\", false),\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tactive.nextIncomingTransferSequence++;\n\t\ttransfer.chunks++;\n\t\ttransfer.bytes += chunk.byteLength;\n\t\ttransfer.pendingBytes += chunk.byteLength;\n\t\tthis.pendingTransferBytes += chunk.byteLength;\n\t\ttransfer.hash.update(chunk);\n\t\tconst operation = active.inboundQueue\n\t\t\t.then(async () => {\n\t\t\t\tif (active.settled) return;\n\t\t\t\tawait this.handler.handleUploadChunk?.(\n\t\t\t\t\tactive.request as RemoteWorkspaceUploadRequest,\n\t\t\t\t\tchunk,\n\t\t\t\t\tsequence,\n\t\t\t\t\tactive.context,\n\t\t\t\t);\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\ttransfer.pendingBytes -= chunk.byteLength;\n\t\t\t\tthis.pendingTransferBytes -= chunk.byteLength;\n\t\t\t});\n\t\tactive.inboundQueue = operation.catch(() => undefined);\n\t\tvoid operation.catch(async (error: unknown) => {\n\t\t\tif (this.active.get(id) === active) await this.handleHandlerFailure(id, error);\n\t\t});\n\t}\n\n\tprivate async handleUploadFinish(id: string, length: number, sha256: string): Promise<void> {\n\t\tconst active = this.active.get(id);\n\t\tif (!active) {\n\t\t\tawait this.handleUnknownCorrelatedId(id, \"upload finish\");\n\t\t\treturn;\n\t\t}\n\t\tconst transfer = active.incomingTransfer;\n\t\tif (!transfer || transfer.finishing || transfer.finished) {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Unexpected upload finish for ${id}` });\n\t\t\treturn;\n\t\t}\n\t\ttransfer.finishing = true;\n\t\tconst digest = transfer.hash.digest(\"hex\");\n\t\tif (\n\t\t\tlength !== transfer.expectedLength ||\n\t\t\tsha256 !== transfer.expectedSha256 ||\n\t\t\ttransfer.bytes !== transfer.expectedLength ||\n\t\t\tdigest !== transfer.expectedSha256\n\t\t) {\n\t\t\tawait this.cancelActive(\n\t\t\t\tid,\n\t\t\t\tthis.createError(\"invalid_request\", \"Upload length or SHA-256 verification failed\", \"not_started\", false),\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst operation = active.inboundQueue.then(async () => {\n\t\t\tif (active.settled) return;\n\t\t\tawait this.handler.handleUploadFinish?.(\n\t\t\t\tactive.request as RemoteWorkspaceUploadRequest,\n\t\t\t\t{ length, sha256 },\n\t\t\t\tactive.context,\n\t\t\t);\n\t\t\ttransfer.finished = true;\n\t\t});\n\t\tactive.inboundQueue = operation.catch(() => undefined);\n\t\tvoid operation.catch(async (error: unknown) => {\n\t\t\tif (this.active.get(id) === active) await this.handleHandlerFailure(id, error);\n\t\t});\n\t}\n\n\tprivate async handleCancel(id: string): Promise<void> {\n\t\tif (this.active.has(id)) {\n\t\t\tawait this.cancelActive(\n\t\t\t\tid,\n\t\t\t\tthis.createError(\"cancelled\", \"Remote workspace request cancelled\", \"not_started\", false),\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.pruneTombstones();\n\t\tif (this.tombstones.has(id)) return;\n\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Cancellation for unknown request ID: ${id}` });\n\t}\n\n\tprivate async completeActive(id: string, result: unknown): Promise<void> {\n\t\tconst active = this.active.get(id);\n\t\tif (!active || active.settled) return;\n\t\tawait Promise.all([active.outboundQueue, active.inboundQueue]);\n\t\tif (active.settled) return;\n\t\tif (active.cancellationError) return;\n\t\ttry {\n\t\t\tconst validated = parseRemoteWorkspaceResult(active.request.method, result, this.limits);\n\t\t\tassertResultWorkspaceIdentity(active.request.method, validated, this.workspace);\n\t\t\tthis.verifyServerTransferResult(active, validated);\n\t\t\tthis.takeActive(id);\n\t\t\ttry {\n\t\t\t\tawait this.send({ type: \"result\", id, result: validated });\n\t\t\t} catch (error) {\n\t\t\t\tif (!(error instanceof RemoteWorkspaceValidationError)) throw error;\n\t\t\t\tawait this.send({\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\tid,\n\t\t\t\t\terror: this.createError(\n\t\t\t\t\t\t\"result_too_large\",\n\t\t\t\t\t\terror.message,\n\t\t\t\t\t\tactive.kind === \"read\" ? \"not_started\" : active.committed ? \"completed\" : \"indeterminate\",\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tawait this.handleHandlerFailure(id, error);\n\t\t}\n\t}\n\n\tprivate verifyServerTransferResult(active: ServerActiveRequest, result: unknown): void {\n\t\tif (active.request.method === \"transfer.upload\") {\n\t\t\tconst transfer = active.incomingTransfer;\n\t\t\tconst metadata = result as { length: number; sha256: string };\n\t\t\tif (\n\t\t\t\t!transfer?.finished ||\n\t\t\t\tmetadata.length !== transfer.expectedLength ||\n\t\t\t\tmetadata.sha256 !== transfer.expectedSha256\n\t\t\t) {\n\t\t\t\tthrow new RemoteWorkspaceValidationError(\"Upload completed without verified terminal metadata\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (active.request.method !== \"transfer.download\" && active.request.method !== \"artifact.read\") return;\n\t\tconst transfer = active.outgoingTransfer;\n\t\tconst metadata = result as { length: number; sha256: string };\n\t\tif (!transfer) throw new RemoteWorkspaceValidationError(\"Download completed without transfer metadata\");\n\t\tconst digest = transfer.hash.digest(\"hex\");\n\t\ttransfer.finished = true;\n\t\tif (\n\t\t\ttransfer.bytes !== transfer.expectedLength ||\n\t\t\tdigest !== transfer.expectedSha256 ||\n\t\t\tmetadata.length !== transfer.expectedLength ||\n\t\t\tmetadata.sha256 !== transfer.expectedSha256\n\t\t) {\n\t\t\tthrow new RemoteWorkspaceValidationError(\"Download length or SHA-256 verification failed\");\n\t\t}\n\t}\n\n\tprivate async handleHandlerFailure(id: string, error: unknown): Promise<void> {\n\t\tif (error instanceof RemoteWorkspaceRequestError) {\n\t\t\tawait this.failActive(id, {\n\t\t\t\tcode: error.code,\n\t\t\t\tmessage: error.message,\n\t\t\t\texecutionState: this.reconcileExecutionState(id, error.executionState),\n\t\t\t\tretryable: error.retryable,\n\t\t\t\t...(error.details === undefined ? {} : { details: error.details }),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tawait this.failActive(\n\t\t\tid,\n\t\t\tthis.createError(\n\t\t\t\t\"internal_error\",\n\t\t\t\t\"Remote workspace request handler failed\",\n\t\t\t\tthis.executionStateFor(id),\n\t\t\t\tfalse,\n\t\t\t),\n\t\t);\n\t}\n\n\tprivate async failActive(id: string, error: RemoteWorkspaceProtocolError): Promise<void> {\n\t\tconst active = this.takeActive(id);\n\t\tif (!active) return;\n\t\tactive.controller.abort();\n\t\tconst [handlerStopped, outboundStopped, inboundStopped] = await Promise.all([\n\t\t\tsettlesWithinDeadline(active.handlerSettled, this.limits.maxCancellationMs),\n\t\t\tsettlesWithinDeadline(active.outboundQueue, this.limits.maxTransportSendMs),\n\t\t\tsettlesWithinDeadline(active.inboundQueue, this.limits.maxCancellationMs),\n\t\t]);\n\t\tif (!handlerStopped || !outboundStopped || !inboundStopped) {\n\t\t\tawait this.failServerTransport(new RemoteWorkspaceTransportError(\"Request callback queue did not drain\"));\n\t\t\treturn;\n\t\t}\n\t\tconst boundedError: RemoteWorkspaceProtocolError = {\n\t\t\t...error,\n\t\t\tmessage: error.message.slice(0, 4096) || error.code,\n\t\t};\n\t\ttry {\n\t\t\tawait this.send({ type: \"error\", id, error: boundedError });\n\t\t} catch (sendError) {\n\t\t\tif (sendError instanceof RemoteWorkspaceTransportError) return;\n\t\t\tawait this.send({\n\t\t\t\ttype: \"error\",\n\t\t\t\tid,\n\t\t\t\terror: this.createError(\n\t\t\t\t\t\"internal_error\",\n\t\t\t\t\t\"Remote workspace error details exceeded protocol limits\",\n\t\t\t\t\tboundedError.executionState,\n\t\t\t\t\tfalse,\n\t\t\t\t),\n\t\t\t}).catch(() => undefined);\n\t\t}\n\t}\n\n\tprivate takeActive(id: string): ServerActiveRequest | undefined {\n\t\tconst active = this.active.get(id);\n\t\tif (!active || active.settled) return undefined;\n\t\tactive.settled = true;\n\t\tthis.active.delete(id);\n\t\tclearTimeout(active.timer);\n\t\tthis.addTombstone(id);\n\t\treturn active;\n\t}\n\n\tprivate executionStateFor(id: string): RemoteWorkspaceExecutionState {\n\t\tconst active = this.active.get(id);\n\t\treturn active ? this.executionStateFromActive(active) : \"not_started\";\n\t}\n\n\tprivate executionStateFromActive(active: ServerActiveRequest): RemoteWorkspaceExecutionState {\n\t\tif (active.committed) return \"completed\";\n\t\tif (active.sideEffectStarted) return \"indeterminate\";\n\t\treturn \"not_started\";\n\t}\n\n\tprivate reconcileExecutionState(id: string, reported: RemoteWorkspaceExecutionState): RemoteWorkspaceExecutionState {\n\t\tconst active = this.active.get(id);\n\t\tif (!active) return reported;\n\t\tif (active.committed) return \"completed\";\n\t\tif (active.sideEffectStarted && reported === \"not_started\") return \"indeterminate\";\n\t\treturn reported;\n\t}\n\n\tprivate async handleInvalidMessage(value: unknown, error: unknown): Promise<void> {\n\t\tconst id = this.usableId(value);\n\t\tif (!id) {\n\t\t\tawait this.closeWithReason({ code: \"invalid_payload\", message: this.errorMessage(error) });\n\t\t\treturn;\n\t\t}\n\t\tif (this.isDuplicateId(id)) {\n\t\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `Duplicate request ID: ${id}` });\n\t\t\treturn;\n\t\t}\n\t\tthis.addTombstone(id);\n\t\tthis.strikes++;\n\t\tawait this.sendError(id, \"invalid_request\", this.errorMessage(error), \"not_started\", false).catch(\n\t\t\t() => undefined,\n\t\t);\n\t\tif (this.strikes >= this.invalidRequestStrikes) {\n\t\t\tawait this.closeWithReason({ code: \"policy_violation\", message: \"Too many invalid requests\" });\n\t\t}\n\t}\n\n\tprivate usableId(value: unknown): string | undefined {\n\t\tif (!value || typeof value !== \"object\" || !(\"id\" in value)) return undefined;\n\t\tconst id = (value as { id?: unknown }).id;\n\t\treturn typeof id === \"string\" && /^[A-Za-z0-9._:-]{1,128}$/.test(id) ? id : undefined;\n\t}\n\n\tprivate async handleUnknownCorrelatedId(id: string, label: string): Promise<void> {\n\t\tthis.pruneTombstones();\n\t\tif (this.tombstones.has(id)) return;\n\t\tawait this.closeWithReason({ code: \"protocol_error\", message: `${label} for unknown request ID: ${id}` });\n\t}\n\n\tprivate isDuplicateId(id: string): boolean {\n\t\tthis.pruneTombstones();\n\t\treturn this.active.has(id) || this.tombstones.has(id);\n\t}\n\n\tprivate addTombstone(id: string): void {\n\t\tthis.pruneTombstones();\n\t\tif (this.tombstones.size >= this.maxTombstones) {\n\t\t\tvoid this.closeWithReason({\n\t\t\t\tcode: \"policy_violation\",\n\t\t\t\tmessage: \"Request tombstone limit exceeded\",\n\t\t\t}).catch(() => undefined);\n\t\t\treturn;\n\t\t}\n\t\tthis.tombstones.set(id, performance.now() + this.tombstoneMs);\n\t}\n\n\tprivate pruneTombstones(): void {\n\t\tconst now = performance.now();\n\t\tfor (const [id, expiresAt] of this.tombstones) if (expiresAt <= now) this.tombstones.delete(id);\n\t}\n\n\tprivate activeTransferCount(): number {\n\t\tlet count = 0;\n\t\tfor (const active of this.active.values()) if (isTransferMethod(active.request.method)) count++;\n\t\treturn count;\n\t}\n\n\tprivate validateCatalogCapabilities(catalog: RemoteWorkspaceCatalog, capabilities: ReadonlySet<string>): void {\n\t\tfor (const method of catalog.operations) {\n\t\t\tconst capability = methodCapability(method);\n\t\t\tif (capability && !capabilities.has(capability)) {\n\t\t\t\tthrow new RemoteWorkspaceValidationError(`Catalog operation ${method} requires capability ${capability}`);\n\t\t\t}\n\t\t\tif (method === \"artifact.read\" && !capabilities.has(\"file_transfer\")) {\n\t\t\t\tthrow new RemoteWorkspaceValidationError(\"Artifact reads require file transfer capability\");\n\t\t\t}\n\t\t}\n\t\tfor (const tool of catalog.tools) {\n\t\t\tfor (const featureFlag of tool.featureFlags) {\n\t\t\t\tif (!capabilities.has(featureFlag)) {\n\t\t\t\t\tthrow new RemoteWorkspaceValidationError(\n\t\t\t\t\t\t`Tool ${tool.name} requires unsupported capability ${featureFlag}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate createError(\n\t\tcode: RemoteWorkspaceErrorCode,\n\t\tmessage: string,\n\t\texecutionState: RemoteWorkspaceExecutionState,\n\t\tretryable: boolean,\n\t): RemoteWorkspaceProtocolError {\n\t\treturn { code, message: message.slice(0, 4096) || code, executionState, retryable };\n\t}\n\n\tprivate sendError(\n\t\tid: string,\n\t\tcode: RemoteWorkspaceErrorCode,\n\t\tmessage: string,\n\t\texecutionState: RemoteWorkspaceExecutionState,\n\t\tretryable: boolean,\n\t): Promise<void> {\n\t\treturn this.send({ type: \"error\", id, error: this.createError(code, message, executionState, retryable) });\n\t}\n\n\tprivate send(message: RemoteWorkspaceServerMessage, preReserved = false): Promise<void> {\n\t\tif (this.closed) return Promise.reject(new Error(\"Remote workspace protocol server is closed\"));\n\t\treturn this.sendPrepared(prepareServerOutbound(message, this.limits), preReserved);\n\t}\n\n\tprivate sendPrepared(\n\t\tprepared: PreparedProtocolMessage<RemoteWorkspaceServerMessage>,\n\t\tpreReserved = false,\n\t): Promise<void> {\n\t\tif (this.closed) return Promise.reject(new Error(\"Remote workspace protocol server is closed\"));\n\t\tconst estimatedBytes = prepared.bytes;\n\t\tif (\n\t\t\t!preReserved &&\n\t\t\t(this.pendingOutboundMessages >= this.limits.maxPendingOutboundMessages ||\n\t\t\t\tthis.pendingOutboundBytes + estimatedBytes > this.limits.maxPendingOutboundBytes)\n\t\t) {\n\t\t\tconst error = new RemoteWorkspaceTransportError(\"Remote workspace outbound queue exceeds negotiated limits\");\n\t\t\tvoid this.failServerTransport(error);\n\t\t\treturn Promise.reject(error);\n\t\t}\n\t\tif (!preReserved) {\n\t\t\tthis.pendingOutboundMessages++;\n\t\t\tthis.pendingOutboundBytes += estimatedBytes;\n\t\t}\n\t\tconst operation = this.sendQueue.then(async () => {\n\t\t\tif (this.closed) throw new RemoteWorkspaceTransportError(\"Remote workspace protocol server is closed\");\n\t\t\ttry {\n\t\t\t\tconst sent = await settlesWithinDeadline(\n\t\t\t\t\tthis.transport.send(prepared.message),\n\t\t\t\t\tthis.limits.maxTransportSendMs,\n\t\t\t\t);\n\t\t\t\tif (!sent) throw new RemoteWorkspaceTransportError(\"Remote workspace transport send timed out\");\n\t\t\t} catch (error) {\n\t\t\t\tthrow error instanceof RemoteWorkspaceTransportError ? error : new RemoteWorkspaceTransportError(error);\n\t\t\t}\n\t\t});\n\t\tconst tracked = operation.finally(() => {\n\t\t\tif (!preReserved) {\n\t\t\t\tthis.pendingOutboundMessages--;\n\t\t\t\tthis.pendingOutboundBytes -= estimatedBytes;\n\t\t\t}\n\t\t});\n\t\tthis.sendQueue = tracked.catch(() => undefined);\n\t\treturn tracked.catch(async (error: unknown) => {\n\t\t\tif (error instanceof RemoteWorkspaceTransportError) await this.failServerTransport(error);\n\t\t\tthrow error;\n\t\t});\n\t}\n\n\tprivate failServerTransport(error: RemoteWorkspaceTransportError): Promise<void> {\n\t\tthis.transportFailurePromise ??= this.failServerTransportNow(error);\n\t\treturn this.transportFailurePromise;\n\t}\n\n\tprivate async failServerTransportNow(error: RemoteWorkspaceTransportError): Promise<void> {\n\t\tconst activeRequests = Array.from(this.active.values());\n\t\tthis.closed = true;\n\t\tthis.draining = true;\n\t\tfor (const active of activeRequests) {\n\t\t\tclearTimeout(active.timer);\n\t\t\tactive.controller.abort();\n\t\t\tactive.settled = true;\n\t\t}\n\t\tawait settlesWithinDeadline(\n\t\t\tPromise.all(\n\t\t\t\tactiveRequests.flatMap((active) => [active.handlerSettled, active.inboundQueue, active.outboundQueue]),\n\t\t\t).then(() => undefined),\n\t\t\tthis.limits.maxCancellationMs,\n\t\t).catch(() => false);\n\t\tthis.active.clear();\n\t\tawait settlesWithinDeadline(\n\t\t\tPromise.resolve(this.transport.close(boundedCloseReason({ code: \"protocol_error\", message: error.message }))),\n\t\t\tthis.limits.maxTransportSendMs,\n\t\t).catch(() => false);\n\t}\n\n\tprivate errorMessage(error: unknown): string {\n\t\treturn error instanceof Error ? error.message : String(error);\n\t}\n\n\tprivate async closeWithReason(reason: RemoteWorkspaceProtocolCloseReason): Promise<void> {\n\t\tif (this.closed) return;\n\t\tlet terminationError: unknown;\n\t\ttry {\n\t\t\tawait this.disconnectInternal(false);\n\t\t} catch (error) {\n\t\t\tterminationError = error;\n\t\t}\n\t\tconst closed = await settlesWithinDeadline(\n\t\t\tPromise.resolve(this.transport.close(boundedCloseReason(reason))),\n\t\t\tthis.limits.maxTransportSendMs,\n\t\t).catch(() => false);\n\t\tif (!closed && !terminationError) {\n\t\t\tterminationError = new RemoteWorkspaceTerminationError(\"Remote workspace transport close timed out\");\n\t\t}\n\t\tif (terminationError) throw terminationError;\n\t}\n}\n"]}
|
|
@@ -141,6 +141,7 @@ export class RemoteWorkspaceClientProtocol {
|
|
|
141
141
|
maxTombstones;
|
|
142
142
|
onCatalogChanged;
|
|
143
143
|
onCatalogRefreshed;
|
|
144
|
+
onDisconnect;
|
|
144
145
|
connectionPrefix = randomBytes(16).toString("base64url");
|
|
145
146
|
pending = new Map();
|
|
146
147
|
tombstones = new Map();
|
|
@@ -176,6 +177,7 @@ export class RemoteWorkspaceClientProtocol {
|
|
|
176
177
|
this.maxTombstones = validateConfiguredInteger(options.maxTombstones ?? 65_536, "maxTombstones", 1, 1_000_000);
|
|
177
178
|
this.onCatalogChanged = options.onCatalogChanged;
|
|
178
179
|
this.onCatalogRefreshed = options.onCatalogRefreshed;
|
|
180
|
+
this.onDisconnect = options.onDisconnect;
|
|
179
181
|
}
|
|
180
182
|
get handshake() {
|
|
181
183
|
return this.negotiated ? cloneHandshakeAck(this.negotiated) : undefined;
|
|
@@ -321,6 +323,7 @@ export class RemoteWorkspaceClientProtocol {
|
|
|
321
323
|
if (this.closed)
|
|
322
324
|
return;
|
|
323
325
|
this.closed = true;
|
|
326
|
+
this.onDisconnect?.(message);
|
|
324
327
|
this.failHandshake(new RemoteWorkspaceDisconnectedError(message, "not_started"));
|
|
325
328
|
for (const [id, pending] of this.pending) {
|
|
326
329
|
const state = pending.kind === "read" ? "not_started" : "indeterminate";
|