@opengeni/runtime 0.13.0 → 0.13.4

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.
@@ -11,6 +11,7 @@ declare const MCP_MAX_TOOL_RESULT_BYTES: number;
11
11
  declare const MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES: number;
12
12
  declare const MCP_MAX_SELECTED_SERVERS = 64;
13
13
  declare const MCP_MAX_CONCURRENT_SERVER_OPERATIONS = 8;
14
+ declare const MCP_DEFAULT_OUTER_CONNECT_TIMEOUT_MS = 10000;
14
15
  declare const MCP_MAX_AGGREGATE_TOOL_LIST_ENTRIES = 4096;
15
16
  declare const MCP_MAX_AGGREGATE_TOOL_LIST_BYTES: number;
16
17
  declare const MCP_MAX_TOOL_SEARCH_SOURCES = 64;
@@ -26,6 +27,7 @@ declare function mcpSerializedSizeBytes(value: unknown): number;
26
27
  declare function assertMcpPayloadWithinBytes(value: unknown, maxBytes: number, label: string): void;
27
28
  declare function assertMcpToolListWithinBounds<T>(tools: readonly T[]): readonly T[];
28
29
  declare function assertMcpServerSelectionWithinBounds<T>(servers: readonly T[]): readonly T[];
30
+ declare function mcpOuterConnectTimeoutMs(configuredTimeouts: readonly (number | undefined)[]): number;
29
31
  type McpToolListContribution = {
30
32
  entries: number;
31
33
  bytes: number;
@@ -64,6 +66,7 @@ declare function boundedParallelMap<T, R>(values: readonly T[], concurrency: num
64
66
  declare function guardedMcpFetch<TInput extends string | URL | Request>(settings: Pick<Settings, "environment" | "integrationsAllowPrivateNetworkTargets">, fetchImpl: (input: TInput, init?: RequestInit) => Promise<Response>, options?: {
65
67
  maxResponseBytes?: number;
66
68
  dnsLookup?: DnsLookup;
69
+ pinResolvedDestination?: boolean;
67
70
  requireHttpsOutsideLocalTest?: boolean;
68
71
  }): (input: TInput, init?: RequestInit) => Promise<Response>;
69
72
  declare function boundMcpResponseBody(response: Response, maxBytes: number): Response;
@@ -75,4 +78,4 @@ declare function assertMcpDestinationAllowed(rawUrl: string, settings: Pick<Sett
75
78
  }): Promise<void>;
76
79
  declare const isNonPublicMcpAddress: typeof isNonPublicAddress;
77
80
 
78
- export { MCP_MAX_AGGREGATE_TOOL_LIST_BYTES, MCP_MAX_AGGREGATE_TOOL_LIST_ENTRIES, MCP_MAX_CONCURRENT_SERVER_OPERATIONS, MCP_MAX_INBOUND_REQUEST_BYTES, MCP_MAX_RESPONSE_BYTES, MCP_MAX_SELECTED_SERVERS, MCP_MAX_TOOL_DEFINITION_BYTES, MCP_MAX_TOOL_LIST_BYTES, MCP_MAX_TOOL_LIST_ENTRIES, MCP_MAX_TOOL_RESULT_BYTES, MCP_MAX_TOOL_SEARCH_DESCRIPTION_BYTES, MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES, MCP_MAX_TOOL_SEARCH_SOURCES, MCP_MAX_TOOL_SEARCH_SOURCE_LABEL_LENGTH, McpAggregateToolListBudget, McpPayloadTooLargeError, assertMcpDestinationAllowed, assertMcpPayloadWithinBytes, assertMcpServerSelectionWithinBounds, assertMcpToolListWithinBounds, boundMcpResponseBody, boundedMcpRequest, boundedParallelMap, cancelMcpResponseBody, guardedMcpFetch, isNonPublicMcpAddress, mcpSerializedSizeBytes };
81
+ export { MCP_DEFAULT_OUTER_CONNECT_TIMEOUT_MS, MCP_MAX_AGGREGATE_TOOL_LIST_BYTES, MCP_MAX_AGGREGATE_TOOL_LIST_ENTRIES, MCP_MAX_CONCURRENT_SERVER_OPERATIONS, MCP_MAX_INBOUND_REQUEST_BYTES, MCP_MAX_RESPONSE_BYTES, MCP_MAX_SELECTED_SERVERS, MCP_MAX_TOOL_DEFINITION_BYTES, MCP_MAX_TOOL_LIST_BYTES, MCP_MAX_TOOL_LIST_ENTRIES, MCP_MAX_TOOL_RESULT_BYTES, MCP_MAX_TOOL_SEARCH_DESCRIPTION_BYTES, MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES, MCP_MAX_TOOL_SEARCH_SOURCES, MCP_MAX_TOOL_SEARCH_SOURCE_LABEL_LENGTH, McpAggregateToolListBudget, McpPayloadTooLargeError, assertMcpDestinationAllowed, assertMcpPayloadWithinBytes, assertMcpServerSelectionWithinBounds, assertMcpToolListWithinBounds, boundMcpResponseBody, boundedMcpRequest, boundedParallelMap, cancelMcpResponseBody, guardedMcpFetch, isNonPublicMcpAddress, mcpOuterConnectTimeoutMs, mcpSerializedSizeBytes };
@@ -1,4 +1,5 @@
1
1
  import {
2
+ MCP_DEFAULT_OUTER_CONNECT_TIMEOUT_MS,
2
3
  MCP_MAX_AGGREGATE_TOOL_LIST_BYTES,
3
4
  MCP_MAX_AGGREGATE_TOOL_LIST_ENTRIES,
4
5
  MCP_MAX_CONCURRENT_SERVER_OPERATIONS,
@@ -25,11 +26,13 @@ import {
25
26
  cancelMcpResponseBody,
26
27
  guardedMcpFetch,
27
28
  isNonPublicMcpAddress,
29
+ mcpOuterConnectTimeoutMs,
28
30
  mcpSerializedSizeBytes,
29
31
  undiciFetch
30
- } from "./chunk-HAN5D27J.js";
32
+ } from "./chunk-QYXOVIUK.js";
31
33
  import "./chunk-PZ5AY32C.js";
32
34
  export {
35
+ MCP_DEFAULT_OUTER_CONNECT_TIMEOUT_MS,
33
36
  MCP_MAX_AGGREGATE_TOOL_LIST_BYTES,
34
37
  MCP_MAX_AGGREGATE_TOOL_LIST_ENTRIES,
35
38
  MCP_MAX_CONCURRENT_SERVER_OPERATIONS,
@@ -56,6 +59,7 @@ export {
56
59
  cancelMcpResponseBody,
57
60
  guardedMcpFetch,
58
61
  isNonPublicMcpAddress,
62
+ mcpOuterConnectTimeoutMs,
59
63
  mcpSerializedSizeBytes,
60
64
  undiciFetch
61
65
  };
@@ -1,6 +1,6 @@
1
1
  export { collectSandboxEnvironment, parseExposedPorts } from '@opengeni/config';
2
2
  export { CAPABILITY_DESCRIPTORS, CapabilityDescriptor, DESKTOP_STREAM_PORT, StreamTokenPayload, StreamTokenPayload as StreamTokenPayloadType, TERMINAL_STREAM_PORT } from '@opengeni/contracts';
3
3
  import '@openai/agents/sandbox';
4
- export { A as ActiveBackendResolverDeps, b as ActiveBackendUnresolvableError, c as ActivePointer, B as BackendUnresolvableCode, C as ChannelAConflictError, d as ChannelAEmitter, e as ChannelAExecArgs, f as ChannelAExecResult, g as ChannelANotFoundError, h as ChannelASession, i as ChannelAUnavailableError, j as ChannelAUnsupportedError, k as ChannelAValidationError, l as ControlRpc, D as DEFAULT_DESKTOP_GEOMETRY, m as DISPLAY_STACK_TIMEOUT_MS, n as DesktopGeometry, o as DisplayStackCallerKind, p as DisplayStackClassification, q as DisplayStackError, r as DisplayStackTelemetryContext, s as DisplayStackTelemetryEvent, t as DisplayStackTelemetryStatus, u as DisplayStackUnsupportedError, E as EnsureDisplayStackOptions, v as EnsureDisplayStackResult, w as EnsureTerminalServerOptions, x as EnsureTerminalServerResult, y as EstablishedSandboxSession, z as ExposeStreamPortInput, F as ExposeStreamPortResult, G as ExposedPortEndpoint, H as FAULT_FIELD_WHAT_HAPPENED, I as FAULT_FIELD_WHAT_PRESERVED, J as FAULT_FIELD_WHAT_TO_TRY, K as FAULT_FIELD_WHICH_LAYER, L as LiveModalSandboxLeaseAttribution, M as MaterializeRunCredentialsOptions, N as MintStreamTokenInput, O as MockAgentResponder, P as MockAgentResponderOptions, Q as MockExecHandler, S as ModalModuleLoader, T as ModalOrphanSweepResult, U as ModalOrphanSweepTermination, V as ModalSandboxAttribution, W as NEVER_SENT_DETAIL_KEY, X as NatsControlRpc, Y as NatsOpStreamConnection, Z as NatsOpStreamTransport, _ as NatsRequestConnection, $ as NegotiationContext, a0 as NormalizedRunCredentialMaterial, a1 as NumstatEntry, a2 as OP_STREAM_ACK_INTERVAL_MS, a3 as OP_STREAM_DEFAULT_WINDOW_BYTES, a4 as OP_STREAM_RECONNECT_HOLD_MS, a5 as OP_STREAM_SILENCE_TIMEOUT_MS, a6 as OpStreamExecOutcome, a7 as OpStreamJournal, a8 as OpStreamSubscription, a9 as OpStreamTransport, aa as OpStreamUnavailableError, ab as PROVIDER_REGISTRY, ac as ProviderConstructionContext, ad as ProviderRegistration, ae as REPOSITORY_DISCOVERY_LIMIT, af as RecordingArtifactMetadata, ag as RecordingCodec, ah as RecordingContentType, ai as RecordingError, aj as RecordingProcess, ak as RecordingUnavailableError, al as RepositoryDiscoveryDegradedReason, am as RepositoryDiscoveryResult, an as ResolvedActiveBackend, ao as RevalidateModalOrphanTermination, ap as RoutableBackendSession, aq as RoutableSandbox, ar as RoutingSandboxSession, as as RoutingSandboxSessionDeps, at as RoutingTransitionEvent, au as RoutingUnsupportedError, av as RunCredentialCommandRunner, aw as RunCredentialCommandSession, ax as RunCredentialExpectedScope, R as RunCredentialSessionReady, ay as RunCredentialValidationError, az as SELFHOSTED_DEFAULT_TIMEOUT_MS, aA as SELFHOSTED_DRAINING_MAX_RETRIES, aB as SELFHOSTED_EXEC_DRAINING_MAX_RETRIES, aC as SELFHOSTED_IDEMPOTENT_READONLY_OPS, aD as SELFHOSTED_INFRASTRUCTURE_FAULT_CLASSES, aE as SELFHOSTED_NEVER_SENT_MAX_RETRIES, aF as SELFHOSTED_RECONNECT_WINDOW_MS, aG as SELFHOSTED_RELAY_STREAM_PATH, aH as SELFHOSTED_RETRY_BACKOFF_BASE_MS, aI as SELFHOSTED_RETRY_BACKOFF_CAP_MS, aJ as SELFHOSTED_RETRY_BACKOFF_FACTOR, aK as SELFHOSTED_TIMEOUT_MAX_RETRIES, aL as STREAM_PORT, aM as STREAM_TOKEN_DEFAULT_TTL_SECONDS, aN as SandboxChannelAService, aO as SandboxChannelAServiceOptions, aP as SandboxConfigError, aQ as SandboxCreatedCallback, aR as SandboxProviderUnavailableError, aS as SandboxResumeStateUnavailableError, aT as SelfhostedApplyDiff, aU as SelfhostedControlError, aV as SelfhostedEditor, aW as SelfhostedEnrollment, aX as SelfhostedExecArgs, aY as SelfhostedExecResult, aZ as SelfhostedImageOutput, a_ as SelfhostedLivenessState, a$ as SelfhostedNegotiationInput, b0 as SelfhostedOpObservation, b1 as SelfhostedOpObserver, b2 as SelfhostedOpStreamDeps, b3 as SelfhostedRelayConfig, b4 as SelfhostedRetryClock, b5 as SelfhostedRetryDecision, b6 as SelfhostedSandboxClient, b7 as SelfhostedSession, b8 as SelfhostedSessionBuild, b9 as SelfhostedSessionDeps, ba as SelfhostedSessionState, bb as SelfhostedUnavailableReason, bc as StartRecordingInput, bd as StreamPortUnavailableError, be as SwapTargetEstablishability, bf as TERMINAL_SERVER_TIMEOUT_MS, bg as TerminalServerError, bh as TerminalServerUnsupportedError, bi as ToolspaceTokenPathError, bj as agentErrorToControlError, bk as assertDescriptorRegistryInvariants, bl as assertProviderRegistryInvariants, bm as assertSafeRelPath, bn as backendSupportsOs, bo as buildDisplayStackScript, bp as buildSelfhostedBackendSession, bq as buildStreamUrl, br as buildTerminalServerScript, bs as clearRunCredentials, bt as clearRunCredentialsForAttempt, bu as contentTypeForCodec, bv as createSandboxClient, bw as createSandboxClientForBackend, bx as decideSelfhostedRetry, by as decodeModalSnapshotId, bz as defaultSelfhostedRetryClock, bA as deletePriorPersistedSnapshot, bB as deleteRecordingArtifacts, bC as deserializeSandboxSessionStateEnvelope, bD as desktopCapableBackend, bE as drainingExhaustedError, bF as drainingMessage, bG as ensureDisplayStack, bH as ensureModalRegistryImage, bI as ensureTerminalServer, bJ as establishSandboxSessionFromEnvelope, bK as execDeadlineHint, bL as exposeStreamPort, bM as extForCodec, bN as inspectRecordingArtifact, bO as isExecSessionLostBanner, bP as isProviderSandboxNotFoundError, bQ as isSelfhostedProviderNotFoundError, bR as makeActiveBackendResolver, bS as materializeRunCredentials, bT as mintStreamToken, bU as modalSandboxAttributionEnvironment, bV as modalSandboxAttributionTags, bW as negotiateCapabilities, bX as negotiateSelfhostedCapabilities, bY as nextDurableOpId, bZ as normalizeRunCredentialsResolution, b_ as offlineAgentError, b$ as offlineControlResponse, c0 as opAckSubject, c1 as opFrameSubject, c2 as parseExecBannerExitCode, c3 as parseExecBannerSessionId, c4 as parseNumstatZ, c5 as parsePorcelainV2, c6 as parseUnifiedPatch, c7 as payloadTooLargeMessage, c8 as readWorkspaceArchiveFromEnvelopeSessionState, c9 as recordingStorageKey, ca as renderSelfhostedFault, cb as restoredSandboxSessionStateFromEntry, cc as runCredentialPointerFile, cd as runCredentialRoot, ce as runWithToolCallCorrelation, cf as sandboxStateEntryFromRunState, cg as sanitizeOpIdToken, ch as selectBackend, ci as selfhostedFaultClass, cj as selfhostedLiveness, ck as selfhostedRetryBackoffMs, cl as serializeEstablishedSandboxEnvelope, cm as setSelfhostedApplyDiff, cn as startRecording, co as stopRecording, cp as stripExecBanner, cq as subjectFor, cr as swapTargetEstablishability, cs as sweepModalOrphanSandboxes, ct as tagModalSandbox, cu as tearDownDisplayStack, cv as tearDownTerminalServer, cw as terminateModalSandboxById, cx as timeoutAgentError, cy as timeoutControlResponse, cz as toolspaceTokenFileForSession, cA as toolspaceTokenFileFromEnvironment, cB as uploadRecordingArtifact, cC as verifyStreamToken, cD as withRunCredentialEnvironment, cE as withRunCredentialsClient, cF as withRunCredentialsSession, cG as withToolspaceTokenClient, cH as withToolspaceTokenEnvironment, cI as withToolspaceTokenSession } from '../index-DGUrY_yR.js';
4
+ export { A as ActiveBackendResolverDeps, b as ActiveBackendUnresolvableError, c as ActivePointer, B as BackendUnresolvableCode, C as ChannelAConflictError, d as ChannelAEmitter, e as ChannelAExecArgs, f as ChannelAExecResult, g as ChannelANotFoundError, h as ChannelASession, i as ChannelAUnavailableError, j as ChannelAUnsupportedError, k as ChannelAValidationError, l as ControlRpc, D as DEFAULT_DESKTOP_GEOMETRY, m as DISPLAY_STACK_TIMEOUT_MS, n as DefaultBackendLossResult, o as DesktopGeometry, p as DisplayStackCallerKind, q as DisplayStackClassification, r as DisplayStackError, s as DisplayStackTelemetryContext, t as DisplayStackTelemetryEvent, u as DisplayStackTelemetryStatus, v as DisplayStackUnsupportedError, E as EnsureDisplayStackOptions, w as EnsureDisplayStackResult, x as EnsureTerminalServerOptions, y as EnsureTerminalServerResult, z as EstablishedSandboxSession, F as ExposeStreamPortInput, G as ExposeStreamPortResult, H as ExposedPortEndpoint, I as FAULT_FIELD_WHAT_HAPPENED, J as FAULT_FIELD_WHAT_PRESERVED, K as FAULT_FIELD_WHAT_TO_TRY, L as FAULT_FIELD_WHICH_LAYER, M as LiveModalSandboxLeaseAttribution, N as MaterializeRunCredentialsOptions, O as MintStreamTokenInput, P as MockAgentResponder, Q as MockAgentResponderOptions, S as MockExecHandler, T as ModalModuleLoader, U as ModalOrphanSweepResult, V as ModalOrphanSweepTermination, W as ModalSandboxAttribution, X as NEVER_SENT_DETAIL_KEY, Y as NatsControlRpc, Z as NatsOpStreamConnection, _ as NatsOpStreamTransport, $ as NatsRequestConnection, a0 as NegotiationContext, a1 as NormalizedRunCredentialMaterial, a2 as NumstatEntry, a3 as OP_STREAM_ACK_INTERVAL_MS, a4 as OP_STREAM_DEFAULT_WINDOW_BYTES, a5 as OP_STREAM_RECONNECT_HOLD_MS, a6 as OP_STREAM_SILENCE_TIMEOUT_MS, a7 as OpStreamExecOutcome, a8 as OpStreamJournal, a9 as OpStreamSubscription, aa as OpStreamTransport, ab as OpStreamUnavailableError, ac as PROVIDER_REGISTRY, ad as ProviderConstructionContext, ae as ProviderRegistration, af as ProviderSandboxFailure, ag as ProviderSandboxFailureKind, ah as REPOSITORY_DISCOVERY_LIMIT, ai as RecordingArtifactMetadata, aj as RecordingCodec, ak as RecordingContentType, al as RecordingError, am as RecordingProcess, an as RecordingUnavailableError, ao as RepositoryDiscoveryDegradedReason, ap as RepositoryDiscoveryResult, aq as ResolvedActiveBackend, ar as RevalidateModalOrphanTermination, as as RoutableBackendSession, at as RoutableSandbox, au as RoutingBackendRecoveryRequiredError, av as RoutingMutationOutcomeUnknownError, aw as RoutingRetainedProcess, ax as RoutingRetainedProcessAdoption, ay as RoutingRetainedProcessNotFoundError, az as RoutingRetainedProcessTerminalProof, aA as RoutingSandboxSession, aB as RoutingSandboxSessionDeps, aC as RoutingTransitionEvent, aD as RoutingUnsupportedError, aE as RunCredentialCommandRunner, aF as RunCredentialCommandSession, aG as RunCredentialExpectedScope, R as RunCredentialSessionReady, aH as RunCredentialValidationError, aI as SELFHOSTED_DEFAULT_TIMEOUT_MS, aJ as SELFHOSTED_DRAINING_MAX_RETRIES, aK as SELFHOSTED_EXEC_DRAINING_MAX_RETRIES, aL as SELFHOSTED_IDEMPOTENT_READONLY_OPS, aM as SELFHOSTED_INFRASTRUCTURE_FAULT_CLASSES, aN as SELFHOSTED_NEVER_SENT_MAX_RETRIES, aO as SELFHOSTED_RECONNECT_WINDOW_MS, aP as SELFHOSTED_RELAY_STREAM_PATH, aQ as SELFHOSTED_RETRY_BACKOFF_BASE_MS, aR as SELFHOSTED_RETRY_BACKOFF_CAP_MS, aS as SELFHOSTED_RETRY_BACKOFF_FACTOR, aT as SELFHOSTED_TIMEOUT_MAX_RETRIES, aU as STREAM_PORT, aV as STREAM_TOKEN_DEFAULT_TTL_SECONDS, aW as SandboxChannelAService, aX as SandboxChannelAServiceOptions, aY as SandboxConfigError, aZ as SandboxCreatedCallback, a_ as SandboxExecReadinessError, a$ as SandboxProviderUnavailableError, b0 as SandboxReplacementProviderStateError, b1 as SandboxResumeStateUnavailableError, b2 as SelfhostedApplyDiff, b3 as SelfhostedControlError, b4 as SelfhostedEditor, b5 as SelfhostedEnrollment, b6 as SelfhostedExecArgs, b7 as SelfhostedExecResult, b8 as SelfhostedImageOutput, b9 as SelfhostedLivenessState, ba as SelfhostedNegotiationInput, bb as SelfhostedOpObservation, bc as SelfhostedOpObserver, bd as SelfhostedOpStreamDeps, be as SelfhostedRelayConfig, bf as SelfhostedRetryClock, bg as SelfhostedRetryDecision, bh as SelfhostedSandboxClient, bi as SelfhostedSession, bj as SelfhostedSessionBuild, bk as SelfhostedSessionDeps, bl as SelfhostedSessionState, bm as SelfhostedUnavailableReason, bn as StartRecordingInput, bo as StreamPortUnavailableError, bp as SwapTargetEstablishability, bq as TERMINAL_SERVER_TIMEOUT_MS, br as TerminalServerError, bs as TerminalServerUnsupportedError, bt as ToolspaceTokenPathError, bu as VerifiedWorkspaceArchive, bv as WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION, bw as WorkspaceArchiveDescriptor, bx as WorkspaceArchiveIntegrityCode, by as WorkspaceArchiveIntegrityError, bz as WorkspaceTreeFingerprint, bA as agentErrorToControlError, bB as assertDescriptorRegistryInvariants, bC as assertProviderRegistryInvariants, bD as assertSafeRelPath, bE as backendSupportsOs, bF as buildDisplayStackScript, bG as buildSelfhostedBackendSession, bH as buildStreamUrl, bI as buildTerminalServerScript, bJ as captureVerifiedWorkspaceArchive, bK as classifyProviderSandboxFailure, bL as clearRunCredentials, bM as clearRunCredentialsForAttempt, bN as contentTypeForCodec, bO as createSandboxClient, bP as createSandboxClientForBackend, bQ as decideSelfhostedRetry, bR as decodeModalSnapshotId, bS as defaultSelfhostedRetryClock, bT as deletePriorPersistedSnapshot, bU as deleteRecordingArtifacts, bV as deserializeSandboxSessionStateEnvelope, bW as desktopCapableBackend, bX as drainingExhaustedError, bY as drainingMessage, bZ as ensureDisplayStack, b_ as ensureModalRegistryImage, b$ as ensureTerminalServer, c0 as establishSandboxSessionFromEnvelope, c1 as execDeadlineHint, c2 as exposeStreamPort, c3 as extForCodec, c4 as fingerprintSandboxWorkspace, c5 as hasPersistableSandboxProviderIdentity, c6 as inspectRecordingArtifact, c7 as isExecSessionLostBanner, c8 as isProviderSandboxGoneDuringRoutedOperation, c9 as isProviderSandboxNotFoundError, ca as isProviderSandboxTransientError, cb as isSelfhostedProviderNotFoundError, cc as makeActiveBackendResolver, cd as materializeRunCredentials, ce as mintStreamToken, cf as modalSandboxAttributionEnvironment, cg as modalSandboxAttributionTags, ch as negotiateCapabilities, ci as negotiateSelfhostedCapabilities, cj as nextDurableOpId, ck as normalizeRunCredentialsResolution, cl as offlineAgentError, cm as offlineControlResponse, cn as opAckSubject, co as opFrameSubject, cp as parseExecBannerExitCode, cq as parseExecBannerSessionId, cr as parseNumstatZ, cs as parsePorcelainV2, ct as parseUnifiedPatch, cu as parseWorkspaceArchiveDescriptor, cv as payloadTooLargeMessage, cw as readVerifiedWorkspaceArchive, cx as readWorkspaceArchiveFromEnvelopeSessionState, cy as recordingStorageKey, cz as renderSelfhostedFault, cA as requirePersistableReplacementSandboxEnvelope, cB as restoredSandboxSessionStateFromEntry, cC as runCredentialPointerFile, cD as runCredentialRoot, cE as runWithToolCallCorrelation, cF as sandboxStateEntryFromRunState, cG as sanitizeOpIdToken, cH as selectBackend, cI as selfhostedFaultClass, cJ as selfhostedLiveness, cK as selfhostedRetryBackoffMs, cL as serializeEstablishedSandboxEnvelope, cM as serializeReplacementSandboxEnvelope, cN as setSelfhostedApplyDiff, cO as startRecording, cP as stopRecording, cQ as stripExecBanner, cR as subjectFor, cS as swapTargetEstablishability, cT as sweepModalOrphanSandboxes, cU as tagModalSandbox, cV as tearDownDisplayStack, cW as tearDownTerminalServer, cX as terminateModalSandboxById, cY as timeoutAgentError, cZ as timeoutControlResponse, c_ as toolspaceTokenFileForSession, c$ as toolspaceTokenFileFromEnvironment, d0 as uploadRecordingArtifact, d1 as verifyRestoredWorkspace, d2 as verifySandboxExecReadiness, d3 as verifyStreamToken, d4 as withRunCredentialEnvironment, d5 as withRunCredentialsClient, d6 as withRunCredentialsSession, d7 as withToolspaceTokenClient, d8 as withToolspaceTokenEnvironment, d9 as withToolspaceTokenSession } from '../index-DU-KXVlx.js';
5
5
  import 'modal';
6
6
  import '@opengeni/agent-proto';
@@ -28,6 +28,9 @@ import {
28
28
  REPOSITORY_DISCOVERY_LIMIT,
29
29
  RecordingError,
30
30
  RecordingUnavailableError,
31
+ RoutingBackendRecoveryRequiredError,
32
+ RoutingMutationOutcomeUnknownError,
33
+ RoutingRetainedProcessNotFoundError,
31
34
  RoutingSandboxSession,
32
35
  RoutingUnsupportedError,
33
36
  RunCredentialValidationError,
@@ -47,7 +50,9 @@ import {
47
50
  STREAM_TOKEN_DEFAULT_TTL_SECONDS,
48
51
  SandboxChannelAService,
49
52
  SandboxConfigError,
53
+ SandboxExecReadinessError,
50
54
  SandboxProviderUnavailableError,
55
+ SandboxReplacementProviderStateError,
51
56
  SandboxResumeStateUnavailableError,
52
57
  SelfhostedControlError,
53
58
  SelfhostedSandboxClient,
@@ -59,6 +64,8 @@ import {
59
64
  TerminalServerError,
60
65
  TerminalServerUnsupportedError,
61
66
  ToolspaceTokenPathError,
67
+ WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
68
+ WorkspaceArchiveIntegrityError,
62
69
  agentErrorToControlError,
63
70
  assertDescriptorRegistryInvariants,
64
71
  assertProviderRegistryInvariants,
@@ -68,6 +75,8 @@ import {
68
75
  buildSelfhostedBackendSession,
69
76
  buildStreamUrl,
70
77
  buildTerminalServerScript,
78
+ captureVerifiedWorkspaceArchive,
79
+ classifyProviderSandboxFailure,
71
80
  clearRunCredentials,
72
81
  clearRunCredentialsForAttempt,
73
82
  collectSandboxEnvironment,
@@ -90,9 +99,13 @@ import {
90
99
  execDeadlineHint,
91
100
  exposeStreamPort,
92
101
  extForCodec,
102
+ fingerprintSandboxWorkspace,
103
+ hasPersistableSandboxProviderIdentity,
93
104
  inspectRecordingArtifact,
94
105
  isExecSessionLostBanner,
106
+ isProviderSandboxGoneDuringRoutedOperation,
95
107
  isProviderSandboxNotFoundError,
108
+ isProviderSandboxTransientError,
96
109
  isSelfhostedProviderNotFoundError,
97
110
  makeActiveBackendResolver,
98
111
  materializeRunCredentials,
@@ -113,10 +126,13 @@ import {
113
126
  parseNumstatZ,
114
127
  parsePorcelainV2,
115
128
  parseUnifiedPatch,
129
+ parseWorkspaceArchiveDescriptor,
116
130
  payloadTooLargeMessage,
131
+ readVerifiedWorkspaceArchive,
117
132
  readWorkspaceArchiveFromEnvelopeSessionState,
118
133
  recordingStorageKey,
119
134
  renderSelfhostedFault,
135
+ requirePersistableReplacementSandboxEnvelope,
120
136
  restoredSandboxSessionStateFromEntry,
121
137
  runCredentialPointerFile,
122
138
  runCredentialRoot,
@@ -128,6 +144,7 @@ import {
128
144
  selfhostedLiveness,
129
145
  selfhostedRetryBackoffMs,
130
146
  serializeEstablishedSandboxEnvelope,
147
+ serializeReplacementSandboxEnvelope,
131
148
  setSelfhostedApplyDiff,
132
149
  startRecording,
133
150
  stopRecording,
@@ -144,6 +161,8 @@ import {
144
161
  toolspaceTokenFileForSession,
145
162
  toolspaceTokenFileFromEnvironment,
146
163
  uploadRecordingArtifact,
164
+ verifyRestoredWorkspace,
165
+ verifySandboxExecReadiness,
147
166
  verifyStreamToken,
148
167
  withRunCredentialEnvironment,
149
168
  withRunCredentialsClient,
@@ -151,7 +170,7 @@ import {
151
170
  withToolspaceTokenClient,
152
171
  withToolspaceTokenEnvironment,
153
172
  withToolspaceTokenSession
154
- } from "../chunk-65UDUFUO.js";
173
+ } from "../chunk-JGUQSAG5.js";
155
174
  import "../chunk-4YJ5Z52Z.js";
156
175
  import "../chunk-K4V4QYCL.js";
157
176
  import "../chunk-PZ5AY32C.js";
@@ -185,6 +204,9 @@ export {
185
204
  REPOSITORY_DISCOVERY_LIMIT,
186
205
  RecordingError,
187
206
  RecordingUnavailableError,
207
+ RoutingBackendRecoveryRequiredError,
208
+ RoutingMutationOutcomeUnknownError,
209
+ RoutingRetainedProcessNotFoundError,
188
210
  RoutingSandboxSession,
189
211
  RoutingUnsupportedError,
190
212
  RunCredentialValidationError,
@@ -204,7 +226,9 @@ export {
204
226
  STREAM_TOKEN_DEFAULT_TTL_SECONDS,
205
227
  SandboxChannelAService,
206
228
  SandboxConfigError,
229
+ SandboxExecReadinessError,
207
230
  SandboxProviderUnavailableError,
231
+ SandboxReplacementProviderStateError,
208
232
  SandboxResumeStateUnavailableError,
209
233
  SelfhostedControlError,
210
234
  SelfhostedSandboxClient,
@@ -216,6 +240,8 @@ export {
216
240
  TerminalServerError,
217
241
  TerminalServerUnsupportedError,
218
242
  ToolspaceTokenPathError,
243
+ WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
244
+ WorkspaceArchiveIntegrityError,
219
245
  agentErrorToControlError,
220
246
  assertDescriptorRegistryInvariants,
221
247
  assertProviderRegistryInvariants,
@@ -225,6 +251,8 @@ export {
225
251
  buildSelfhostedBackendSession,
226
252
  buildStreamUrl,
227
253
  buildTerminalServerScript,
254
+ captureVerifiedWorkspaceArchive,
255
+ classifyProviderSandboxFailure,
228
256
  clearRunCredentials,
229
257
  clearRunCredentialsForAttempt,
230
258
  collectSandboxEnvironment,
@@ -247,9 +275,13 @@ export {
247
275
  execDeadlineHint,
248
276
  exposeStreamPort,
249
277
  extForCodec,
278
+ fingerprintSandboxWorkspace,
279
+ hasPersistableSandboxProviderIdentity,
250
280
  inspectRecordingArtifact,
251
281
  isExecSessionLostBanner,
282
+ isProviderSandboxGoneDuringRoutedOperation,
252
283
  isProviderSandboxNotFoundError,
284
+ isProviderSandboxTransientError,
253
285
  isSelfhostedProviderNotFoundError,
254
286
  makeActiveBackendResolver,
255
287
  materializeRunCredentials,
@@ -270,10 +302,13 @@ export {
270
302
  parseNumstatZ,
271
303
  parsePorcelainV2,
272
304
  parseUnifiedPatch,
305
+ parseWorkspaceArchiveDescriptor,
273
306
  payloadTooLargeMessage,
307
+ readVerifiedWorkspaceArchive,
274
308
  readWorkspaceArchiveFromEnvelopeSessionState,
275
309
  recordingStorageKey,
276
310
  renderSelfhostedFault,
311
+ requirePersistableReplacementSandboxEnvelope,
277
312
  restoredSandboxSessionStateFromEntry,
278
313
  runCredentialPointerFile,
279
314
  runCredentialRoot,
@@ -285,6 +320,7 @@ export {
285
320
  selfhostedLiveness,
286
321
  selfhostedRetryBackoffMs,
287
322
  serializeEstablishedSandboxEnvelope,
323
+ serializeReplacementSandboxEnvelope,
288
324
  setSelfhostedApplyDiff,
289
325
  startRecording,
290
326
  stopRecording,
@@ -301,6 +337,8 @@ export {
301
337
  toolspaceTokenFileForSession,
302
338
  toolspaceTokenFileFromEnvironment,
303
339
  uploadRecordingArtifact,
340
+ verifyRestoredWorkspace,
341
+ verifySandboxExecReadiness,
304
342
  verifyStreamToken,
305
343
  withRunCredentialEnvironment,
306
344
  withRunCredentialsClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/runtime",
3
- "version": "0.13.0",
3
+ "version": "0.13.4",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -50,8 +50,8 @@
50
50
  "@openai/agents-extensions": "0.13.3",
51
51
  "@opengeni/agent-proto": "^0.3.0",
52
52
  "@opengeni/codex": "^0.2.7",
53
- "@opengeni/config": "^0.6.9",
54
- "@opengeni/contracts": "^0.18.0",
53
+ "@opengeni/config": "^0.7.1",
54
+ "@opengeni/contracts": "^0.19.0",
55
55
  "@opengeni/network": "^0.1.1",
56
56
  "debug": "4.4.3",
57
57
  "modal": "0.7.6",
package/src/index.ts CHANGED
@@ -49,6 +49,7 @@ import {
49
49
  boundedParallelMap,
50
50
  cancelMcpResponseBody,
51
51
  guardedMcpFetch,
52
+ mcpOuterConnectTimeoutMs,
52
53
  undiciFetch,
53
54
  } from "./mcp-network";
54
55
  import {
@@ -2342,7 +2343,7 @@ export type ConnectedMcpServerBatches = {
2342
2343
  */
2343
2344
  export async function connectMcpServersInBatches(
2344
2345
  servers: MCPServer[],
2345
- options: { strict: boolean },
2346
+ options: { strict: boolean; connectTimeoutMs?: number },
2346
2347
  ): Promise<ConnectedMcpServerBatches> {
2347
2348
  assertMcpServerSelectionWithinBounds(servers);
2348
2349
  const batches: ConnectedMcpServerBatch[] = [];
@@ -2352,6 +2353,9 @@ export async function connectMcpServersInBatches(
2352
2353
  await connectMcpServers(
2353
2354
  servers.slice(offset, offset + MCP_MAX_CONCURRENT_SERVER_OPERATIONS),
2354
2355
  {
2356
+ ...(options.connectTimeoutMs === undefined
2357
+ ? {}
2358
+ : { connectTimeoutMs: options.connectTimeoutMs }),
2355
2359
  connectInParallel: true,
2356
2360
  strict: options.strict,
2357
2361
  },
@@ -2404,7 +2408,13 @@ export async function prepareAgentTools(
2404
2408
  }
2405
2409
  const registry = new Map(settings.mcpServers.map((server) => [server.id, server]));
2406
2410
  const aggregateToolBudget = new McpAggregateToolListBudget();
2407
- const mcpFetchImpl = options.mcpFetchImpl ?? undiciFetch;
2411
+ // npm Undici's dispatcher transport can hang indefinitely under Bun even
2412
+ // after an AbortSignal fires. Bun's native fetch is the supported runtime
2413
+ // transport there; destination policy validation still runs before every
2414
+ // credential-bearing MCP request.
2415
+ const useBunNativeFetch = options.mcpFetchImpl === undefined && !!process.versions.bun;
2416
+ const mcpFetchImpl =
2417
+ options.mcpFetchImpl ?? (useBunNativeFetch ? globalThis.fetch.bind(globalThis) : undiciFetch);
2408
2418
  const servers = await boundedParallelMap(
2409
2419
  tools,
2410
2420
  MCP_MAX_CONCURRENT_SERVER_OPERATIONS,
@@ -2421,7 +2431,10 @@ export async function prepareAgentTools(
2421
2431
  const guardedFetch = guardedMcpFetch(
2422
2432
  firstParty ? { ...settings, integrationsAllowPrivateNetworkTargets: true } : settings,
2423
2433
  baseFetch,
2424
- firstParty ? { requireHttpsOutsideLocalTest: false } : {},
2434
+ {
2435
+ ...(firstParty ? { requireHttpsOutsideLocalTest: false } : {}),
2436
+ ...(useBunNativeFetch ? { pinResolvedDestination: false } : {}),
2437
+ },
2425
2438
  );
2426
2439
  const fetchImpl = config.connectionRef
2427
2440
  ? connectionBrokerFetch(guardedFetch, config, options)
@@ -2478,24 +2491,33 @@ export async function prepareAgentTools(
2478
2491
  server,
2479
2492
  bestEffort,
2480
2493
  optional,
2494
+ timeoutMs: config.timeoutMs,
2481
2495
  };
2482
2496
  },
2483
2497
  );
2484
- const requiredServers = servers.filter((entry) => !entry.bestEffort).map((entry) => entry.server);
2485
- const bestEffortServers = servers
2486
- .filter((entry) => entry.bestEffort)
2487
- .map((entry) => entry.server);
2498
+ const requiredEntries = servers.filter((entry) => !entry.bestEffort);
2499
+ const bestEffortEntries = servers.filter((entry) => entry.bestEffort);
2500
+ const requiredServers = requiredEntries.map((entry) => entry.server);
2501
+ const bestEffortServers = bestEffortEntries.map((entry) => entry.server);
2488
2502
  // Names of the OPTIONAL servers (not codex_apps) so a drop is surfaced as a
2489
2503
  // warning; codex_apps keeps its historically-quiet drop (a not-logged-in
2490
2504
  // ChatGPT plan is a normal, non-noteworthy state).
2491
2505
  const optionalServerNames = new Set(
2492
2506
  servers.filter((entry) => entry.optional).map((entry) => entry.server.name),
2493
2507
  );
2494
- const connectedRequired = await connectMcpServersInBatches(requiredServers, { strict: true });
2508
+ const connectedRequired = await connectMcpServersInBatches(requiredServers, {
2509
+ strict: true,
2510
+ connectTimeoutMs: mcpOuterConnectTimeoutMs(requiredEntries.map((entry) => entry.timeoutMs)),
2511
+ });
2495
2512
  let connectedBestEffort: ConnectedMcpServerBatches | null = null;
2496
2513
  try {
2497
2514
  connectedBestEffort = bestEffortServers.length
2498
- ? await connectMcpServersInBatches(bestEffortServers, { strict: false })
2515
+ ? await connectMcpServersInBatches(bestEffortServers, {
2516
+ strict: false,
2517
+ connectTimeoutMs: mcpOuterConnectTimeoutMs(
2518
+ bestEffortEntries.map((entry) => entry.timeoutMs),
2519
+ ),
2520
+ })
2499
2521
  : null;
2500
2522
  } catch (error) {
2501
2523
  await connectedRequired.close().catch(() => undefined);
@@ -18,6 +18,10 @@ export const MCP_MAX_TOOL_RESULT_BYTES = 1024 * 1024;
18
18
  export const MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES = 256 * 1024;
19
19
  export const MCP_MAX_SELECTED_SERVERS = 64;
20
20
  export const MCP_MAX_CONCURRENT_SERVER_OPERATIONS = 8;
21
+ // @openai/agents has a separate lifecycle fence around MCPServer.connect().
22
+ // It defaults to 10 seconds and can otherwise preempt a larger per-server
23
+ // transport timeout before that server finishes its own handshake.
24
+ export const MCP_DEFAULT_OUTER_CONNECT_TIMEOUT_MS = 10_000;
21
25
  export const MCP_MAX_AGGREGATE_TOOL_LIST_ENTRIES = 4_096;
22
26
  export const MCP_MAX_AGGREGATE_TOOL_LIST_BYTES = 16 * 1024 * 1024;
23
27
  export const MCP_MAX_TOOL_SEARCH_SOURCES = 64;
@@ -87,6 +91,15 @@ export function assertMcpServerSelectionWithinBounds<T>(servers: readonly T[]):
87
91
  return servers;
88
92
  }
89
93
 
94
+ export function mcpOuterConnectTimeoutMs(
95
+ configuredTimeouts: readonly (number | undefined)[],
96
+ ): number {
97
+ return Math.max(
98
+ MCP_DEFAULT_OUTER_CONNECT_TIMEOUT_MS,
99
+ ...configuredTimeouts.flatMap((timeoutMs) => (timeoutMs === undefined ? [] : [timeoutMs])),
100
+ );
101
+ }
102
+
90
103
  type McpToolListContribution = {
91
104
  entries: number;
92
105
  bytes: number;
@@ -199,16 +212,28 @@ export function guardedMcpFetch<TInput extends string | URL | Request>(
199
212
  options: {
200
213
  maxResponseBytes?: number;
201
214
  dnsLookup?: DnsLookup;
215
+ pinResolvedDestination?: boolean;
202
216
  requireHttpsOutsideLocalTest?: boolean;
203
217
  } = {},
204
218
  ): (input: TInput, init?: RequestInit) => Promise<Response> {
205
219
  return async (input: TInput, init?: RequestInit) => {
206
- const response = await pinnedFetch(input, init, settings, {
207
- fetchImpl: fetchImpl as FetchLike,
220
+ const destinationOptions = {
208
221
  ...(options.dnsLookup ? { dnsLookup: options.dnsLookup } : {}),
209
222
  label: "MCP endpoint",
210
223
  requireHttpsOutsideLocalTest: options.requireHttpsOutsideLocalTest ?? true,
211
- });
224
+ };
225
+ let response: Response;
226
+ if (options.pinResolvedDestination === false) {
227
+ await resolvePinnedDestination(input instanceof Request ? input.url : input, settings, {
228
+ ...destinationOptions,
229
+ });
230
+ response = await fetchImpl(input, { ...init, redirect: "manual" });
231
+ } else {
232
+ response = await pinnedFetch(input, init, settings, {
233
+ fetchImpl: fetchImpl as FetchLike,
234
+ ...destinationOptions,
235
+ });
236
+ }
212
237
  return boundMcpResponseBody(response, options.maxResponseBytes ?? MCP_MAX_RESPONSE_BYTES);
213
238
  };
214
239
  }
@@ -58,9 +58,21 @@ import type {
58
58
  TerminalExecRequest,
59
59
  TerminalExecResponse,
60
60
  } from "@opengeni/contracts";
61
- import { parseExecBannerExitCode, parseExecBannerSessionId } from "./exec-banner";
62
-
63
- export { parseExecBannerExitCode, parseExecBannerSessionId } from "./exec-banner";
61
+ import {
62
+ isExecSessionLostBanner,
63
+ parseExecBannerExitCode,
64
+ parseExecBannerSessionId,
65
+ } from "./exec-banner";
66
+ import {
67
+ createTurnToolCancellationController,
68
+ TurnSandboxCommandCancelledError,
69
+ } from "./turn-tool-cancellation";
70
+
71
+ export {
72
+ isExecSessionLostBanner,
73
+ parseExecBannerExitCode,
74
+ parseExecBannerSessionId,
75
+ } from "./exec-banner";
64
76
 
65
77
  // ── The minimal session surface Channel A consumes (a structural subset of the
66
78
  // SDK's SandboxSession, all optional — capability-probed before use). ─────────
@@ -101,6 +113,21 @@ export type ChannelASession = {
101
113
  yieldTimeMs?: number;
102
114
  maxOutputTokens?: number;
103
115
  }): Promise<string>;
116
+ writeStdinForProcessMutation?(args: {
117
+ sessionId: number;
118
+ chars?: string;
119
+ yieldTimeMs?: number;
120
+ maxOutputTokens?: number;
121
+ }): Promise<string>;
122
+ writeStdinForProcessControl?(args: {
123
+ sessionId: number;
124
+ chars?: string;
125
+ yieldTimeMs?: number;
126
+ maxOutputTokens?: number;
127
+ }): Promise<string>;
128
+ cancelExecCommand?(opId: string): Promise<boolean>;
129
+ hasRetainedProcess?(providerSessionId: number): boolean;
130
+ execCommandForProcessControl?(providerSessionId: number, args: ChannelAExecArgs): Promise<string>;
104
131
  createEditor?(runAs?: string): ChannelAEditor;
105
132
  supportsPty?(): boolean;
106
133
  };
@@ -1133,17 +1160,49 @@ export class SandboxChannelAService {
1133
1160
 
1134
1161
  // ════════════════════════ Terminal exec + PTY (A2) ════════════════════════
1135
1162
 
1136
- /** Run a bounded command, return buffered stdout/stderr + exit code inline. The
1137
- * long-running tail (when the process hasn't exited within timeoutMs) keeps
1138
- * running in-box; if emitStream is set the buffered output is also published as
1139
- * the agent firehose so other viewers see it. */
1163
+ /** Run a bounded command to physical completion and return buffered output.
1164
+ * A deadline cancels the exact provider operation, proves its process group
1165
+ * absent, and settles any retained-process admission before returning 124. */
1140
1166
  async terminalExec(req: TerminalExecRequest): Promise<TerminalExecResponse> {
1141
- const r = await this.run({
1142
- cmd: req.command,
1143
- workdir: this.terminalWorkdir(req.cwd),
1144
- yieldTimeMs: req.timeoutMs,
1145
- });
1146
- const running = r.exitCode === null && typeof r.sessionId === "number";
1167
+ const abort = new AbortController();
1168
+ const controller = createTurnToolCancellationController(abort.signal);
1169
+ const timeoutReason = new Error(`Terminal command exceeded ${req.timeoutMs}ms`);
1170
+ let timedOut = false;
1171
+ const timer = setTimeout(() => {
1172
+ timedOut = true;
1173
+ abort.abort(timeoutReason);
1174
+ }, req.timeoutMs);
1175
+ const terminalWorkdir = this.terminalWorkdir(req.cwd);
1176
+ let r: Awaited<ReturnType<typeof controller.runSandboxCommandStructured>>;
1177
+ try {
1178
+ r = await controller.runSandboxCommandStructured(this.session, {
1179
+ cmd: req.command,
1180
+ ...(terminalWorkdir ? { workdir: terminalWorkdir } : {}),
1181
+ ...(this.runAs ? { runAs: this.runAs } : {}),
1182
+ yieldTimeMs: req.timeoutMs,
1183
+ });
1184
+ if (timedOut) {
1185
+ await controller.waitForQuiescence();
1186
+ r = {
1187
+ stdout: "",
1188
+ stderr: timeoutReason.message,
1189
+ exitCode: 124,
1190
+ wallTimeSeconds: req.timeoutMs / 1_000,
1191
+ };
1192
+ }
1193
+ } catch (error) {
1194
+ controller.cancel(error);
1195
+ await controller.waitForQuiescence();
1196
+ if (!timedOut && !(error instanceof TurnSandboxCommandCancelledError)) throw error;
1197
+ r = {
1198
+ stdout: "",
1199
+ stderr: timeoutReason.message,
1200
+ exitCode: 124,
1201
+ wallTimeSeconds: req.timeoutMs / 1_000,
1202
+ };
1203
+ } finally {
1204
+ clearTimeout(timer);
1205
+ }
1147
1206
  if (req.emitStream && (r.stdout || r.stderr)) {
1148
1207
  const events: { type: SessionEventType; payload: unknown }[] = [];
1149
1208
  const commandId = crypto.randomUUID();
@@ -1163,7 +1222,7 @@ export class SandboxChannelAService {
1163
1222
  stdout: r.stdout,
1164
1223
  stderr: r.stderr,
1165
1224
  exitCode: r.exitCode,
1166
- running,
1225
+ running: false,
1167
1226
  wallTimeSeconds: r.wallTimeSeconds,
1168
1227
  };
1169
1228
  }
@@ -1203,10 +1262,13 @@ export class SandboxChannelAService {
1203
1262
  * it as terminal.pty.output.delta). Throws ChannelAUnsupportedError when the
1204
1263
  * backend has no writeStdin. */
1205
1264
  async ptyWrite(_req: PtyWriteRequest, execSessionId: number, data: string): Promise<string> {
1206
- if (!this.session.writeStdin) {
1265
+ const write =
1266
+ this.session.writeStdinForProcessMutation?.bind(this.session) ??
1267
+ this.session.writeStdin?.bind(this.session);
1268
+ if (!write) {
1207
1269
  throw new ChannelAUnsupportedError("interactive terminal unsupported on this backend");
1208
1270
  }
1209
- const out = await this.session.writeStdin({
1271
+ const out = await write({
1210
1272
  sessionId: execSessionId,
1211
1273
  chars: data,
1212
1274
  yieldTimeMs: 250,
@@ -1228,25 +1290,27 @@ export class SandboxChannelAService {
1228
1290
  /** Resize an open PTY (SIGWINCH via stty against the exec-session). The SDK has
1229
1291
  * no resize method; stty in the same tty session updates the geometry. */
1230
1292
  async ptyResize(req: PtyResizeRequest, execSessionId: number): Promise<void> {
1231
- if (!this.session.writeStdin) return;
1293
+ const write =
1294
+ this.session.writeStdinForProcessControl?.bind(this.session) ??
1295
+ this.session.writeStdin?.bind(this.session);
1296
+ if (!write) return;
1232
1297
  // Send a stty in-band on the same pty session.
1233
- await this.session.writeStdin({
1298
+ await write({
1234
1299
  sessionId: execSessionId,
1235
1300
  chars: `stty cols ${req.cols} rows ${req.rows}\n`,
1236
1301
  yieldTimeMs: 50,
1237
1302
  });
1238
1303
  }
1239
1304
 
1240
- /** Close an open PTY: write exit/EOF. The caller marks the row closed + emits
1241
- * terminal.pty.exited. */
1242
- async ptyClose(_req: PtyCloseRequest, execSessionId: number | null): Promise<void> {
1243
- if (execSessionId !== null && this.session.writeStdin) {
1244
- try {
1245
- await this.session.writeStdin({ sessionId: execSessionId, chars: "", yieldTimeMs: 50 }); // EOF
1246
- } catch {
1247
- // best-effort; the row is marked closed regardless.
1248
- }
1249
- }
1305
+ /** Ask an open PTY to exit and return the exact provider banner. The routing
1306
+ * session settles durable process authority only when that banner proves this
1307
+ * locator exited or was lost; callers must keep metadata open otherwise. */
1308
+ async ptyClose(_req: PtyCloseRequest, execSessionId: number | null): Promise<string> {
1309
+ const write =
1310
+ this.session.writeStdinForProcessControl?.bind(this.session) ??
1311
+ this.session.writeStdin?.bind(this.session);
1312
+ if (execSessionId === null || !write) return "";
1313
+ return await write({ sessionId: execSessionId, chars: "", yieldTimeMs: 250 }); // EOF
1250
1314
  }
1251
1315
 
1252
1316
  // ──────────────────────────── helpers ──────────────────────────────────────
@@ -1435,24 +1499,6 @@ function isDefinitePathNotFoundError(error: unknown): boolean {
1435
1499
  );
1436
1500
  }
1437
1501
 
1438
- // Detect the Modal "the exec-session you're writing to no longer exists" banner.
1439
- // writeStdin reports a vanished session as a non-throwing string of the shape
1440
- // `write_stdin failed: session not found: <N>` (it does NOT raise). This is a
1441
- // hard cancellation-fence fact, so classify only the complete known banner (or
1442
- // its bare provider fact) carrying the exact tracked numeric id. ID-less,
1443
- // malformed, mismatched, or embellished/ambiguous text must remain fail-closed.
1444
- export function isExecSessionLostBanner(out: string, execSessionId: number): boolean {
1445
- if (!out || !Number.isSafeInteger(execSessionId) || execSessionId < 0) return false;
1446
- const match = out.match(/^(?:write_stdin failed: )?session not found: (\d+)$/);
1447
- // JavaScript's `$` also matches immediately before one final line terminator.
1448
- // Requiring the regex match to consume the entire string keeps even that
1449
- // otherwise-special case fail-closed.
1450
- if (!match || match[0] !== out) return false;
1451
- // String equality is intentional: parseInt would normalize malformed facts
1452
- // such as `01` and can round an out-of-range integer into another identity.
1453
- return match[1] === String(execSessionId);
1454
- }
1455
-
1456
1502
  function sniffBinary(bytes: Buffer): boolean {
1457
1503
  const n = Math.min(bytes.byteLength, 8192);
1458
1504
  for (let i = 0; i < n; i++) if (bytes[i] === 0) return true;