| 228 | |
| 229 | // ── 3. Interrupts (LangGraph `interrupt()` → AG-UI custom event) ─ |
| 230 | onCustomEvent({ event }) { |
| 231 | if (aborted) return; |
| 232 | const e = event as { name?: string; value?: unknown }; |
| 233 | if (!e.name || !interruptEventNames.has(e.name)) return; |
| 234 | // LangGraph's AG-UI adapter ships the interrupt value as a JSON string |
| 235 | // in some shapes (and as an object in others). Normalize here so |
| 236 | // downstream schema validation always sees the parsed shape. |
| 237 | let value = e.value; |
| 238 | if (typeof value === "string") { |
| 239 | try { |
| 240 | value = JSON.parse(value); |
| 241 | } catch { |
| 242 | // Leave it as a string — the handler's schema will reject it |
| 243 | // explicitly with a clearer error. |
| 244 | } |
| 245 | } |
| 246 | pendingInterrupt = { eventName: e.name, value }; |
| 247 | }, |
| 248 | |
| 249 | // ── 4. Run lifecycle: finalise streams ───────────────────────────── |
| 250 | async onRunFinishedEvent() { |