@adhdev/session-host-core 1.0.82-rc.26 → 1.0.82-rc.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -148,6 +148,26 @@ interface ResizeSessionPayload {
148
148
  interface StopSessionPayload {
149
149
  sessionId: string;
150
150
  }
151
+ interface MeshWriterAssignment {
152
+ meshId: string;
153
+ nodeId: string;
154
+ taskId: string;
155
+ attemptId: string;
156
+ generation: number;
157
+ dispatchNonce: number;
158
+ ownerDaemonId: string;
159
+ }
160
+ interface MeshWriterStopTarget {
161
+ runtimeKey: string;
162
+ startedAt: number;
163
+ osPid: number;
164
+ providerType: string;
165
+ assignment: MeshWriterAssignment;
166
+ }
167
+ interface StopMeshWriterPayload extends MeshWriterStopTarget {
168
+ sessionId: string;
169
+ clientId: string;
170
+ }
151
171
  interface DeleteSessionPayload {
152
172
  sessionId: string;
153
173
  /** Stop a live runtime before deleting the persisted record. */
@@ -304,6 +324,9 @@ type SessionHostRequest = {
304
324
  } | {
305
325
  type: 'stop_session';
306
326
  payload: StopSessionPayload;
327
+ } | {
328
+ type: 'stop_mesh_writer';
329
+ payload: StopMeshWriterPayload;
307
330
  } | {
308
331
  type: 'delete_session';
309
332
  payload: DeleteSessionPayload;
@@ -663,6 +686,8 @@ interface SessionHostClientOptions {
663
686
  endpoint?: SessionHostEndpoint;
664
687
  appName?: string;
665
688
  requestTimeoutMs?: number;
689
+ /** Join the shared client registry (default true). Disable for short-lived probes. */
690
+ registerAsShared?: boolean;
666
691
  }
667
692
  /**
668
693
  * Why an established session-host connection went away.
@@ -672,6 +697,7 @@ interface SessionHostClientOptions {
672
697
  */
673
698
  type SessionHostDisconnectReason = 'error' | 'ended' | 'closed';
674
699
  declare function disconnectSessionHostClients(endpoint?: SessionHostEndpoint, error?: Error): void;
700
+ declare function findConnectedSessionHostClient(endpoint?: SessionHostEndpoint): SessionHostClient | null;
675
701
  interface SessionHostDisconnectInfo {
676
702
  reason: SessionHostDisconnectReason;
677
703
  endpointPath: string;
@@ -703,6 +729,8 @@ declare class SessionHostClient {
703
729
  private attachedSessions;
704
730
  constructor(options?: SessionHostClientOptions);
705
731
  static disconnectClients(endpoint?: SessionHostEndpoint, error?: Error): void;
732
+ static findConnectedClient(endpoint?: SessionHostEndpoint): SessionHostClient | null;
733
+ isConnected(): boolean;
706
734
  /**
707
735
  * Fires when an established connection to the session host goes away.
708
736
  *
@@ -718,9 +746,11 @@ declare class SessionHostClient {
718
746
  onDisconnect(listener: (info: SessionHostDisconnectInfo) => void): () => void;
719
747
  private handleDisconnect;
720
748
  private sendRawRequestOnSocket;
721
- connect(): Promise<void>;
749
+ connect(timeoutMs?: number): Promise<void>;
722
750
  onEvent(listener: (event: SessionHostEvent) => void): () => void;
723
- request<T = unknown>(request: SessionHostRequest): Promise<SessionHostResponse<T>>;
751
+ request<T = unknown>(request: SessionHostRequest, options?: {
752
+ timeoutMs?: number;
753
+ }): Promise<SessionHostResponse<T>>;
724
754
  disconnect(error?: Error): void;
725
755
  close(): Promise<void>;
726
756
  }
@@ -880,4 +910,4 @@ interface SessionHostControlTransport {
880
910
  */
881
911
  declare function createSessionHostControlPlane(transport: SessionHostControlTransport): SessionHostControlPlane;
882
912
 
883
- export { type AcquireWritePayload, type AttachSessionPayload, CLI_CHILD_NICE_ENV, CLI_CHILD_NICE_INCREMENT, CLI_CHILD_SCHEDULING_ENV, type ClearSessionBufferPayload, type CreateSessionPayload, DEFAULT_CONFIG_DIR_NAME, DEFAULT_SESSION_RING_BUFFER_MAX_BYTES, type DetachSessionPayload, type ForceDetachClientPayload, type GetHostDiagnosticsPayload, type GetSnapshotPayload, type GetTerminalSnapshotPayload, type HostUpgradeLeasePayload, type PruneDuplicateSessionsPayload, type ReleaseWritePayload, type ResizeSessionPayload, type RestartSessionPayload, type ResumeSessionPayload, SESSION_HOST_SUPPORTED_REQUEST_TYPES, type SendInputPayload, type SendSignalPayload, type SessionAttachedClient, type SessionBufferSnapshot, type SessionClientType, type SessionHostCategory, SessionHostClient, type SessionHostClientOptions, type SessionHostControlPlane, type SessionHostControlTransport, type SessionHostDiagnostics, type SessionHostDisconnectInfo, type SessionHostDisconnectReason, type SessionHostDuplicateSessionGroup, type SessionHostEndpoint, type SessionHostEndpointOptions, type SessionHostEvent, type SessionHostEventEnvelope, type SessionHostLogEntry, type SessionHostPruneDuplicatesResult, type SessionHostRecord, SessionHostRegistry, type SessionHostRequest, type SessionHostRequestEnvelope, type SessionHostRequestTrace, type SessionHostRequestType, type SessionHostResponse, type SessionHostResponseEnvelope, type SessionHostRuntimeTransition, type SessionHostSnapshot, type SessionHostSurfaceKind, type SessionHostSurfaceRecordLike, type SessionHostWireEnvelope, type SessionLaunchCommand, type SessionLifecycle, type SessionOwnerType, SessionRingBuffer, type SessionRingBufferOptions, type SessionTerminalSnapshot, type SessionTerminalState, type SessionTermination, type SessionTransport, type SessionWriteOwner, type StopSessionPayload, type UpdateSessionMetaPayload, applyTerminalColorEnv, buildRuntimeDisplayName, buildRuntimeKey, canonicalizeInstancePath, classifyTermination, createCliChildPriorityPolicy, createLineParser, createResponseEnvelope, createSessionHostControlPlane, disconnectSessionHostClients, ensureNodePtySpawnHelperPermissions, formatRuntimeOwner, getDefaultSessionHostEndpoint, getSessionHostRecoveryLabel, getSessionHostSurfaceKind, getWorkspaceLabel, hiddenSpawn as hiddenSpawnProcess, isDefaultInstanceConfigDir, isSessionHostLiveRuntime, isSessionHostRecoverySnapshot, isTerminalRecord, partitionSessionHostDiagnosticsSessions, partitionSessionHostRecords, replayLaunchEnv, resolveAttachableRuntimeRecord, resolveInstanceConfigDir, resolveRuntimeRecord, resolveSessionHostIpcKey, sanitizeSpawnEnv, spawnCliChild, writeEnvelope };
913
+ export { type AcquireWritePayload, type AttachSessionPayload, CLI_CHILD_NICE_ENV, CLI_CHILD_NICE_INCREMENT, CLI_CHILD_SCHEDULING_ENV, type ClearSessionBufferPayload, type CreateSessionPayload, DEFAULT_CONFIG_DIR_NAME, DEFAULT_SESSION_RING_BUFFER_MAX_BYTES, type DetachSessionPayload, type ForceDetachClientPayload, type GetHostDiagnosticsPayload, type GetSnapshotPayload, type GetTerminalSnapshotPayload, type HostUpgradeLeasePayload, type MeshWriterAssignment, type MeshWriterStopTarget, type PruneDuplicateSessionsPayload, type ReleaseWritePayload, type ResizeSessionPayload, type RestartSessionPayload, type ResumeSessionPayload, SESSION_HOST_SUPPORTED_REQUEST_TYPES, type SendInputPayload, type SendSignalPayload, type SessionAttachedClient, type SessionBufferSnapshot, type SessionClientType, type SessionHostCategory, SessionHostClient, type SessionHostClientOptions, type SessionHostControlPlane, type SessionHostControlTransport, type SessionHostDiagnostics, type SessionHostDisconnectInfo, type SessionHostDisconnectReason, type SessionHostDuplicateSessionGroup, type SessionHostEndpoint, type SessionHostEndpointOptions, type SessionHostEvent, type SessionHostEventEnvelope, type SessionHostLogEntry, type SessionHostPruneDuplicatesResult, type SessionHostRecord, SessionHostRegistry, type SessionHostRequest, type SessionHostRequestEnvelope, type SessionHostRequestTrace, type SessionHostRequestType, type SessionHostResponse, type SessionHostResponseEnvelope, type SessionHostRuntimeTransition, type SessionHostSnapshot, type SessionHostSurfaceKind, type SessionHostSurfaceRecordLike, type SessionHostWireEnvelope, type SessionLaunchCommand, type SessionLifecycle, type SessionOwnerType, SessionRingBuffer, type SessionRingBufferOptions, type SessionTerminalSnapshot, type SessionTerminalState, type SessionTermination, type SessionTransport, type SessionWriteOwner, type StopMeshWriterPayload, type StopSessionPayload, type UpdateSessionMetaPayload, applyTerminalColorEnv, buildRuntimeDisplayName, buildRuntimeKey, canonicalizeInstancePath, classifyTermination, createCliChildPriorityPolicy, createLineParser, createResponseEnvelope, createSessionHostControlPlane, disconnectSessionHostClients, ensureNodePtySpawnHelperPermissions, findConnectedSessionHostClient, formatRuntimeOwner, getDefaultSessionHostEndpoint, getSessionHostRecoveryLabel, getSessionHostSurfaceKind, getWorkspaceLabel, hiddenSpawn as hiddenSpawnProcess, isDefaultInstanceConfigDir, isSessionHostLiveRuntime, isSessionHostRecoverySnapshot, isTerminalRecord, partitionSessionHostDiagnosticsSessions, partitionSessionHostRecords, replayLaunchEnv, resolveAttachableRuntimeRecord, resolveInstanceConfigDir, resolveRuntimeRecord, resolveSessionHostIpcKey, sanitizeSpawnEnv, spawnCliChild, writeEnvelope };
package/dist/index.d.ts CHANGED
@@ -148,6 +148,26 @@ interface ResizeSessionPayload {
148
148
  interface StopSessionPayload {
149
149
  sessionId: string;
150
150
  }
151
+ interface MeshWriterAssignment {
152
+ meshId: string;
153
+ nodeId: string;
154
+ taskId: string;
155
+ attemptId: string;
156
+ generation: number;
157
+ dispatchNonce: number;
158
+ ownerDaemonId: string;
159
+ }
160
+ interface MeshWriterStopTarget {
161
+ runtimeKey: string;
162
+ startedAt: number;
163
+ osPid: number;
164
+ providerType: string;
165
+ assignment: MeshWriterAssignment;
166
+ }
167
+ interface StopMeshWriterPayload extends MeshWriterStopTarget {
168
+ sessionId: string;
169
+ clientId: string;
170
+ }
151
171
  interface DeleteSessionPayload {
152
172
  sessionId: string;
153
173
  /** Stop a live runtime before deleting the persisted record. */
@@ -304,6 +324,9 @@ type SessionHostRequest = {
304
324
  } | {
305
325
  type: 'stop_session';
306
326
  payload: StopSessionPayload;
327
+ } | {
328
+ type: 'stop_mesh_writer';
329
+ payload: StopMeshWriterPayload;
307
330
  } | {
308
331
  type: 'delete_session';
309
332
  payload: DeleteSessionPayload;
@@ -663,6 +686,8 @@ interface SessionHostClientOptions {
663
686
  endpoint?: SessionHostEndpoint;
664
687
  appName?: string;
665
688
  requestTimeoutMs?: number;
689
+ /** Join the shared client registry (default true). Disable for short-lived probes. */
690
+ registerAsShared?: boolean;
666
691
  }
667
692
  /**
668
693
  * Why an established session-host connection went away.
@@ -672,6 +697,7 @@ interface SessionHostClientOptions {
672
697
  */
673
698
  type SessionHostDisconnectReason = 'error' | 'ended' | 'closed';
674
699
  declare function disconnectSessionHostClients(endpoint?: SessionHostEndpoint, error?: Error): void;
700
+ declare function findConnectedSessionHostClient(endpoint?: SessionHostEndpoint): SessionHostClient | null;
675
701
  interface SessionHostDisconnectInfo {
676
702
  reason: SessionHostDisconnectReason;
677
703
  endpointPath: string;
@@ -703,6 +729,8 @@ declare class SessionHostClient {
703
729
  private attachedSessions;
704
730
  constructor(options?: SessionHostClientOptions);
705
731
  static disconnectClients(endpoint?: SessionHostEndpoint, error?: Error): void;
732
+ static findConnectedClient(endpoint?: SessionHostEndpoint): SessionHostClient | null;
733
+ isConnected(): boolean;
706
734
  /**
707
735
  * Fires when an established connection to the session host goes away.
708
736
  *
@@ -718,9 +746,11 @@ declare class SessionHostClient {
718
746
  onDisconnect(listener: (info: SessionHostDisconnectInfo) => void): () => void;
719
747
  private handleDisconnect;
720
748
  private sendRawRequestOnSocket;
721
- connect(): Promise<void>;
749
+ connect(timeoutMs?: number): Promise<void>;
722
750
  onEvent(listener: (event: SessionHostEvent) => void): () => void;
723
- request<T = unknown>(request: SessionHostRequest): Promise<SessionHostResponse<T>>;
751
+ request<T = unknown>(request: SessionHostRequest, options?: {
752
+ timeoutMs?: number;
753
+ }): Promise<SessionHostResponse<T>>;
724
754
  disconnect(error?: Error): void;
725
755
  close(): Promise<void>;
726
756
  }
@@ -880,4 +910,4 @@ interface SessionHostControlTransport {
880
910
  */
881
911
  declare function createSessionHostControlPlane(transport: SessionHostControlTransport): SessionHostControlPlane;
882
912
 
883
- export { type AcquireWritePayload, type AttachSessionPayload, CLI_CHILD_NICE_ENV, CLI_CHILD_NICE_INCREMENT, CLI_CHILD_SCHEDULING_ENV, type ClearSessionBufferPayload, type CreateSessionPayload, DEFAULT_CONFIG_DIR_NAME, DEFAULT_SESSION_RING_BUFFER_MAX_BYTES, type DetachSessionPayload, type ForceDetachClientPayload, type GetHostDiagnosticsPayload, type GetSnapshotPayload, type GetTerminalSnapshotPayload, type HostUpgradeLeasePayload, type PruneDuplicateSessionsPayload, type ReleaseWritePayload, type ResizeSessionPayload, type RestartSessionPayload, type ResumeSessionPayload, SESSION_HOST_SUPPORTED_REQUEST_TYPES, type SendInputPayload, type SendSignalPayload, type SessionAttachedClient, type SessionBufferSnapshot, type SessionClientType, type SessionHostCategory, SessionHostClient, type SessionHostClientOptions, type SessionHostControlPlane, type SessionHostControlTransport, type SessionHostDiagnostics, type SessionHostDisconnectInfo, type SessionHostDisconnectReason, type SessionHostDuplicateSessionGroup, type SessionHostEndpoint, type SessionHostEndpointOptions, type SessionHostEvent, type SessionHostEventEnvelope, type SessionHostLogEntry, type SessionHostPruneDuplicatesResult, type SessionHostRecord, SessionHostRegistry, type SessionHostRequest, type SessionHostRequestEnvelope, type SessionHostRequestTrace, type SessionHostRequestType, type SessionHostResponse, type SessionHostResponseEnvelope, type SessionHostRuntimeTransition, type SessionHostSnapshot, type SessionHostSurfaceKind, type SessionHostSurfaceRecordLike, type SessionHostWireEnvelope, type SessionLaunchCommand, type SessionLifecycle, type SessionOwnerType, SessionRingBuffer, type SessionRingBufferOptions, type SessionTerminalSnapshot, type SessionTerminalState, type SessionTermination, type SessionTransport, type SessionWriteOwner, type StopSessionPayload, type UpdateSessionMetaPayload, applyTerminalColorEnv, buildRuntimeDisplayName, buildRuntimeKey, canonicalizeInstancePath, classifyTermination, createCliChildPriorityPolicy, createLineParser, createResponseEnvelope, createSessionHostControlPlane, disconnectSessionHostClients, ensureNodePtySpawnHelperPermissions, formatRuntimeOwner, getDefaultSessionHostEndpoint, getSessionHostRecoveryLabel, getSessionHostSurfaceKind, getWorkspaceLabel, hiddenSpawn as hiddenSpawnProcess, isDefaultInstanceConfigDir, isSessionHostLiveRuntime, isSessionHostRecoverySnapshot, isTerminalRecord, partitionSessionHostDiagnosticsSessions, partitionSessionHostRecords, replayLaunchEnv, resolveAttachableRuntimeRecord, resolveInstanceConfigDir, resolveRuntimeRecord, resolveSessionHostIpcKey, sanitizeSpawnEnv, spawnCliChild, writeEnvelope };
913
+ export { type AcquireWritePayload, type AttachSessionPayload, CLI_CHILD_NICE_ENV, CLI_CHILD_NICE_INCREMENT, CLI_CHILD_SCHEDULING_ENV, type ClearSessionBufferPayload, type CreateSessionPayload, DEFAULT_CONFIG_DIR_NAME, DEFAULT_SESSION_RING_BUFFER_MAX_BYTES, type DetachSessionPayload, type ForceDetachClientPayload, type GetHostDiagnosticsPayload, type GetSnapshotPayload, type GetTerminalSnapshotPayload, type HostUpgradeLeasePayload, type MeshWriterAssignment, type MeshWriterStopTarget, type PruneDuplicateSessionsPayload, type ReleaseWritePayload, type ResizeSessionPayload, type RestartSessionPayload, type ResumeSessionPayload, SESSION_HOST_SUPPORTED_REQUEST_TYPES, type SendInputPayload, type SendSignalPayload, type SessionAttachedClient, type SessionBufferSnapshot, type SessionClientType, type SessionHostCategory, SessionHostClient, type SessionHostClientOptions, type SessionHostControlPlane, type SessionHostControlTransport, type SessionHostDiagnostics, type SessionHostDisconnectInfo, type SessionHostDisconnectReason, type SessionHostDuplicateSessionGroup, type SessionHostEndpoint, type SessionHostEndpointOptions, type SessionHostEvent, type SessionHostEventEnvelope, type SessionHostLogEntry, type SessionHostPruneDuplicatesResult, type SessionHostRecord, SessionHostRegistry, type SessionHostRequest, type SessionHostRequestEnvelope, type SessionHostRequestTrace, type SessionHostRequestType, type SessionHostResponse, type SessionHostResponseEnvelope, type SessionHostRuntimeTransition, type SessionHostSnapshot, type SessionHostSurfaceKind, type SessionHostSurfaceRecordLike, type SessionHostWireEnvelope, type SessionLaunchCommand, type SessionLifecycle, type SessionOwnerType, SessionRingBuffer, type SessionRingBufferOptions, type SessionTerminalSnapshot, type SessionTerminalState, type SessionTermination, type SessionTransport, type SessionWriteOwner, type StopMeshWriterPayload, type StopSessionPayload, type UpdateSessionMetaPayload, applyTerminalColorEnv, buildRuntimeDisplayName, buildRuntimeKey, canonicalizeInstancePath, classifyTermination, createCliChildPriorityPolicy, createLineParser, createResponseEnvelope, createSessionHostControlPlane, disconnectSessionHostClients, ensureNodePtySpawnHelperPermissions, findConnectedSessionHostClient, formatRuntimeOwner, getDefaultSessionHostEndpoint, getSessionHostRecoveryLabel, getSessionHostSurfaceKind, getWorkspaceLabel, hiddenSpawn as hiddenSpawnProcess, isDefaultInstanceConfigDir, isSessionHostLiveRuntime, isSessionHostRecoverySnapshot, isTerminalRecord, partitionSessionHostDiagnosticsSessions, partitionSessionHostRecords, replayLaunchEnv, resolveAttachableRuntimeRecord, resolveInstanceConfigDir, resolveRuntimeRecord, resolveSessionHostIpcKey, sanitizeSpawnEnv, spawnCliChild, writeEnvelope };
package/dist/index.js CHANGED
@@ -52,6 +52,7 @@ __export(index_exports, {
52
52
  createSessionHostControlPlane: () => createSessionHostControlPlane,
53
53
  disconnectSessionHostClients: () => disconnectSessionHostClients,
54
54
  ensureNodePtySpawnHelperPermissions: () => ensureNodePtySpawnHelperPermissions,
55
+ findConnectedSessionHostClient: () => findConnectedSessionHostClient,
55
56
  formatRuntimeOwner: () => formatRuntimeOwner,
56
57
  getDefaultSessionHostEndpoint: () => getDefaultSessionHostEndpoint,
57
58
  getSessionHostRecoveryLabel: () => getSessionHostRecoveryLabel,
@@ -88,6 +89,7 @@ var SESSION_HOST_SUPPORTED_REQUEST_TYPES = [
88
89
  "send_input",
89
90
  "resize_session",
90
91
  "stop_session",
92
+ "stop_mesh_writer",
91
93
  "delete_session",
92
94
  "resume_session",
93
95
  "acquire_write",
@@ -818,6 +820,19 @@ function disconnectSessionHostClients(endpoint, error) {
818
820
  }
819
821
  }
820
822
  }
823
+ function findConnectedSessionHostClient(endpoint) {
824
+ for (const ref of activeClientRefs) {
825
+ const client = ref.deref();
826
+ if (!client) {
827
+ activeClientRefs.delete(ref);
828
+ continue;
829
+ }
830
+ if ((!endpoint || client.endpoint.path === endpoint.path) && client.isConnected()) {
831
+ return client;
832
+ }
833
+ }
834
+ return null;
835
+ }
821
836
  var SessionHostClient = class {
822
837
  endpoint;
823
838
  socket = null;
@@ -835,11 +850,17 @@ var SessionHostClient = class {
835
850
  if (typeof options.requestTimeoutMs === "number" && options.requestTimeoutMs > 0) {
836
851
  this.requestTimeoutMs = options.requestTimeoutMs;
837
852
  }
838
- activeClientRefs.add(new WeakRef(this));
853
+ if (options.registerAsShared !== false) activeClientRefs.add(new WeakRef(this));
839
854
  }
840
855
  static disconnectClients(endpoint, error) {
841
856
  disconnectSessionHostClients(endpoint, error);
842
857
  }
858
+ static findConnectedClient(endpoint) {
859
+ return findConnectedSessionHostClient(endpoint);
860
+ }
861
+ isConnected() {
862
+ return Boolean(this.socket && !this.socket.destroyed && establishedSockets.has(this.socket));
863
+ }
843
864
  /**
844
865
  * Fires when an established connection to the session host goes away.
845
866
  *
@@ -901,18 +922,32 @@ var SessionHostClient = class {
901
922
  }
902
923
  }
903
924
  }
904
- sendRawRequestOnSocket(socket, request) {
925
+ sendRawRequestOnSocket(socket, request, timeoutMs = this.requestTimeoutMs) {
905
926
  const requestId = generateUUID();
906
927
  const envelope = {
907
928
  kind: "request",
908
929
  requestId,
909
930
  request
910
931
  };
911
- const timeoutMs = this.requestTimeoutMs > 0 ? this.requestTimeoutMs : 3e4;
912
932
  const timeoutLabel = timeoutMs % 1e3 === 0 ? `${timeoutMs / 1e3}s` : `${timeoutMs}ms`;
933
+ const startedAt = Date.now();
934
+ const startedMonotonicAt = performance.now();
913
935
  return new Promise((resolve3, reject) => {
914
936
  const timeout = setTimeout(() => {
915
- const timeoutError = new Error(`Session host request timed out after ${timeoutLabel} (${request.type})`);
937
+ const actualElapsedMs = Math.max(0, Math.round(performance.now() - startedMonotonicAt));
938
+ const wallElapsedMs = Math.max(0, Date.now() - startedAt);
939
+ const observerLagMs = Math.max(0, actualElapsedMs - timeoutMs);
940
+ const timeoutError = Object.assign(
941
+ new Error(`Session host request timed out after ${timeoutLabel} (${request.type})`),
942
+ {
943
+ code: "SESSION_HOST_REQUEST_TIMEOUT",
944
+ requestType: request.type,
945
+ scheduledTimeoutMs: timeoutMs,
946
+ actualElapsedMs,
947
+ wallElapsedMs,
948
+ observerLagMs
949
+ }
950
+ );
916
951
  if (this.socket === socket) {
917
952
  this.disconnect(timeoutError);
918
953
  } else {
@@ -938,7 +973,7 @@ var SessionHostClient = class {
938
973
  socket.write(serializeEnvelope(envelope));
939
974
  });
940
975
  }
941
- async connect() {
976
+ async connect(timeoutMs = this.requestTimeoutMs) {
942
977
  if (this.socket && !this.socket.destroyed) {
943
978
  if (this.connectPromise) await this.connectPromise;
944
979
  return;
@@ -986,11 +1021,44 @@ var SessionHostClient = class {
986
1021
  });
987
1022
  const connectTask = (async () => {
988
1023
  await new Promise((resolve3, reject) => {
989
- socket.once("connect", () => {
1024
+ const cleanup = () => {
1025
+ clearTimeout(timer);
1026
+ socket.off("connect", connected);
1027
+ socket.off("error", failed);
1028
+ socket.off("close", closed);
1029
+ };
1030
+ const connected = () => {
1031
+ cleanup();
990
1032
  establishedSockets.add(socket);
991
1033
  resolve3();
992
- });
993
- socket.once("error", reject);
1034
+ };
1035
+ const failed = (error) => {
1036
+ cleanup();
1037
+ reject(error);
1038
+ };
1039
+ const closed = () => failed(new Error("Session host connection closed before connect"));
1040
+ const connectStartedAt = Date.now();
1041
+ const connectStartedMonotonicAt = performance.now();
1042
+ const timer = setTimeout(() => {
1043
+ const actualElapsedMs = Math.max(0, Math.round(performance.now() - connectStartedMonotonicAt));
1044
+ const wallElapsedMs = Math.max(0, Date.now() - connectStartedAt);
1045
+ const observerLagMs = Math.max(0, actualElapsedMs - timeoutMs);
1046
+ const error = Object.assign(
1047
+ new Error(`Session host connect timed out after ${timeoutMs}ms`),
1048
+ {
1049
+ code: "SESSION_HOST_CONNECT_TIMEOUT",
1050
+ scheduledTimeoutMs: timeoutMs,
1051
+ actualElapsedMs,
1052
+ wallElapsedMs,
1053
+ observerLagMs
1054
+ }
1055
+ );
1056
+ failed(error);
1057
+ if (this.socket === socket) this.disconnect(error);
1058
+ }, timeoutMs);
1059
+ socket.once("connect", connected);
1060
+ socket.once("error", failed);
1061
+ socket.once("close", closed);
994
1062
  });
995
1063
  if (this.attachedSessions.size > 0 && this.socket === socket && !socket.destroyed) {
996
1064
  for (const [sessionId, sub] of [...this.attachedSessions.entries()]) {
@@ -1020,11 +1088,46 @@ var SessionHostClient = class {
1020
1088
  this.eventListeners.delete(listener);
1021
1089
  };
1022
1090
  }
1023
- async request(request) {
1024
- await this.connect();
1091
+ async request(request, options = {}) {
1092
+ const timeoutMs = Math.max(1, options.timeoutMs ?? this.requestTimeoutMs);
1093
+ const deadline = Date.now() + timeoutMs;
1094
+ const alreadyConnected = this.isConnected() && !this.connectPromise;
1095
+ if (options.timeoutMs !== void 0) {
1096
+ let timer;
1097
+ const raceStartedAt = Date.now();
1098
+ const raceStartedMonotonicAt = performance.now();
1099
+ try {
1100
+ await Promise.race([
1101
+ this.connect(timeoutMs),
1102
+ new Promise((_resolve, reject) => {
1103
+ timer = setTimeout(() => {
1104
+ const actualElapsedMs = Math.max(0, Math.round(performance.now() - raceStartedMonotonicAt));
1105
+ const wallElapsedMs = Math.max(0, Date.now() - raceStartedAt);
1106
+ const observerLagMs = Math.max(0, actualElapsedMs - timeoutMs);
1107
+ const error = Object.assign(
1108
+ new Error(`Session host connect timed out after ${timeoutMs}ms`),
1109
+ {
1110
+ code: "SESSION_HOST_CONNECT_TIMEOUT",
1111
+ scheduledTimeoutMs: timeoutMs,
1112
+ actualElapsedMs,
1113
+ wallElapsedMs,
1114
+ observerLagMs
1115
+ }
1116
+ );
1117
+ reject(error);
1118
+ this.disconnect(error);
1119
+ }, timeoutMs);
1120
+ })
1121
+ ]);
1122
+ } finally {
1123
+ if (timer) clearTimeout(timer);
1124
+ }
1125
+ } else {
1126
+ await this.connect(timeoutMs);
1127
+ }
1025
1128
  const requestSocket = this.socket;
1026
1129
  if (!requestSocket) throw new Error("Session host socket unavailable");
1027
- const response = await this.sendRawRequestOnSocket(requestSocket, request);
1130
+ const response = await this.sendRawRequestOnSocket(requestSocket, request, options.timeoutMs === void 0 || alreadyConnected ? timeoutMs : Math.max(1, deadline - Date.now()));
1028
1131
  if (response.success) {
1029
1132
  if (request.type === "attach_session") {
1030
1133
  this.attachedSessions.set(request.payload.sessionId, {
@@ -1285,6 +1388,7 @@ function createSessionHostControlPlane(transport) {
1285
1388
  createSessionHostControlPlane,
1286
1389
  disconnectSessionHostClients,
1287
1390
  ensureNodePtySpawnHelperPermissions,
1391
+ findConnectedSessionHostClient,
1288
1392
  formatRuntimeOwner,
1289
1393
  getDefaultSessionHostEndpoint,
1290
1394
  getSessionHostRecoveryLabel,