Skip to content

[BREAKING] Python: Bind AG-UI workflow interrupts to the resolved thread id - #9365

Merged
Eduard van Valkenburg (eavanvalkenburg) merged 5 commits into
microsoft:mainfrom
eavanvalkenburg:ag-ui-omitted-thread-ids
Oct 15, 2026
Merged

Eduard van Valkenburg (eavanvalkenburg) merged 5 commits into
microsoft:mainfrom
eavanvalkenburg:ag-ui-omitted-thread-ids

Conversation

@eavanvalkenburg

@eavanvalkenburg Eduard van Valkenburg (eavanvalkenburg) commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Motivation & Context

AG-UI workflow requests that omit threadId resolved a thread id in two places that did not agree: AgentFrameworkWorkflow.run() generated one, and run_workflow_stream() generated a different one for RUN_STARTED. The id reported to the client was therefore not the id used for the workflow_factory cache, the Thread Snapshot, or the ownership of live request_info interrupts. A client could not continue a run with the id it was given, and the interrupts of runs without threadId were not tied to the id reported for that run.

Description & Review Guide

  • What are the major changes?
    • AgentFrameworkWorkflow.run() resolves the thread id once and passes it to run_workflow_stream(), so the protocol events, workflow_factory cache, Thread Snapshot, and live request_info ownership all use the same id. The client-supplied id is normalized in one place, so an empty id is treated as omitted everywhere.
    • Live request_info interrupts are owned by (Snapshot Scope, resolved thread id). Checkpoint ownership still binds client-supplied thread ids; a checkpoint saved by a run that omitted threadId also records the generated id as an alias, so the reported id and legacy id-less restores both resume it (same Snapshot Scope only).
    • A workflow_factory request that omits threadId but carries a resume entry, a tool result, or an approval response is rejected with WORKFLOW_RESUME_NOT_FOUND before a workflow instance is built. Message responses are detected with the same extraction the workflow stream uses. Checkpoint resumes are not affected.
    • Five existing tests that resumed a workflow without threadId now send one. New regression tests cover runs without threadId, and the package README and AGENTS.md document the behavior.
  • What is the impact of these changes?
    • Breaking: continuing a workflow run that started without threadId now requires sending the id reported in RUN_STARTED as threadId. A continuation that omits it is rejected with WORKFLOW_RESUME_NOT_FOUND. Clients that already send threadId are unaffected.
    • Breaking, workflow_factory only: a request without threadId that includes a tool result or approval response, including replayed history, is now rejected instead of starting a new run. Send threadId with such requests.
    • Sending the reported id now continues the run (live or from a checkpoint), reuses the workflow_factory instance, and hydrates the Thread Snapshot stored under that id.
    • Unchanged: client-supplied thread ids, Snapshot Scope handling, and legacy id-less checkpoint resumes.
  • What do you want reviewers to focus on?
    • Keeping checkpoint ownership on client-supplied thread ids, with the generated id only as a same-scope alias.
    • Detecting tool and approval responses by shape on workflow_factory requests without threadId, which also rejects replayed history that contains a tool result.
    • The compatibility note for continuations that omit threadId.

Related Issue

N/A - no existing issue covers this change.

AI Assistance

  • No material AI assistance was used.
  • This is an AI-assisted contribution. I reviewed, understood, and verified all submitted content and accept responsibility for it.

AI assistance details: Implementation, tests, documentation, and this PR description were AI-assisted.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR links to an agreed issue with no competing open PR, or the Related Issue section documents a trivial-change or repository-automation exception.
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

…ead id

Workflow runs that omit threadId now resolve a single thread id up front. The generated id is the
one reported in RUN_STARTED, keys the workflow_factory cache and Thread Snapshot, and owns the
run's live request_info interrupts, so a continuation must send it back as threadId. Checkpoint
ownership still binds only client-supplied thread ids, which keeps legacy unthreaded checkpoint
resumes working.
Copilot AI balanced review requested due to automatic review settings October 14, 2026 09:24
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python labels Oct 14, 2026
@agent-framework-automation agent-framework-automation Bot added the breaking change Usage: [PRs], Target: all PRs that introduce changes that are not backward compatible label Oct 14, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Factory-based omitted continuations and empty-ID checkpoint ownership do not consistently implement the documented behavior.

2 open findings
What changed in this PR

Aligns generated AG-UI workflow thread IDs across events, caching, snapshots, and live interrupt ownership.

Changes:

  • Resolves omitted thread IDs once and propagates them through workflow execution.
  • Adds regression coverage for continuation, scope isolation, and snapshots.
  • Documents the breaking continuation behavior.
File Description
python/​packages/​ag-ui/​agent_framework_ag_ui/​_workflow.py Resolves IDs and binds live interrupts.
python/​packages/​ag-ui/​agent_framework_ag_ui/​_workflow_run.py Accepts the pre-resolved thread ID.
python/​packages/​ag-ui/​tests/​ag_ui/​test_workflow_agent.py Adds wrapper-level regression tests.
python/​packages/​ag-ui/​tests/​ag_ui/​test_endpoint.py Updates and expands endpoint tests.
python/​packages/​ag-ui/​README.md Documents generated-ID continuation.
python/​packages/​ag-ui/​AGENTS.md Records thread ownership behavior.

🧠 Review effort: Balanced


💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread python/packages/ag-ui/agent_framework_ag_ui/_workflow.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_workflow.py

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (1 commit(s)): 8be122eaa148
Model: gpt-5.6-sol

Overview

The change consistently reuses one resolved Thread id for live workflow events, factory instances, snapshots, and request_info ownership, with strong regression coverage for foreign ids and Snapshot Scopes. Core workflow concurrency checks also prevent the generated-id path from creating simultaneous runs on one workflow instance. One durable-continuation gap remains: checkpoints created by an unthreaded run retain id-less ownership, so the newly advertised generated id cannot restore them.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
1 verified finding remained after source verification (1 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: python/packages/ag-ui/agent_framework_ag_ui/_workflow.py

Comment thread python/packages/ag-ui/agent_framework_ag_ui/_workflow.py Outdated
…stent

- Normalize the client-supplied thread id once, so an empty id is treated as omitted for both the
  resolved id and checkpoint ownership.
- Reject a workflow_factory resume that omits threadId before building a workflow instance.
- Record the generated thread id on checkpoints of runs that omitted threadId, so the id reported in
  RUN_STARTED resumes them while id-less restores keep working.
mypy and zuban infer one tuple type per variable, so the two loops now use separate names.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAF Automated Review — Iteration 2

Result: Findings reported
Scope: 2 net-new commit(s): 791508200d0f, c415256f7770
Model: gpt-5.6-sol

Overview

The change consistently normalizes omitted IDs and adds same-scope generated-ID aliases for durable checkpoint resumes, with focused tests covering scope isolation, legacy ID-less restores, and canonical resume rejection. However, the new factory-resume guard only recognizes canonical resume interrupt IDs, so the existing message-based continuation channel can still start a new workflow under a different generated ID.

Reviewed the supplied incremental change set across correctness, security/reliability, architecture, and failure behavior.
1 verified finding remained after source verification (1 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: python/packages/ag-ui/agent_framework_ag_ui/_workflow.py

Comment thread python/packages/ag-ui/agent_framework_ag_ui/_workflow.py Outdated
…tory workflows

A workflow_factory request that omits threadId reports a new generated id, so nothing can be pending on
it. Besides resume entries, reject tool results and approval responses, as the workflow stream defines
them, with WORKFLOW_RESUME_NOT_FOUND before a workflow instance is built, and document the behavior.
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_workflow.py
Merged via the queue into microsoft:main with commit 0d5af0f Oct 15, 2026
50 checks passed
@eavanvalkenburg
Eduard van Valkenburg (eavanvalkenburg) deleted the ag-ui-omitted-thread-ids branch October 15, 2026 07:19

This branch was previously deployed

1 inactive deployment
github-app-auth — 10375263 Deployed Oct 14, 2026 by eavanvalkenburg via add_label #25323
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Usage: [PRs], Target: all PRs that introduce changes that are not backward compatible documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants