MCPcopy
hub / github.com/codeaashu/claude-code / handleTransportPermanentClose

Function handleTransportPermanentClose

src/bridge/replBridge.ts:887–966  ·  view source on GitHub ↗

* Body of the transport's setOnClose callback, hoisted to initBridgeCore * scope so /bridge-kick can fire it directly. setOnClose wraps this with * a stale-transport guard; debugFireClose calls it bare. * * With autoReconnect:true, this only fires on: clean close (1000), * permanent s

(closeCode: number | undefined)

Source from the content-addressed store, hash-verified

885 * exhaustion. Transient drops are retried internally by the transport.
886 */
887 function handleTransportPermanentClose(closeCode: number | undefined): void {
888 logForDebugging(
889 `[bridge:repl] Transport permanently closed: code=${closeCode}`,
890 )
891 logEvent('tengu_bridge_repl_ws_closed', {
892 code: closeCode,
893 })
894 // Capture SSE seq high-water mark before nulling. When called from
895 // setOnClose the guard guarantees transport !== null; when fired from
896 // /bridge-kick it may already be null (e.g. fired twice) — skip.
897 if (transport) {
898 const closedSeq = transport.getLastSequenceNum()
899 if (closedSeq > lastTransportSequenceNum) {
900 lastTransportSequenceNum = closedSeq
901 }
902 transport = null
903 }
904 // Transport is gone — wake the poll loop out of its at-capacity
905 // heartbeat sleep so it's fast-polling by the time the reconnect
906 // below completes and the server re-queues work.
907 wakePollLoop()
908 // Reset flush state so writeMessages() hits the !transport guard
909 // (with a warning log) instead of silently queuing into a buffer
910 // that will never be drained. Unlike onWorkReceived (which
911 // preserves pending messages for the new transport), onClose is
912 // a permanent close — no new transport will drain these.
913 const dropped = flushGate.drop()
914 if (dropped > 0) {
915 logForDebugging(
916 `[bridge:repl] Dropping ${dropped} pending message(s) on transport close (code=${closeCode})`,
917 { level: 'warn' },
918 )
919 }
920
921 if (closeCode === 1000) {
922 // Clean close — session ended normally. Tear down the bridge.
923 onStateChange?.('failed', 'session ended')
924 pollController.abort()
925 triggerTeardown()
926 return
927 }
928
929 // Transport reconnect budget exhausted or permanent server
930 // rejection. By this point the env has usually been reaped
931 // server-side (BQ 2026-03-12: ~98% of ws_closed never recover
932 // via poll alone). stopWork(force=false) can't re-dispatch work
933 // from an archived env; reconnectEnvironmentWithSession can
934 // re-activate it via POST /bridge/reconnect, or fall through
935 // to a fresh session if the env is truly gone. The poll loop
936 // (already woken above) picks up the re-queued work once
937 // doReconnect completes.
938 onStateChange?.(
939 'reconnecting',
940 `Remote Control connection lost (code ${closeCode})`,
941 )
942 logForDebugging(
943 `[bridge:repl] Transport reconnect budget exhausted (code=${closeCode}), attempting env reconnect`,
944 )

Callers 1

wireTransportFunction · 0.85

Calls 7

logForDebuggingFunction · 0.85
logEventFunction · 0.85
onStateChangeFunction · 0.85
triggerTeardownFunction · 0.85
getLastSequenceNumMethod · 0.80
dropMethod · 0.80

Tested by

no test coverage detected