Skip to content

Python: HITL respond-URL addressing from inside workflows - #1

Closed
ahmedmuhsin wants to merge 30 commits into
feature/python-durabletask-subworkflows-multiworkflowfrom
feature/python-durabletask-workflow-hitl-context
Closed

Python: HITL respond-URL addressing from inside workflows#1
ahmedmuhsin wants to merge 30 commits into
feature/python-durabletask-subworkflows-multiworkflowfrom
feature/python-durabletask-workflow-hitl-context

Conversation

@ahmedmuhsin

Copy link
Copy Markdown
Owner

Motivation & Context

The durable function HITL flow pauses a workflow with request_info and waits for a human to POST a response to the /respond endpoint. The gap was that nothing inside the workflow could build that respond URL. An executor only receives its message payload, not the orchestration instanceId, and the requestId does not exist until request_info runs. So to email someone an approval link you had to pull both ids from the HTTP run response or poll the status endpoint from outside the app.

This came out of a real POC where someone wanted Agent B to email a reviewer when it finished, then resume to Agent C once approved, but had no way to address the running workflow from within it. This change lets a workflow build its own respond URL and notify a human from inside the graph with no caller plumbing of instanceId or requestId, and it works for nested sub workflows too.

Description & Review Guide

  • What are the major changes?

    • The orchestrator now surfaces orchestration metadata (the addressable root instanceId, the workflow name, and for nested workflows the request path prefix) onto each executor through the durable runner context. There is no new core API.
    • A new WorkflowHitlContext helper in agent-framework-azurefunctions builds the canonical respond and status URLs from that metadata. It returns None in process so the same executor degrades gracefully when it is not on the durable host.
    • WorkflowHitlContext.pending_request_id(ctx) reads the id request_info just generated back off the context, so the user never mints an id by hand. It works on any host through the existing core runner context protocol.
    • For nested sub workflows the address context propagates down call_sub_orchestrator via a new SUBWORKFLOW_ADDRESS_KEY marker, so an executor at any depth builds a URL that targets the top level instance with a qualified request id. That marker is stripped from untrusted input alongside the existing input marker, so a top level caller cannot forge it.
    • Samples 12 and 13 show the two step pattern. The review executor reads the request id back and sends it to a downstream notify executor that builds the URL, so a retried activity never emails a dead link because only the committed attempt reaches the notifier.
  • What is the impact of these changes?

    • Additive. No core change, nothing public removed, and top level behavior is unchanged when the helper is not used.
    • New unit tests cover the metadata round trip, the id read back, the nested ordinal and prefix agreement with the read side, marker stripping, and URL building. Integration tests assert that the URL the helper builds equals the server respondUrl and that posting to it resumes the run, for both the flat (12) and nested (13) samples.
  • What do you want reviewers to focus on?

    • The per executor ordinal used to build the nested prefix at dispatch has to match the enumerate index the status and respond endpoints use when they resolve a nested request. That agreement is the one spot where a mistake would send an emailed URL to the wrong child or a 404.

Related Issue

No separate issue. This is stacked on feature/python-durabletask-subworkflows-multiworkflow and targets it directly so the diff stays scoped to the HITL notify work.

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 is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • 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.

ahmedmuhsin and others added 30 commits July 7, 2026 14:43
…ows (microsoft#6696)

* feat(durabletask): add workflow naming helpers (multi-workflow phase 0)

Foundation for hosting multiple workflows (and later sub-workflows) on one
durable task host. Adds a host-agnostic naming module that derives the stable
durable names a hosted workflow registers under.

- New `_workflows/naming.py`:
  - `workflow_orchestrator_name(name)` -> `dafx-{name}` (orchestration name,
    aligned byte-for-byte with .NET `WorkflowNamingHelper`).
  - `workflow_name_from_orchestrator(name)` -> reverse, `None` when not prefixed.
  - `validate_workflow_name(name)` -> rejects empty / malformed / auto-generated
    `WorkflowBuilder-<uuid>` names (validate-and-reject rather than silently
    sanitize, since the name becomes a durable identity and an HTTP route segment).
  - `is_auto_generated_workflow_name(name)`, `DURABLE_NAME_PREFIX`.
- Export the helpers from the package public API.
- Mark `WORKFLOW_ORCHESTRATOR_NAME` deprecated in favor of per-workflow names
  (kept functional; the single-workflow path still uses it until phase 1).
- 39 unit tests covering round-trips and validation.

Design: docs/design/durabletask-multiworkflow-and-subworkflows.md

* feat(durabletask): host multiple workflows per worker with scoped names (phase 1)

Enables hosting more than one MAF workflow on a single standalone Durable Task
worker, and aligns both hosts on workflow-scoped durable names so two co-hosted
workflows that reuse an executor id cannot collide.

Naming (shared, host-agnostic):
- orchestration: dafx-{workflowName} (matches .NET; the name DT tooling surfaces)
- non-agent activity / agent entity: dafx-{workflowName}-{executorId} (scoped)
- New naming helpers workflow_scoped_executor_id / workflow_executor_activity_name.

Standalone worker (agent-framework-durabletask):
- configure_workflow is now additive: stores workflows keyed by Workflow.name,
  rejects duplicate / auto-generated (WorkflowBuilder-<uuid>) / invalid names,
  registers one orchestrator per workflow plus its scoped activities/entities.
- The shared orchestrator dispatches scoped names derived from workflow.name.
- New registered_workflow_names property.

Client (DurableWorkflowClient):
- Optional default workflow_name on the client; start/run/stream accept a per-call
  workflow_name and target dafx-{name}.
- Opt-in ownership validation on status/HITL methods: when a workflow name is
  resolvable, an instance whose orchestration name does not match is treated as
  not-found (status -> None, pending -> [], send_hitl_response / await -> raise),
  mirroring the Azure Functions route-scoping check.

Azure Functions host (agent-framework-azurefunctions):
- Registration now uses the same scoped names so the shared orchestrator's
  dispatch matches (single workflow per app for now; flat workflow/* routes kept).
- Workflow name is validated up front; workflow agents register under the scoped
  entity id; _is_workflow_orchestration scopes to dafx-{workflow.name}.

Samples + tests:
- Durable Task and Azure Functions workflow samples now name their workflow.
- Unit tests cover multi-workflow registration, name validation, client targeting,
  and ownership; integration tests target the named workflows.

WORKFLOW_ORCHESTRATOR_NAME remains exported (deprecated). This is a hard switch:
in-flight single-workflow instances created before upgrade (under the old
workflow_orchestrator name) will not resume.

Design: docs/design/durabletask-multiworkflow-and-subworkflows.md

* feat(azurefunctions): host multiple workflows per app with per-workflow routes (phase 2)

Completes multi-workflow hosting on the Azure Functions host, building on the
shared scoped-naming foundation from the worker phase.

AgentFunctionApp:
- New `workflows=` parameter accepting a list (keyed by each `Workflow.name`) or a
  name->Workflow mapping; the existing `workflow=` is a single-workflow alias.
  Both may be combined. Duplicate names and mapping-key/name mismatches are rejected.
- Each workflow registers its own `dafx-{name}` orchestration, workflow-scoped
  activities/entities, and per-workflow HTTP routes:
  `workflow/{name}/run`, `workflow/{name}/status/{instanceId}`,
  `workflow/{name}/respond/{instanceId}/{requestId}`. Routes are always
  per-workflow (even for a single workflow) so callers don't change URLs as an app
  grows from one workflow to many.
- Route ownership check is per-workflow (`_is_owned_orchestration(status, name)`):
  a leaked instance id for another orchestration -- or another workflow -- is
  treated as not-found, extending the route-scoping defense.
- `get_agent(context, name, workflow_name=...)` resolves a workflow agent under its
  scoped id; bare `agents=` registration keeps the standalone surface. New
  `workflows` introspection property; `.workflow` now returns the sole workflow
  (or None when several are hosted).
- Removed the now-unused flat-URL helper `_build_status_url` (handlers inline
  per-workflow URLs).

Samples + tests:
- Azure Functions workflow samples (09-12) name their workflow; integration tests
  target the per-workflow routes.
- Unit tests cover multi-workflow registration, duplicate/mapping/auto-name
  rejection, and per-workflow ownership.

Note: sample README / demo.http route docs are updated in the docs phase.

Design: docs/design/durabletask-multiworkflow-and-subworkflows.md

* feat(durabletask): sub-workflows via durable child orchestrations (phase 3)

Run WorkflowExecutor nodes as durable child orchestrations on both hosts.

- Protocol: add call_sub_orchestrator to WorkflowOrchestrationContext, implemented by the durabletask and Azure Functions adapters.

- Registration: planner classifies WorkflowExecutor as subworkflow_executors; collect_hosted_workflows walks nested workflows (parent first, deduped by name). Both hosts recursively register every nested workflow's orchestration/agents/activities once; only top-level workflows get HTTP routes. Names validated up front before any registration side effects.

- Orchestrator: dispatch WorkflowExecutor nodes via call_sub_orchestrator(dafx-{innerName}) with deterministic child instance ids ({instanceId}::{executorId}::{counter}), a trusted-input marker carrying nesting depth (bounded at 25), and outputs routed as messages (default) or parent outputs (allow_direct_output).

- Tests: registration/collect, orchestrator prepare/process/unwrap, recursive registration on both hosts. Sample: 11_subworkflow.

* feat(durabletask): sub-workflow HITL via qualified request ids (phase 4)

Surface a nested sub-workflow's human-in-the-loop request behind the top-level instance (B2 single addressing surface).

- Orchestrator records dispatched sub-workflow child instance ids in its custom status (subworkflows map) before suspending in task_all, so the read side can reach a child's pending request while the parent is paused.

- Read side (durabletask client get_pending_hitl_requests; AF status route) recurses into nested child statuses, qualifying each nested request id as {executorId}::{requestId} (accumulated for deeper nesting).

- Write side (durabletask client send_hitl_response; AF respond route) splits a qualified id on '::', resolves the owning child orchestration via the parent's subworkflows map, and raises the event on the leaf child with the bare request id. Unknown/inactive sub-workflow -> error/404.

- Shared SUBWORKFLOW_REQUEST_SEPARATOR ('::') in naming so both hosts and the client agree. respondUrl/respond always targets the top-level instance.

- Tests: TestSubworkflowHitl (durabletask client, 7), TestAgentFunctionAppSubworkflowHitl (AF, 7). Sample: 12_subworkflow_hitl (HITL pause inside an embedded sub-workflow).

* docs(durabletask): ADR + sample route docs for multi-workflow and sub-workflows (phase 5)

- Add ADR-0030 capturing the multi-workflow and sub-workflow hosting decisions (naming, scoped inner names, per-workflow routes, child-orchestration sub-workflows, hard-switch migration, B2 sub-workflow HITL, scoped agent addressing) with considered alternatives; mark the design doc as implemented and link the ADR.

- Update Azure Functions workflow samples (09-12) README/demo.http to the per-workflow route shape (workflow/{name}/run|status|respond) introduced in phase 2.

- Extend the durabletask sample catalog with the workflow hosting patterns (08-12), including the new 11_subworkflow and 12_subworkflow_hitl samples.

* fix(durabletask): harden sub-workflow hosting + add sub-workflow integration tests

Post-review hardening of the multi-workflow / sub-workflow durable hosting:

- Trust boundary: strip the reserved sub-workflow envelope key from untrusted
  client input at both host boundaries (DurableWorkflowClient.start_workflow and
  the AF start route) so a forged envelope cannot reach the trusted pickle path.
- Nested HITL addressing: qualify nested pending requests by (executorId, ordinal)
  using a '~' separator (was '::', which collided with core's auto::N functional
  request ids); the parent status subworkflows map is now a per-executor list so
  multiple children dispatched in one superstep stay independently addressable.
- Reject two different workflow instances that share a name (the same instance
  reused by sibling nodes is still deduped); validate executor ids (separator-free,
  length-bounded) when hosting durably.
- Remove the arbitrary sub-workflow nesting depth cap: a WorkflowExecutor wraps a
  concrete Workflow so the nesting tree is finite at build time, and the durable
  instance-id length limit is the natural ceiling (matches .NET, which has none).

Tests/samples:
- New durabletask integration tests for sub-workflow composition (11) and nested
  sub-workflow HITL (12); new no-agent AF sub-workflow HITL sample (13) + test.
- Exempt no-agent samples from the model-credential gate in both integration
  conftests so the nested-HITL plumbing is covered deterministically.
- Update durabletask sample 12 docs to the new qualified-id format.

Validated: 484 unit tests; durabletask integration 08/09/11/12 and AF 12/13 pass
against the live emulators; pyright 0 errors; ruff clean.

* fix(durabletask): address PR review feedback on naming, typing, and docs

- Unquote df.DurableOrchestrationClient annotations so pyupgrade passes.
- Narrow the split_subworkflow_request_id result before unpacking in a naming test so the strict type checkers pass.
- Correct the durabletask sample catalog to the {executor}~{ordinal}~{requestId} qualified id format.
- Reword the Azure Functions sub-workflow sample intro so it does not imply a difference from a same-numbered sample.
- Drop internal shorthand (B2, phase labels) from code comments.

* fix(durabletask): reject case-insensitive workflow name collisions

The route ownership guard compares the durable orchestration name with casefold(), but registration kept raw names as distinct keys. Hosting 'Orders' and 'orders' therefore succeeded while either workflow's status/respond route could operate on the other's instances. Reject case-insensitive name collisions at registration (within a composition via collect_hosted_workflows, and across registration calls via the case-folded _registered_orchestrations map and the top-level guard in both hosts) so the case-folded ownership boundary stays real. Single names of any case remain valid; only collisions are rejected.

* docs(durabletask): remove multiworkflow/subworkflow ADR and design docs

Drop the ADR and design exploration documents and the dangling docstring reference to them.

* refactor(durabletask): simplify workflow client status parsing and drop deprecated orchestrator-name symbols

Extract a shared _parse_custom_status helper in DurableWorkflowClient to remove duplicated custom-status JSON parsing across three call sites.

Drop the now-unused single-workflow compatibility shims WORKFLOW_ORCHESTRATOR_NAME and WorkflowRegistrationPlan.orchestrator_name, replaced by per-workflow workflow_orchestrator_name(name).

* fix(core): drop WORKFLOW_ORCHESTRATOR_NAME from agent_framework.azure re-exports

The constant was removed from agent-framework-durabletask, but the core azure lazy-loading namespace still re-exported it, breaking pyright in packages/core. Remove it from both the runtime _IMPORTS map and the .pyi stub.

* fix(durabletask): atomic multi-workflow registration and bubble sub-workflow events

Make configure_workflow / AgentFunctionApp registration atomic: check every cross-call name collision before mutating any state, so a colliding nested sub-workflow no longer leaves a host partially configured (with the top-level name stuck in the registry). Applied to both the standalone worker and the Functions app.

Bubble sub-workflow intermediate events: a workflow run as a child orchestration now returns a SUBWORKFLOW_RESULT_KEY envelope carrying its outputs plus event timeline, and the parent re-tags the child's intermediate events with the WorkflowExecutor node id and republishes them, matching the in-process WorkflowExecutor contract. Top-level runs still return a bare outputs list.

Adds cross-registration atomicity tests on both hosts and unit tests for the result envelope and event bubbling. Resolves review threads on _worker.py, orchestrator.py, and test coverage.

* fix(azurefunctions): widen workflow orchestrator wrapper return type

The shared run_workflow_orchestrator now returns list | dict (the sub-workflow result envelope), so the azurefunctions _workflow.py wrapper that delegates to it must widen its Generator return annotation to match. Caught by the package-level pyright in CI (Package Checks), which type-checks the whole package, not just the files changed in the previous commit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t#6933)

* Add security information to harness features xml docs

* Address PR comments
…ns (microsoft#6653)

* .NET: Replace internal AG-UI implementation with external ag-ui packages

Remove the in-tree Microsoft.Agents.AI.AGUI sources and consume the external
AG-UI .NET SDK packages (AGUI.Abstractions, AGUI.Formatting, AGUI.Protobuf,
AGUI.Client, AGUI.Server) at 0.1.0-preview instead.

- Microsoft.Agents.AI.Hosting.AGUI.AspNetCore keeps its own ASP.NET glue
  (MapAGUI / AddAGUI / SSE result) layered over the framework-agnostic
  AGUI.Server primitives (ToChatRequestContext / AsAGUIEventStreamAsync).
- Migrate call sites to the options-based AGUIChatClient constructor and recover
  the originating AG-UI input via ChatOptions.TryGetRunAgentInput.
- Multi-turn continuation flows through parentRunId + threadId on
  RawRepresentationFactory; shared state flows through RunAgentInput.State and is
  surfaced as StateSnapshotEvent raw representations.
- Update samples, hosting/unit/integration tests, and central package versions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add migration README for removed Microsoft.Agents.AI.AGUI package

Keep the package folder in place with a README explaining that the in-tree AG-UI protocol abstractions moved to the external AGUI.* NuGet packages, with a mapping of old namespaces to the new packages and a migration guide.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
…ion (microsoft#6937)

GitHubCopilotAgent never forwarded the Copilot SDK's skill_directories
(and disabled_skills) parameters to create_session/resume_session, so
native Copilot CLI skills could not be configured through the agent.

Add both as fields on GitHubCopilotOptions and forward them (with
runtime-override and empty-list-clears-defaults semantics matching
instruction_directories) in _create_session and _resume_session.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…a sample (microsoft#6983)

* Add multi-tenant hosting hosting security consideration to a2a sample

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…) (microsoft#5100)

Pass knowledge_source_params with include_reference_source_data=True for
each resolved knowledge source on the KnowledgeBaseRetrievalRequest, so
ref.source_data is populated when the source has source_data_fields
configured. Uses SearchIndexKnowledgeSourceParams (azure-search-documents
12.0.0) and resolves real source names for both created and existing
knowledge bases (avoids the prior 'None-source' name).

Fixes microsoft#5095

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
…ation (microsoft#4553)

* Python: Improve error message when TypeVar is used in handler registration

Fixes microsoft#4547. Adds early detection of unresolved TypeVar instances in:
- @handler decorator (both explicit and introspected type paths)
- @executor decorator (both explicit and introspected type paths)
- WorkflowContext type argument validation (direct and union members)

When a TypeVar is detected, a clear ValueError is raised with actionable
guidance to use concrete types via @handler(input=ConcreteType, output=ConcreteType).

* Address PR review: runtime-safe TypeVar detection and unit tests

- Add shared is_typevar() helper in _typing_utils.py that safely detects
  TypeVar from both typing and typing_extensions modules
- Replace all isinstance(x, TypeVar) calls with is_typevar() in
  _executor.py, _function_executor.py, and _workflow_context.py
- Add 18 unit tests covering TypeVar validation for @handler, @executor,
  and WorkflowContext[T] (explicit params, introspection, union members)

* Fix pyright error: add type annotation to _TYPEVAR_TYPES

Pyright's reportUnknownVariableType flagged the inferred type as
partially unknown. Adding an explicit `tuple[type, ...]` annotation
resolves the strict-mode check.

* Suppress pyright reportUnknownVariableType for _TYPEVAR_TYPES

Pyright cannot infer the runtime type of TypeVar constructors, so the
tuple elements resolve to type[Unknown]. A type annotation alone does
not satisfy strict mode — add an inline suppression for this specific
diagnostic since the unknown types are intentional (runtime TypeVar
class detection).

* Reject nested TypeVars in workflow annotations

---------

Co-authored-by: Kranthi Kumar Manchikanti <kmanchikanti@microsoft.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
…ormat` (microsoft#5884)

Bug
---
`RawAnthropicClient._prepare_options` forwards `response_format` as the
**deprecated** beta parameter `output_format={"type": "json_schema", "schema":
{...}}` plus the beta flag `structured-outputs-2025-11-13`. When the same
request also includes `tools`, Claude emits concatenated / malformed JSON —
e.g. three copies of the schema's empty default like
`{"matches":[]}{"matches":[]}{"matches":[]}` — instead of populating the
schema. Anthropic's GA shape — `output_config={"format": {"type":
"json_schema", "schema": {...}}}` — works correctly with tools.

Verified empirically on `agent-framework-anthropic` against
`claude-sonnet-4-6` for a structured-output workload that combined
`response_format` with a tool (`run_shell`); the deprecated path produced
the malformed concatenated output, the GA path did not.

Changes
-------
- Move `response_format` into `run_options["output_config"]["format"]` and
  stop adding the `structured-outputs-2025-11-13` beta flag (the GA path
  doesn't need it).
- Merge the format into any caller-supplied `output_config` so e.g.
  `output_config["effort"]` (adaptive-thinking effort level) survives the
  transformation.
- Drop the now-unused `STRUCTURED_OUTPUTS_BETA_FLAG` constant (private to
  this module — no external callers).
- `_prepare_response_format` keeps the same `{"type": "json_schema",
  "schema": ...}` return shape; the docstring is updated to point at the
  GA target.

Test plan
---------
- `uv run pytest packages/anthropic/tests` → 130 passed.
- New tests:
  - `test_prepare_options_uses_output_config_for_response_format` — the
    GA `output_config.format` shape is emitted, the deprecated
    `output_format` key is not, and the `structured-outputs-2025-11-13`
    beta flag is not added.
  - `test_prepare_options_preserves_caller_supplied_output_config_effort`
    — a caller-supplied `output_config["effort"]` survives the merge.
  - `test_prepare_options_no_response_format_omits_output_config` — no
    `output_config` is added implicitly when `response_format` is absent.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
…#5935)

Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
…icrosoft#6294)

* fix: use client_kwargs instead of invalid options kwarg in workflow sample

Workflow.run() does not accept an options parameter. The store=False
kwarg was silently ignored. Use client_kwargs to correctly forward it
to the underlying chat client.

Fixes microsoft#6293

* fix: use backend-neutral wording in client_kwargs comment

---------

Co-authored-by: Benke Qu <bequ@microsoft.com>
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Python: Fix response metadata construction

Propagate complete AgentResponse metadata through core response construction and provider finalization paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Refine Ollama response metadata parsing

Filter Ollama usage details to real token counts and only propagate streaming finish metadata from final chunks.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Address response metadata review comments

Accumulate Copilot non-streaming usage events and keep structured response value parsing lazy for provider hooks.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Consolidate Dependabot dependency updates

* Restore method assignment suppression
Mypy intentionally targets Python 3.10 for test typing, but NumPy 2.5 stubs include Python 3.12 type statement syntax. Skip following NumPy stubs so dependency maintenance can validate the repository tests without parsing NumPy internals.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Lazy load root agent_framework exports

Move the root public API to lazy runtime exports backed by a typed stub, keep Runner deprecation handling in the owning workflow runner module, and document the maintenance pattern.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Tighten harness factory typing

Add a private harness stub so create_harness_agent has a fully known public signature without depending on agent-framework-tools at runtime.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address lazy root export review comments

Harden the circular import guard and add root export smoke tests covering representative lazy imports, star imports, and root stub export synchronization.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ot-required function bypassing (microsoft#6970)

* Remove experimental flags for RequirePerServiceCallChatHistoryPersistence and DisableApprovalNotRequiredFunctionBypassing

* Address PR comments

* Fix failing test
)

* Revise Python hosting channels ADR

Refocus the accepted-but-unreleased Python hosting channels ADR on protocol-specific Agent Framework conversion helpers and an optional execution-state host instead of a channel route-contribution framework.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Align hosting ADR with split state helpers

Update the protocol-helper ADR to reflect AgentState and WorkflowState, plain SessionStore and CheckpointStore behavior, explicit post-run session storage, workflow checkpoint storage, and direct WorkflowBuilder/orchestration-builder support.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Generalize protocol helper taxonomy

Add protocol-neutral helper families for run conversion, result rendering, streaming, session-id extraction, and command/action parsing. Classify protocol-specific helpers based on quick scans across Activity/Bot Framework, Discord, A2A, and MCP.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify stream helper naming

Use the single <protocol>_stream_from_run(...) helper naming convention in the hosting protocol-helper ADR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use state-level storage helpers in hosting ADR

Update ADR examples so app code calls AgentState.set_session and WorkflowState.set_checkpoint_storage instead of reaching into underlying stores directly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address hosting ADR review comments

Clarify fail-closed Foundry isolation helpers, fix workflow checkpoint resume examples, describe durable checkpoint cursor storage, add caller-owned session authorization comments, and switch the Django sketch to an async view.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify workflow checkpoint state in hosting ADR

Keep WorkflowState focused on resolving workflow targets, use existing CheckpointStorage directly, describe app-owned checkpoint cursor storage, and mark appendix code as minimum-shape sketches rather than runtime-ready samples.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Rename stream helper convention

Use <protocol>_from_streaming_run(...) as the protocol-helper naming convention for rendering streaming run output.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* added notes on state and continuity

* updates based on review

* added consulted

* updates based on review

* remove pyright for illustrative code

* Add streaming to Responses ADR sketch

Extend the FastAPI appendix sketch with the streaming branch and note that the Django sketch omits streaming to avoid duplicating the same state/finalization pattern.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* added note on extending the server

* added note on responsible for

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…icrosoft#5366) (microsoft#6528)

* Python: add ATR validation FunctionMiddleware sample (execution-boundary validation, microsoft#5366)

Adds python/samples/02-agents/middleware/atr_validation_middleware.py: a
FunctionMiddleware that validates tool arguments at the execution boundary and
raises MiddlewareTermination before call_next() when they match an attack
pattern, so the tool never runs. This is the deterministic, single-enforcement-
point pattern named in microsoft#5366 and answers its open follow-up about a recommended
validation-at-execution-boundary sample.

The check is a small self-contained deny-list mirroring Agent Threat Rules (ATR)
intent (prompt injection, exfiltration, credential access in tool args); a
docstring notes how to swap in the full open ruleset via pyatr. No external
dependency, so the sample stays import-clean.

Updates the middleware README Files table.

Signed-off-by: Adam Lin <adam@agentthreatrule.org>

* Python: Samples: run the real ATR engine in atr_validation_middleware

Address review on microsoft#6528:
- Load and run the real ATR ruleset via pyatr (ATREngine + AgentEvent
  tool_call event) instead of re-implementing a regex deny-list; the
  built-in deny-list is now only a fallback when pyatr is not installed.
- Add re.DOTALL (and a whole-text scan) to the fallback patterns so
  multiline injection payloads are not missed.
- Move load_dotenv() into main() so importing the module has no side
  effects.
- Route the middleware block/allow messages through a module logger
  instead of print().
- Include the matched ATR rule id in the log and in the
  MiddlewareTermination message for auditability.
- Update the middleware README entry to match.

* fix(samples): make ATR validation middleware pass ty/pyrefly typing CI

Resolve the three type-checker errors flagged on the samples typing jobs
(ty + pyrefly, reportMissingImports/reportAttributeAccessIssue via pyright):

- pyatr is an optional, unstubbed runtime dependency that is not installed
  in the typing CI env; mark its imports with `# type: ignore` so the
  unresolved-import error is suppressed while keeping the graceful
  ImportError -> deny-list fallback intact.
- Replace the function-attribute engine cache
  (`_detect_with_atr._engine`), which ty/pyrefly reject, with a clean
  `functools.lru_cache`-backed `_load_atr_engine()` loader.
- Type the argument-scanning helpers to accept the real
  `FunctionInvocationContext.arguments` type (`BaseModel | Mapping[str, Any]`)
  and normalise a pydantic model via `model_dump()` before scanning, fixing
  the invalid-argument-type error.

ty / pyrefly / pyright (samples config) / ruff check + format all clean on
the file; runtime block/allow behaviour verified for both dict and BaseModel
arguments.

* Python: Samples: simplify ATR middleware to plain pyatr import

Address review feedback (@eavanvalkenburg): now that the sample runs the
real pyatr engine, drop the optional-import scaffolding.

- Add a dependency header declaring pyatr (pip install pyatr).
- Switch to a plain top-level `import pyatr` and remove the
  try/except ImportError fallback path.
- Remove the regex deny-list (_FALLBACK_PATTERNS, _detect_with_fallback);
  keep 2-3 representative pattern shapes inline as a reference comment so
  readers still see the kind of rules ATR encodes. Detection is now a
  single straight-line engine call.
- Keep the prior typing fixes: `# type: ignore` on the pyatr import
  (unstubbed, absent in the typing CI env), the functools.lru_cache
  engine loader, and the BaseModel | Mapping[str, Any] signatures.

* fix: use PEP 723 inline script metadata for sample dependencies

---------

Signed-off-by: Adam Lin <adam@agentthreatrule.org>
Co-authored-by: eeee2345 <eeee2345@users.noreply.github.com>
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Python: Remove experimental marker from Skills API

Promote the Skills feature from experimental to stable, mirroring
.NET PR microsoft#6861. Removes the @experimental(SKILLS) decorators from the
skills APIs and the SKILLS ExperimentalFeature enum member, updates
tests and samples accordingly. MCP skills (MCP_SKILLS) remain
experimental, matching the .NET change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add experimental-stage assertions for MCP skills types

Guard MCPSkill, MCPSkillResource, and MCPSkillsSource against accidental
promotion by asserting their docstring warning block and
__feature_stage__/__feature_id__ metadata remain experimental (MCP_SKILLS).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove redundant stable-stage test for Skills API

Drop TestSkillsStableStage: asserting the absence of experimental
markers on a released API is not meaningful, and the feature-stage
decorator machinery is already covered by test_feature_stage.py.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…API (issue microsoft#3629) (microsoft#6225)

* Fix: Skip web_search_options for Azure OpenAI Chat Completions API

Azure OpenAI Chat Completions API does not support the web_search_options
parameter. Sending it results in a 400 error: 'Unknown parameter:
web_search_options'.

This fix:
- Stores the use_azure_client flag during initialization
- In _prepare_tools_for_openai, skips web search tools when the client
  is Azure-based, logging a warning that guides users to the Responses
  API (OpenAIChatClient) for web search support on Azure

Closes microsoft#3629

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: raise ValueError instead of silently ignoring web search on Azure

Address review feedback: silent logger.warning was too easy to miss.
Raising ValueError ensures callers know immediately that web search is
incompatible with Azure Chat Completions and directs them to the
Responses API alternative.

- Changed logger.warning to ValueError in _prepare_tools_for_openai
- Added test_prepare_tools_with_web_search_on_azure_raises
- Added test_prepare_tools_with_web_search_on_openai_allowed

* Fix Azure web search test regex

---------

Co-authored-by: Autumn <Autumn@Autumns-MacBook-Air.local>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Python: Add AG-UI approval state store

Key decisions: introduce a bounded process-local server-side Approval State store for AG-UI agent approvals; scope pending approval validation by AG-UI thread id plus the endpoint's configured server-side scope when present; fail closed when approval-like resume decisions arrive without matching server-owned pending Approval State, covering replayed and wrong-scope attempts without requiring Thread Snapshot persistence.

Files changed: packages/ag-ui/agent_framework_ag_ui/_approval_state.py adds the approval-only in-memory store and scoped thread-key helper; _agent.py owns the default store; _endpoint.py forwards the configured scope to approval handling independently of snapshot persistence; _agent_run.py keys pending approvals by scoped thread id and rejects approval resumes with missing state; tests/ag_ui/test_endpoint.py covers successful default resumes, replay failure, and wrong-scope failure without a snapshot store.

Verification: uv run pytest focused approval resume tests -q; uv run poe test -P ag-ui; uv run poe syntax -P ag-ui -C; uv run poe pyright -P ag-ui; git diff --check; git diff --cached --check. uv run poe typing -P ag-ui and uv run poe check -P ag-ui currently stop in mypy before package checking on .venv/lib/python3.13/site-packages/numpy/__init__.pyi using Python 3.12 type-statement syntax under the configured Python 3.11 target.

Notes: local issue/PRD planning artifacts were not staged. Follow-up slices still own already-approved sibling release, queued/auto-approved approval state preservation, replayable tool result persistence, synthetic-skip tightening, and final security/invariant coverage.

* Python: Release AG-UI approved siblings on resume

Key decisions: preserve core already-approved approval request groups inside AG-UI server-side Approval State for the visible approval interrupt; restore those siblings as server-generated approval responses only after the visible canonical resume passes server-owned validation; keep cancelled visible approvals fail-closed without executing or fabricating sibling results.

Files changed: packages/ag-ui/agent_framework_ag_ui/_agent_run.py stores hidden already-approved sibling approval requests with pending approval entries and rehydrates them during resume; packages/ag-ui/tests/ag_ui/test_endpoint.py adds mixed approval-batch endpoint coverage for approved, rejected, and cancelled visible approvals.

Verification: uv run pytest focused mixed approval sibling tests -q; uv run pytest neighboring approval endpoint tests -q; uv run poe syntax -P ag-ui -C; uv run poe pyright -P ag-ui; uv run poe test -P ag-ui; git diff --check; git diff --cached --check. uv run poe typing -P ag-ui and uv run poe check -P ag-ui pass pyright/pyrefly/ty/zuban for this change but still stop in mypy before package checking on .venv/lib/python3.13/site-packages/numpy/__init__.pyi using Python 3.12 type-statement syntax under the configured Python 3.11 target.

Notes: local .ralph, PRD, and CONTEXT.md planning artifacts were not staged. Follow-up slices still own queued/auto-approved approval state preservation, replayable tool result persistence, synthetic-skip tightening, and final security/invariant coverage.

* Python: Preserve AG-UI queued approval state

Key decisions: persist only the core tool-approval state bag inside the AG-UI server-side Approval State Store, keyed by the scoped AG-UI approval thread id; restore that approval-only state into each per-run AgentSession before approval resolution; pop server-collected auto-approved responses into validated server-generated approval messages so they execute exactly like resumed approvals without trusting client state.

Files changed: packages/ag-ui/agent_framework_ag_ui/_approval_state.py stores bounded tool approval state alongside pending approval entries; _agent.py passes the shared store into agent runs; _agent_run.py restores/saves tool approval state and drains collected auto-approved responses through existing pending-approval validation; packages/ag-ui/tests/ag_ui/test_endpoint.py covers queued approval surfacing and auto-approved response execution through SSE behavior.

Verification: uv run pytest focused queued/auto approval endpoint tests -q; uv run pytest neighboring approval endpoint tests -q; uv run poe syntax -P ag-ui -C; uv run poe pyright -P ag-ui; uv run poe test -P ag-ui; git diff --check; git diff --cached --check. uv run poe check -P ag-ui still stops in mypy before package checking on .venv/lib/python3.13/site-packages/numpy/__init__.pyi using Python 3.12 type-statement syntax under the configured Python 3.11 target; syntax, pyright, pyrefly, ty, and zuban portions completed successfully before that failure.

Notes: local .ralph, PRD, and CONTEXT.md planning artifacts were not staged. Follow-up slices still own replayable tool result persistence, synthetic-skip tightening, and final security/invariant coverage.

* Python: Persist AG-UI approved tool results

Key decisions: fold approval-resolved function_result messages into AG-UI Thread Snapshot history under their original tool call ids; strip server-generated canonical function_approvals resume controls from replayable snapshots; keep live TOOL_CALL_RESULT emission unchanged while preserving next-turn provider history validity.

Files changed: packages/ag-ui/agent_framework_ag_ui/_agent_run.py adds snapshot merge helpers for approval-resolved tool results; packages/ag-ui/tests/ag_ui/test_endpoint.py covers mixed approval batch resume, hydration, and next-turn replay through observable endpoint behavior.

Verification: uv run pytest focused replayable approval endpoint test -q; uv run pytest neighboring approval replay tests and test_approval_result_event.py -q; uv run poe syntax -P ag-ui -F; uv run poe syntax -P ag-ui -C; uv run poe pyright -P ag-ui; uv run poe test -P ag-ui; git diff --check; git diff --cached --check. uv run poe check -P ag-ui still stops in mypy before package checking on .venv/lib/python3.13/site-packages/numpy/__init__.pyi using Python 3.12 type-statement syntax under the configured Python 3.11 target; syntax, pyright, pyrefly, ty, and zuban portions completed successfully before that failure.

Notes: local .ralph, PRD, and CONTEXT.md planning artifacts were not staged. Follow-up slices still own synthetic-skip tightening and final security/invariant coverage.

* Python: Limit AG-UI synthetic skipped results

Key decisions: treat server-owned Approval State, current approval resume decisions, and existing replayable tool results as non-abandoned tool calls for AG-UI sanitizer repair; keep the defensive skipped-result fallback for genuinely abandoned tool calls; reject client-injected tool results as insufficient to satisfy pending server-owned Approval State.

Files changed: packages/ag-ui/agent_framework_ag_ui/_message_adapters.py adds protected tool-call context to synthetic skip injection; packages/ag-ui/agent_framework_ag_ui/_agent_run.py derives protected ids from pending approvals and stored approval-only state; packages/ag-ui/tests/ag_ui/test_message_adapters.py and test_endpoint.py cover protected pending calls, resume decisions, abandoned-call repair, and forged tool-result behavior.

Verification: uv run pytest focused sanitizer red/green tests -q; uv run pytest focused pending-approval endpoint tests -q; uv run pytest package sanitizer plus neighboring approval endpoint tests -q; uv run poe syntax -P ag-ui -C; uv run poe pyright -P ag-ui; uv run poe test -P ag-ui; git diff --check; git diff --cached --check. uv run poe typing -P ag-ui passes pyright/pyrefly/ty/zuban but still stops in mypy before package checking on .venv/lib/python3.13/site-packages/numpy/__init__.pyi using Python 3.12 type-statement syntax under the configured Python 3.11 target.

Notes: local .ralph, PRD, and CONTEXT.md planning artifacts were not staged. Follow-up slice still owns final AG-UI approval repair security and exact-once invariant coverage.

* Python: Verify AG-UI approval invariants

Key decisions: cover final AG-UI approval repair invariants at the FastAPI endpoint seam; treat wrong-thread resumes, client-supplied approval message spoofing, and client-injected approval state as non-executing fail-closed paths; assert exact-once replayable tool results for completed approval batches; document that Approval State is process-local and production authentication, authorization, and deployment/storage durability remain application responsibilities.

Files changed: packages/ag-ui/tests/ag_ui/test_endpoint.py adds endpoint-observable security and exact-once coverage; packages/ag-ui/README.md documents Approval State production responsibilities.

Verification: uv run pytest packages/ag-ui/tests/ag_ui/test_endpoint.py -q -k 'approval_resume_wrong_thread or approval_function_name_mismatch_message or approval_argument_mismatch_message or approval_client_fields_do_not_mutate or approval_resume_persists_replayable_tool_results'; uv run poe syntax -P ag-ui -F; uv run poe syntax -P ag-ui -C; uv run poe pyright -P ag-ui; uv run poe test -P ag-ui; uv run poe test-typing -P ag-ui --checker pyright; git diff --check; git diff --cached --check.

Notes: local .ralph, PRD, and CONTEXT.md planning artifacts were not staged. This completes the final AG-UI approval repair security and invariant coverage slice.

* Python: Clear AG-UI queued approvals on cancel

* Python: Address AG-UI approval review feedback
…6977)

* Python: Add refresh_interval (TTL) to CachingSkillsSource

Port .NET's CachingAgentSkillsSourceOptions.RefreshInterval to the Python
skills cache. Previously CachingSkillsSource cached a source's skill list
indefinitely (only clearing on a failed fetch), so callers had no built-in
way to periodically re-discover skills whose backing source changes at
runtime (notably MCPSkillsSource over the network).

CachingSkillsSource now accepts an optional refresh_interval (timedelta):
a cached list older than the interval is treated as stale and re-fetched on
the next call. When None (default) the cache never expires, so existing
behavior is unchanged. Freshness is measured with a monotonic clock via a
monkeypatchable _monotonic() helper. SkillsProvider.__init__ and from_paths
expose a cache_refresh_interval kwarg threaded into the built-in cache.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Address review feedback on CachingSkillsSource refresh_interval

- from_paths: do not forward cache_refresh_interval when disable_caching=True,
  matching the docstring and avoiding a TypeError for legacy subclass __init__
  signatures.
- Correct docstring/AGENTS.md wording: a failed fetch does not update the cache
  (initial failure leaves it empty; a refresh failure keeps the prior list),
  rather than "resetting"/"leaving empty" in all cases.
- Fix test typing: narrow provider._source via isinstance before accessing
  inner_source/_refresh_interval so ty/zuban/mypy/pyright all resolve them.
- Add regression tests for disable_caching + interval and legacy-subclass paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Do not forward cache_refresh_interval from from_paths into __init__

The refresh interval is already baked into the composed CachingSkillsSource
that from_paths builds, and __init__ leaves a caller-supplied source
un-wrapped, so forwarding cache_refresh_interval into cls(...) was a no-op
for caching behavior while breaking legacy subclasses whose __init__ predates
the kwarg (with caching enabled or disabled). Remove the forwarding entirely.

Strengthen the regression test to cover the real break: a legacy subclass
calling from_paths(paths, cache_refresh_interval=...) with caching enabled
must not raise and the composed source still carries the interval.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Drop _monotonic wrapper; call time.monotonic() directly

Address review feedback: remove the _monotonic() helper that existed only to
aid testing. CachingSkillsSource now calls time.monotonic() inline, and the
refresh-interval tests monkeypatch time.monotonic directly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Restore main's AGENTS.md sections lost in merge resolution

The merge used 'checkout --ours' for AGENTS.md, which took the whole file
from this branch and inadvertently reverted main's non-conflicting additions
(the __init__.pyi tree entry and the 'Root Public API' section). Restore
main's version and re-apply only the intended SkillsSource decorators change
(refresh_interval docs + reworded cache-failure semantics).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rosoft#6606)

* fix: use writable skills download directory

* fix: handle empty skills download directory override

---------

Co-authored-by: malsabbagh05 <malsabbagh05@users.noreply.github.com>
Co-authored-by: Tao Chen <taochen@microsoft.com>
* Add progressive MCP disclosure

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address progressive MCP review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Document progressive MCP loader name collisions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address progressive MCP review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix progressive MCP test typing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Track progressive MCP warning feature id

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Document internal typing helper guidance

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix README stars badge link

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Support batch progressive MCP load unload

Allow progressive MCP load_tool and unload_tool to accept either a single tool name or a list of tool names, applying successful changes in batches with per-tool model-visible results.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
…ecutors

Let a workflow notify a human reviewer (e.g. email an approval link) from inside the
graph, without the caller threading the instanceId/requestId by hand.

- durabletask: the orchestrator injects host_context {instance_id, workflow_name,
  request_path_prefix} into each activity input; CapturingRunnerContext surfaces it as
  host_metadata. No new core API.
- azurefunctions: WorkflowHitlContext.from_context(ctx) builds the canonical
  respond/status URLs (returns None in-process so callers degrade gracefully).
  Re-exported through the agent_framework.azure lazy namespace.
- Nested sub-workflows: the address context (root instance + workflow name +
  accumulated {executor}~{ordinal}~ prefix) propagates down call_sub_orchestrator via a
  new SUBWORKFLOW_ADDRESS_KEY marker, so an executor at any depth builds a URL that
  targets the addressable top-level instance with a qualified request id. The per-child
  ordinal matches the read-side enumerate() index used by the status/respond endpoints.
  The marker is stripped from untrusted input alongside SUBWORKFLOW_INPUT_KEY
  (confused-deputy / info-leak guard).
- Samples 12 and 13 reworked into the retry-safe two-step notify pattern: the emitter
  generates an explicit request id and a downstream NotifyExecutor builds the URL and
  notifies, so failed upstream retries never produce a dead link.

Tests: unit coverage for the metadata round-trip, address/ordinal agreement (fan-out at
depth and nested prefix accumulation), marker stripping, and URL building; integration
tests assert the helper-built URL equals the server respondUrl and resumes the run, for
both the flat (12) and nested (13) samples.
…g one in samples

Add WorkflowHitlContext.pending_request_id(ctx), an async helper that returns the
id request_info just generated (read from the runner context's pending request-info
events). This works on any host via the core RunnerContext protocol method, so it
needs no core change.

Samples 12 and 13 now call request_info() and read the id back to forward to the
NotifyExecutor, instead of minting a uuid by hand and passing request_id=. The
read-back happens in the same activity execution that generated the id, so the
pending request event and the notify message still commit together with the same id
(retry-safe; failed upstream retries notify no one).
…afety

Tighten pending_request_id docstring to require calling it immediately after request_info, and explain why that is safe on the durable host (each executor runs in its own activity with its own runner context, so the pending set only holds this executor's requests and the newest is the one just emitted). Document the two-step notify pattern in the 12 and 13 sample READMEs, including the downstream-notifier retry safety and the nested address-prefix propagation.
@ahmedmuhsin
ahmedmuhsin force-pushed the feature/python-durabletask-workflow-hitl-context branch from 5f249ed to 57b12c3 Compare July 8, 2026 21:58
@ahmedmuhsin

Copy link
Copy Markdown
Owner Author

Superseded by microsoft#7001, which targets main directly now that the sub-workflow / multi-workflow base work has merged (microsoft#6696). Rebased onto main with the same three HITL commits.

@ahmedmuhsin ahmedmuhsin closed this Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.