ag-ui-protocol / ag-ui-protocol/ag-ui

[adk-middleware] Adopt interrupt-aware run lifecycle — emit RunFinished.outcome: interrupt and consume RunAgentInput.resume[]

Abierto
#2,031 0 comentarios 0 reacciones 0 asignados Ver en GitHub
enhancement
Lenguaje dominante
Python
Estrellas
15.9k
Forks
1.4k
Merge medio
1 d 17 h
PR fusionados (30 d)
163

Descripción

## Summary

The core SDKs landed the interrupt-aware run lifecycle in #1569 (spec: [`docs/concepts/interrupts.mdx`](https://github.com/ag-ui-protocol/ag-ui/blob/main/docs/concepts/interrupts.mdx)). That PR explicitly scoped framework integrations out: *"Framework integrations are explicitly out of scope — they will adopt against the now-finalized spec in a follow-up."* LangGraph and Strands have interrupt-protocol follow-ups (#1409, #1318, #2014); **ADK does not.** This issue tracks that gap.

Originally raised as the protocol-level ask in #170; that issue's protocol concern is now resolved by #1569, and this is the remaining ADK implementation work.

## Current behavior

`adk-middleware` does not participate in the interrupt lifecycle:

- `RunFinishedEvent` is emitted with **no `outcome` field** — the legacy "omitted → success" shape ([`adk_agent.py:1592`](https://github.com/ag-ui-protocol/ag-ui/blob/main/integrations/adk-middleware/python/src/ag_ui_adk/adk_agent.py#L1592), [`adk_agent.py:1875`](https://github.com/ag-ui-protocol/ag-ui/blob/main/integrations/adk-middleware/python/src/ag_ui_adk/adk_agent.py#L1875)).
- It never emits `outcome: { type: "interrupt", interrupts: [...] }`.
- It does not read `RunAgentInput.resume[]`; long-running/HITL results are round-tripped via the legacy tool-message path (`HITL_RESUME`, [`adk_agent.py:1793`](https://github.com/ag-ui-protocol/ag-ui/blob/main/integrations/adk-middleware/python/src/ag_ui_adk/adk_agent.py#L1793)).

Functionally LRO/HITL works (client tools wrapped in `LongRunningFunctionTool`, results returned as tool messages), but it does so via the pre-interrupt convention rather than the formal protocol contract.

## Proposed work

1. **Emit interrupt outcomes:** when an ADK run pauses on a long-running/HITL tool call, end the run with `RunFinished { outcome: { type: "interrupt", interrupts: [{ id, reason: "tool_call", toolCallId, responseSchema?, … }] } }` instead of a bare `RunFinished`. Emit required `StateSnapshot` / `MessagesSnapshot` before it per the spec's state-at-boundary rule.
2. **Consume `resume[]`:** read `RunAgentInput.resume[]`, correlate `interruptId` → `toolCallId`, and emit `ToolCallResult` against the original `toolCallId` **without** re-emitting `ToolCallStart/Args/End`.
3. **Support `approveWithEdits`:** honor `editedArgs` in the resume payload (full replacement, not merge) where applicable.
4. **Declare capabilities:** advertise `humanInTheLoop.interrupts` (and `approveWithEdits` if supported).
5. **Enforce contract rules:** pending interrupts block non-conforming input (rule 4 → `RunError`); `expiresAt` and `responseSchema` validation; idempotent replay of resumes.
6. **Back-compat:** keep the legacy tool-message HITL path working for clients on `maxVersion <= 0.0.52` (mirrors the `BackwardCompatibility_0_0_52` normalization in core).
7. **Tests:** interrupt-outcome emission, resume round-trip, parallel interrupts, approve-with-edits, expiry/validation `RunError` paths.

## References

- Spec: [`docs/concepts/interrupts.mdx`](https://github.com/ag-ui-protocol/ag-ui/blob/main/docs/concepts/interrupts.mdx)
- Capabilities: `humanInTheLoop.interrupts` / `approveWithEdits`
- Core lifecycle PR: #1569
- Original protocol ask: #170 · discussion #200
- Peer integration adoption: #1409, #1318, #2014

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.