(msg: SDKMessage)
| 201 | // await — messages with attachments just land in the queue slightly |
| 202 | // later, which is fine (web messages aren't rapid-fire). |
| 203 | async function handleInboundMessage(msg: SDKMessage): Promise<void> { |
| 204 | try { |
| 205 | const fields = extractInboundMessageFields(msg); |
| 206 | if (!fields) return; |
| 207 | |
| 208 | const { uuid } = fields; |
| 209 | |
| 210 | // Dynamic import keeps the bridge code out of non-BRIDGE_MODE builds. |
| 211 | const { resolveAndPrepend } = await import('../bridge/inboundAttachments.js'); |
| 212 | const rawContent = fields.content; |
| 213 | let sanitized: string | Array<{ type: string; [key: string]: unknown }> = |
| 214 | typeof rawContent === 'string' |
| 215 | ? rawContent |
| 216 | : (rawContent as unknown as Array<{ type: string; [key: string]: unknown }>); |
| 217 | if (feature('KAIROS_GITHUB_WEBHOOKS')) { |
| 218 | /* eslint-disable @typescript-eslint/no-require-imports */ |
| 219 | const { sanitizeInboundWebhookContent } = |
| 220 | require('../bridge/webhookSanitizer.js') as typeof import('../bridge/webhookSanitizer.js'); |
| 221 | /* eslint-enable @typescript-eslint/no-require-imports */ |
| 222 | if (typeof sanitized === 'string') { |
| 223 | sanitized = sanitizeInboundWebhookContent(sanitized); |
| 224 | } |
| 225 | } |
| 226 | const content = await resolveAndPrepend(msg, sanitized as string | ContentBlockParam[]); |
| 227 | |
| 228 | const preview = typeof content === 'string' ? content.slice(0, 80) : `[${content.length} content blocks]`; |
| 229 | logForDebugging(`[bridge:repl] Injecting inbound user message: ${preview}${uuid ? ` uuid=${uuid}` : ''}`); |
| 230 | enqueue({ |
| 231 | value: content, |
| 232 | mode: 'prompt' as const, |
| 233 | uuid, |
| 234 | // skipSlashCommands stays true as defense-in-depth — |
| 235 | // processUserInputBase overrides it internally when bridgeOrigin |
| 236 | // is set AND the resolved command passes isBridgeSafeCommand. |
| 237 | // This keeps exit-word suppression and immediate-command blocks |
| 238 | // intact for any code path that checks skipSlashCommands directly. |
| 239 | skipSlashCommands: true, |
| 240 | bridgeOrigin: true, |
| 241 | }); |
| 242 | } catch (e) { |
| 243 | logForDebugging(`[bridge:repl] handleInboundMessage failed: ${e}`, { level: 'error' }); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | // State change callback — maps bridge lifecycle events to AppState. |
| 248 | function handleStateChange(state: BridgeState, detail?: string): void { |
nothing calls this directly
no test coverage detected