@adhdev/session-host-core 1.0.58-rc.2 → 1.0.58-rc.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.
package/dist/index.d.mts CHANGED
@@ -538,12 +538,43 @@ interface SessionHostClientOptions {
538
538
  endpoint?: SessionHostEndpoint;
539
539
  appName?: string;
540
540
  }
541
+ /**
542
+ * Why an established session-host connection went away.
543
+ * - `error` — socket emitted 'error' (includes ECONNRESET on abrupt host death)
544
+ * - `ended` — peer sent FIN ('end'): the host closed the connection cleanly
545
+ * - `closed` — socket fully closed ('close') without a preceding error/end
546
+ */
547
+ type SessionHostDisconnectReason = 'error' | 'ended' | 'closed';
548
+ interface SessionHostDisconnectInfo {
549
+ reason: SessionHostDisconnectReason;
550
+ endpointPath: string;
551
+ /** In-flight requests abandoned by this disconnect (they were rejected). */
552
+ pendingRequests: number;
553
+ error?: Error;
554
+ }
541
555
  declare class SessionHostClient {
542
556
  readonly endpoint: SessionHostEndpoint;
543
557
  private socket;
544
558
  private requestWaiters;
545
559
  private eventListeners;
560
+ private disconnectListeners;
561
+ /** Guards against emitting both an 'error'-path and a 'close'-path disconnect for one socket. */
562
+ private disconnectedSockets;
546
563
  constructor(options?: SessionHostClientOptions);
564
+ /**
565
+ * Fires when an established connection to the session host goes away.
566
+ *
567
+ * Before this existed the client registered only 'data' and 'error' handlers,
568
+ * so a *clean* FIN — the host process exiting normally, or dying to an
569
+ * uncaught exception that took the socket down without an error event on our
570
+ * end — ran nothing at all: `this.socket` stayed non-null pointing at a dead
571
+ * socket, in-flight waiters hung to their 30s timeout, and no caller was told.
572
+ * That silence is what let a session-host death present downstream as an
573
+ * unrelated application-level fault (a missing completion marker) instead of
574
+ * "the host is gone".
575
+ */
576
+ onDisconnect(listener: (info: SessionHostDisconnectInfo) => void): () => void;
577
+ private handleDisconnect;
547
578
  connect(): Promise<void>;
548
579
  onEvent(listener: (event: SessionHostEvent) => void): () => void;
549
580
  request<T = unknown>(request: SessionHostRequest): Promise<SessionHostResponse<T>>;
@@ -683,4 +714,4 @@ interface SessionHostControlTransport {
683
714
  */
684
715
  declare function createSessionHostControlPlane(transport: SessionHostControlTransport): SessionHostControlPlane;
685
716
 
686
- export { type AcquireWritePayload, type AttachSessionPayload, 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 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 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 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, createLineParser, createResponseEnvelope, createSessionHostControlPlane, ensureNodePtySpawnHelperPermissions, formatRuntimeOwner, getDefaultSessionHostEndpoint, getSessionHostRecoveryLabel, getSessionHostSurfaceKind, getWorkspaceLabel, isDefaultInstanceConfigDir, isSessionHostLiveRuntime, isSessionHostRecoverySnapshot, isTerminalRecord, partitionSessionHostDiagnosticsSessions, partitionSessionHostRecords, resolveAttachableRuntimeRecord, resolveInstanceConfigDir, resolveRuntimeRecord, resolveSessionHostIpcKey, sanitizeSpawnEnv, writeEnvelope };
717
+ export { type AcquireWritePayload, type AttachSessionPayload, 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 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 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, createLineParser, createResponseEnvelope, createSessionHostControlPlane, ensureNodePtySpawnHelperPermissions, formatRuntimeOwner, getDefaultSessionHostEndpoint, getSessionHostRecoveryLabel, getSessionHostSurfaceKind, getWorkspaceLabel, isDefaultInstanceConfigDir, isSessionHostLiveRuntime, isSessionHostRecoverySnapshot, isTerminalRecord, partitionSessionHostDiagnosticsSessions, partitionSessionHostRecords, resolveAttachableRuntimeRecord, resolveInstanceConfigDir, resolveRuntimeRecord, resolveSessionHostIpcKey, sanitizeSpawnEnv, writeEnvelope };
package/dist/index.d.ts CHANGED
@@ -538,12 +538,43 @@ interface SessionHostClientOptions {
538
538
  endpoint?: SessionHostEndpoint;
539
539
  appName?: string;
540
540
  }
541
+ /**
542
+ * Why an established session-host connection went away.
543
+ * - `error` — socket emitted 'error' (includes ECONNRESET on abrupt host death)
544
+ * - `ended` — peer sent FIN ('end'): the host closed the connection cleanly
545
+ * - `closed` — socket fully closed ('close') without a preceding error/end
546
+ */
547
+ type SessionHostDisconnectReason = 'error' | 'ended' | 'closed';
548
+ interface SessionHostDisconnectInfo {
549
+ reason: SessionHostDisconnectReason;
550
+ endpointPath: string;
551
+ /** In-flight requests abandoned by this disconnect (they were rejected). */
552
+ pendingRequests: number;
553
+ error?: Error;
554
+ }
541
555
  declare class SessionHostClient {
542
556
  readonly endpoint: SessionHostEndpoint;
543
557
  private socket;
544
558
  private requestWaiters;
545
559
  private eventListeners;
560
+ private disconnectListeners;
561
+ /** Guards against emitting both an 'error'-path and a 'close'-path disconnect for one socket. */
562
+ private disconnectedSockets;
546
563
  constructor(options?: SessionHostClientOptions);
564
+ /**
565
+ * Fires when an established connection to the session host goes away.
566
+ *
567
+ * Before this existed the client registered only 'data' and 'error' handlers,
568
+ * so a *clean* FIN — the host process exiting normally, or dying to an
569
+ * uncaught exception that took the socket down without an error event on our
570
+ * end — ran nothing at all: `this.socket` stayed non-null pointing at a dead
571
+ * socket, in-flight waiters hung to their 30s timeout, and no caller was told.
572
+ * That silence is what let a session-host death present downstream as an
573
+ * unrelated application-level fault (a missing completion marker) instead of
574
+ * "the host is gone".
575
+ */
576
+ onDisconnect(listener: (info: SessionHostDisconnectInfo) => void): () => void;
577
+ private handleDisconnect;
547
578
  connect(): Promise<void>;
548
579
  onEvent(listener: (event: SessionHostEvent) => void): () => void;
549
580
  request<T = unknown>(request: SessionHostRequest): Promise<SessionHostResponse<T>>;
@@ -683,4 +714,4 @@ interface SessionHostControlTransport {
683
714
  */
684
715
  declare function createSessionHostControlPlane(transport: SessionHostControlTransport): SessionHostControlPlane;
685
716
 
686
- export { type AcquireWritePayload, type AttachSessionPayload, 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 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 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 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, createLineParser, createResponseEnvelope, createSessionHostControlPlane, ensureNodePtySpawnHelperPermissions, formatRuntimeOwner, getDefaultSessionHostEndpoint, getSessionHostRecoveryLabel, getSessionHostSurfaceKind, getWorkspaceLabel, isDefaultInstanceConfigDir, isSessionHostLiveRuntime, isSessionHostRecoverySnapshot, isTerminalRecord, partitionSessionHostDiagnosticsSessions, partitionSessionHostRecords, resolveAttachableRuntimeRecord, resolveInstanceConfigDir, resolveRuntimeRecord, resolveSessionHostIpcKey, sanitizeSpawnEnv, writeEnvelope };
717
+ export { type AcquireWritePayload, type AttachSessionPayload, 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 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 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, createLineParser, createResponseEnvelope, createSessionHostControlPlane, ensureNodePtySpawnHelperPermissions, formatRuntimeOwner, getDefaultSessionHostEndpoint, getSessionHostRecoveryLabel, getSessionHostSurfaceKind, getWorkspaceLabel, isDefaultInstanceConfigDir, isSessionHostLiveRuntime, isSessionHostRecoverySnapshot, isTerminalRecord, partitionSessionHostDiagnosticsSessions, partitionSessionHostRecords, resolveAttachableRuntimeRecord, resolveInstanceConfigDir, resolveRuntimeRecord, resolveSessionHostIpcKey, sanitizeSpawnEnv, writeEnvelope };
package/dist/index.js CHANGED
@@ -627,14 +627,77 @@ function createLineParser(onEnvelope) {
627
627
  }
628
628
  };
629
629
  }
630
+ var establishedSockets = /* @__PURE__ */ new WeakSet();
630
631
  var SessionHostClient = class {
631
632
  endpoint;
632
633
  socket = null;
633
634
  requestWaiters = /* @__PURE__ */ new Map();
634
635
  eventListeners = /* @__PURE__ */ new Set();
636
+ disconnectListeners = /* @__PURE__ */ new Set();
637
+ /** Guards against emitting both an 'error'-path and a 'close'-path disconnect for one socket. */
638
+ disconnectedSockets = /* @__PURE__ */ new WeakSet();
635
639
  constructor(options = {}) {
636
640
  this.endpoint = options.endpoint || getDefaultSessionHostEndpoint(options.appName || "adhdev");
637
641
  }
642
+ /**
643
+ * Fires when an established connection to the session host goes away.
644
+ *
645
+ * Before this existed the client registered only 'data' and 'error' handlers,
646
+ * so a *clean* FIN — the host process exiting normally, or dying to an
647
+ * uncaught exception that took the socket down without an error event on our
648
+ * end — ran nothing at all: `this.socket` stayed non-null pointing at a dead
649
+ * socket, in-flight waiters hung to their 30s timeout, and no caller was told.
650
+ * That silence is what let a session-host death present downstream as an
651
+ * unrelated application-level fault (a missing completion marker) instead of
652
+ * "the host is gone".
653
+ */
654
+ onDisconnect(listener) {
655
+ this.disconnectListeners.add(listener);
656
+ return () => {
657
+ this.disconnectListeners.delete(listener);
658
+ };
659
+ }
660
+ handleDisconnect(socket, reason, error) {
661
+ if (this.disconnectedSockets.has(socket)) return;
662
+ this.disconnectedSockets.add(socket);
663
+ const wasEstablished = establishedSockets.has(socket);
664
+ if (!wasEstablished) {
665
+ if (this.socket === socket) this.socket = null;
666
+ try {
667
+ socket.destroy();
668
+ } catch {
669
+ }
670
+ const failure2 = error || new Error(`Session host connect failed (${this.endpoint.path})`);
671
+ for (const waiter of this.requestWaiters.values()) waiter.reject(failure2);
672
+ this.requestWaiters.clear();
673
+ return;
674
+ }
675
+ const pendingRequests = this.requestWaiters.size;
676
+ const failure = error || new Error(`Session host connection ${reason} (${this.endpoint.path})`);
677
+ for (const waiter of this.requestWaiters.values()) {
678
+ waiter.reject(failure);
679
+ }
680
+ this.requestWaiters.clear();
681
+ if (this.socket === socket) {
682
+ this.socket = null;
683
+ }
684
+ try {
685
+ socket.destroy();
686
+ } catch {
687
+ }
688
+ const info = {
689
+ reason,
690
+ endpointPath: this.endpoint.path,
691
+ pendingRequests,
692
+ error
693
+ };
694
+ for (const listener of this.disconnectListeners) {
695
+ try {
696
+ listener(info);
697
+ } catch {
698
+ }
699
+ }
700
+ }
638
701
  async connect() {
639
702
  if (this.socket && !this.socket.destroyed) return;
640
703
  if (this.socket) {
@@ -660,20 +723,19 @@ var SessionHostClient = class {
660
723
  }
661
724
  }));
662
725
  socket.on("error", (error) => {
663
- for (const waiter of this.requestWaiters.values()) {
664
- waiter.reject(error);
665
- }
666
- this.requestWaiters.clear();
667
- if (this.socket === socket) {
668
- this.socket = null;
669
- }
670
- try {
671
- socket.destroy();
672
- } catch {
673
- }
726
+ this.handleDisconnect(socket, "error", error);
727
+ });
728
+ socket.on("close", () => {
729
+ this.handleDisconnect(socket, "closed");
730
+ });
731
+ socket.on("end", () => {
732
+ this.handleDisconnect(socket, "ended");
674
733
  });
675
734
  await new Promise((resolve3, reject) => {
676
- socket.once("connect", () => resolve3());
735
+ socket.once("connect", () => {
736
+ establishedSockets.add(socket);
737
+ resolve3();
738
+ });
677
739
  socket.once("error", reject);
678
740
  });
679
741
  }
@@ -715,6 +777,7 @@ var SessionHostClient = class {
715
777
  if (!this.socket) return;
716
778
  const socket = this.socket;
717
779
  this.socket = null;
780
+ this.disconnectedSockets.add(socket);
718
781
  for (const waiter of this.requestWaiters.values()) {
719
782
  waiter.reject(new Error("Session host client closed"));
720
783
  }