( data: string, recentPostedUUIDs: BoundedUUIDSet, recentInboundUUIDs: BoundedUUIDSet, onInboundMessage: ((msg: SDKMessage) => void | Promise<void>) | undefined, onPermissionResponse?: ((response: SDKControlResponse) => void) | undefined, onControlRequest?: ((request: SDKControlRequest) => void) | undefined, )
| 222 | * server replayed history after a transport swap lost the seq-num cursor). |
| 223 | */ |
| 224 | export function handleIngressMessage( |
| 225 | data: string, |
| 226 | recentPostedUUIDs: BoundedUUIDSet, |
| 227 | recentInboundUUIDs: BoundedUUIDSet, |
| 228 | onInboundMessage: ((msg: SDKMessage) => void | Promise<void>) | undefined, |
| 229 | onPermissionResponse?: ((response: SDKControlResponse) => void) | undefined, |
| 230 | onControlRequest?: ((request: SDKControlRequest) => void) | undefined, |
| 231 | ): void { |
| 232 | try { |
| 233 | const parsed: unknown = normalizeControlMessageKeys(jsonParse(data)) |
| 234 | |
| 235 | // control_response is not an SDKMessage — check before the type guard |
| 236 | if (isSDKControlResponse(parsed)) { |
| 237 | logForDebugging('[bridge:repl] Ingress message type=control_response') |
| 238 | onPermissionResponse?.(parsed) |
| 239 | return |
| 240 | } |
| 241 | |
| 242 | // control_request from the server (initialize, set_model, can_use_tool). |
| 243 | // Must respond promptly or the server kills the WS (~10-14s timeout). |
| 244 | if (isSDKControlRequest(parsed)) { |
| 245 | logForDebugging( |
| 246 | `[bridge:repl] Inbound control_request subtype=${(parsed.request as { subtype?: string }).subtype}`, |
| 247 | ) |
| 248 | onControlRequest?.(parsed) |
| 249 | return |
| 250 | } |
| 251 | |
| 252 | if (!isSDKMessage(parsed)) return |
| 253 | |
| 254 | // Check for UUID to detect echoes of our own messages |
| 255 | const uuid = |
| 256 | 'uuid' in parsed && typeof parsed.uuid === 'string' |
| 257 | ? parsed.uuid |
| 258 | : undefined |
| 259 | |
| 260 | if (uuid && recentPostedUUIDs.has(uuid)) { |
| 261 | logForDebugging( |
| 262 | `[bridge:repl] Ignoring echo: type=${parsed.type} uuid=${uuid}`, |
| 263 | ) |
| 264 | return |
| 265 | } |
| 266 | |
| 267 | // Defensive dedup: drop inbound prompts we've already forwarded. The |
| 268 | // SSE seq-num carryover (lastTransportSequenceNum) is the primary fix |
| 269 | // for history-replay; this catches edge cases where that negotiation |
| 270 | // fails (server ignores from_sequence_num, transport died before |
| 271 | // receiving any frames, etc). |
| 272 | if (uuid && recentInboundUUIDs.has(uuid)) { |
| 273 | logForDebugging( |
| 274 | `[bridge:repl] Ignoring re-delivered inbound: type=${parsed.type} uuid=${uuid}`, |
| 275 | ) |
| 276 | return |
| 277 | } |
| 278 | |
| 279 | logForDebugging( |
| 280 | `[bridge:repl] Ingress message type=${parsed.type}${uuid ? ` uuid=${uuid}` : ''}`, |
| 281 | ) |
no test coverage detected