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

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

Ouverte
#2,031 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
enhancement
Langage dominant
Python
Étoiles
15.9k
Forks
1.4k
Merge moyen
1 j 17 h
PR mergées (30 j)
163

Description

## 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

Guide de contribution

Ouvrir le guide de contribution

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.