Hosting packages (hosting, hosting-responses, hosting-telegram) were excluded
from the 1.10.0 release but their entries remained in the CHANGELOG.
Also removes the core hosting channel entry since it's unreachable without
the hosting packages.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: align GitHub Copilot approval to SDK on_pre_tool_use hook
Replace the bespoke on_function_approval enforcement in the GitHub Copilot provider with the Copilot SDK's native on_pre_tool_use hook. When no caller hook is supplied, a default hook returns 'ask' for approval_mode='always_require' tools (routed to on_permission_request) and defers others; a caller-supplied on_pre_tool_use takes precedence and logs a warning for any unenforced approval tool.
Fixes#6746
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix type-checker errors and restore load_dotenv in sample
Use a complete PreToolUseHookInput in on_pre_tool_use hook tests so pyright/pyrefly/ty/zuban no longer report missing required TypedDict keys. Restore load_dotenv() in the function-approval sample for consistency with the other GitHub Copilot samples (PR review feedback).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Deprecate on_function_approval instead of removing it
Per PR review feedback, keep the on_function_approval callback working (still enforced in the tool handler for approval_mode='always_require' tools) but emit a DeprecationWarning at construction, so existing users get a signal rather than a silent behavior change. The default on_pre_tool_use ask-hook is not installed when on_function_approval is set, avoiding double-gating. Precedence: user on_pre_tool_use > on_function_approval > default ask-hook. Adds tests for the deprecated path and documents it in the package README.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Make on_function_approval and on_pre_tool_use mutually exclusive
Per automated review feedback, instead of a precedence ordering between the deprecated on_function_approval callback and the new on_pre_tool_use hook (which silently double-gated when both were set), raise ValueError if both are supplied - at construction (both in default_options) or per run (per-run on_pre_tool_use with a construction-time on_function_approval). This matches the repo convention for deprecated-vs-new params (see _workflows/_workflow.py) and removes the flag-threading. Updates tests and the package README.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Add include_detailed_errors option for skill script execution
Port the .NET fix from #6680. SkillsProvider previously swallowed
exceptions from skill script execution and resource reading, returning a
generic error string so the model could not self-correct.
- Add an include_detailed_errors option to SkillsProvider.__init__ and
from_paths. When True, script-execution failures return an error string
with the exception message appended; when False (default), the exception
is logged and re-raised, delegating to the function-invocation pipeline's
own include_detailed_errors policy.
- _read_skill_resource now logs and re-raises instead of returning a
generic error string. Resources take no model arguments, so a swallowed
generic error is not actionable by the model.
- Update and add tests covering the new propagation and detailed-error
behavior.
Fixes#6681
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Re-raise skill script/resource errors instead of adding a provider option
Address PR review: returning a plain error string from the skill provider
bypassed the shared tool-error contract (no exception metadata, not counted
toward consecutive-error limits), risking infinite retries.
Instead of porting the .NET provider-level IncludeDetailedErrors option,
_run_skill_script and _read_skill_resource now always log and re-raise on
failure. This delegates error handling to the function-invocation pipeline,
whose existing include_detailed_errors policy is the Python equivalent of
.NET's FunctionInvokingChatClient.IncludeDetailedErrors and correctly
preserves exception metadata and consecutive-error counting.
Validation failures (empty/unknown skill, script, or resource names) still
return user-facing error strings. Tests updated accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: [BREAKING] Make all SkillsProvider tools require approval by default
All tools exposed by SkillsProvider (load_skill, read_skill_resource,
run_skill_script) now require approval by default. Previously only
run_skill_script could be gated, and only when require_script_approval=True.
- Register all three tools with approval_mode="always_require"
- Add read_only_tools_auto_approval_rule and all_tools_auto_approval_rule
static rules plus tool-name constants (mirrors FileAccessProvider)
- Remove the require_script_approval option from __init__ and from_paths
- Add skills_auto_approval sample; update script_approval sample/docs
Closes#6728
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: batch skill approval responses and tidy sample
- Collect a response for every approval request and send them in a single
agent.run so the approval loop always makes progress (no infinite loop when
a request lacks a function_call); reject non-function requests instead of
skipping them. Applied to both the skills_auto_approval and script_approval
samples.
- Extract ToolApprovalMiddleware into a local variable in skills_auto_approval
for readability.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: add approval handling to remaining skills samples
The secure-by-default change makes all SkillsProvider tools require approval,
which left the other skills samples emitting approval requests instead of the
documented answers. Add ToolApprovalMiddleware with the all-tools auto-approval
rule (and a session, which the middleware requires) so these samples run
unattended as before:
- code_defined_skill, file_based_skill, class_based_skill, mixed_skills,
skill_filtering, mcp_based_skill
- providers/foundry/foundry_chat_client_with_toolbox_skills
The dedicated script_approval (manual) and skills_auto_approval (selective)
samples continue to demonstrate interactive approval handling.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: simplify "host approval" wording to "approval"
Apply maintainer suggestions dropping "host" from the skill-approval
docstrings, and align the matching SkillsProvider docstring/AGENTS.md note for
consistency.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update agent-framework-azure-ai-search to work across the stable/GA azure-search-documents SDK (12.0.0, api-version 2026-04-01) and the preview SDK (12.1.0b1, api-version 2026-05-01-preview) for both semantic and agentic modes.
- Bump the dependency to azure-search-documents>=12.0.0,<13 and the package to 1.0.0b260618.
- Add an api_version parameter (threaded into SearchClient, SearchIndexClient, and KnowledgeBaseRetrievalClient) plus STABLE_API_VERSION/PREVIEW_API_VERSION constants, re-exported from agent_framework.azure.
- Auto-detect preview-only agentic features (output mode, low/medium reasoning effort) via _preview_features_active(), which requires both the preview SDK and a preview api-version; defaults (extractive + minimal) work on both channels and preview-only options raise an actionable error otherwise.
- Make knowledge-base imports SDK-version resilient and fix the 12.x surface (k -> k_nearest_neighbors, defensive additional_properties).
- Update tests (pass on both SDKs), docs, samples, CHANGELOG, and uv.lock.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add samples for the harness blog part 2
* Address PR comments
* Fix blog links.
* Address PR comments
* Fix bug where mode was incorrectly defaulted when reading the mode before the first run.
* Add reference to new sample readme
Ollama's `format` param only accepts '', 'json', or a JSON-schema dict, so
passing a Pydantic model class (the form OpenAIChatClient/FoundryChatClient and
create_harness_agent plan mode use) raised a ValidationError while building the
request. Convert a model class to its JSON schema when mapping response_format
-> format, keeping the original class for typed response parsing.
* Python: add GitHub MCP security label sample
* modified samples to create devui auth token, support debugging with security, and change context label only using the labels of unhidden result from tools
* FIDES: secure MCP labeling, _meta IFC parsing, and docs updates
* FIDES: secure MCP labeling, _meta IFC parsing, and docs updates
* modified docs
* fixed PR comments, simplified github_mcp example
* commented github_mcp example
* remove the parse_github_mcp_labels and fix the user_identity label propogation
* fix: use standard GitHub MCP endpoint with X-MCP-Features: ifc_labels instead of /insiders
- Switch MCP_URL from /mcp/insiders to /mcp/ in github_mcp_example.py
- Add MCP_HEADERS constant with X-MCP-Features: ifc_labels to opt-in to
server-side IFC label emission in _meta payloads
- Fix SecureMCPToolProxy to pass headers via httpx.AsyncClient so they are
included on session.initialize(), not just on tool calls (was causing 401
to silently surface as anyio cancel-scope CancelledError)
- Update README, FIDES_DEVELOPER_GUIDE, FIDES_IMPLEMENTATION_SUMMARY, and
0024-prompt-injection-defense.md to remove all /insiders references
* address PR comments
* Simplify GitHub MCP security sample to DevUI-only; document SecureAgentConfig quarantine client global behavior
* minor PR comments
* fixing failed checks
* fixing failed checks
---------
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Python: bump package versions for 1.10.0 release
- Released cohort (core, openai, foundry, root): 1.9.0/1.8.2 -> 1.10.0
- agent-framework-ag-ui: rc5 -> rc6 (tool history replay fix)
- Beta/alpha packages with changes: anthropic, azurefunctions, bedrock,
durabletask, hyperlight, purview, foundry-hosting, gemini, hosting,
hosting-responses, hosting-telegram, tools bumped to new date stamp (260625)
- Inter-package dependency bounds updated for changed packages
- CHANGELOG.md updated with [1.10.0] section and compare links
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: update stale hosting dependency pins in hosting-responses and hosting-telegram
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* CI: cap xdist workers at 4 for Azure OpenAI and Functions integration jobs
The Azure OpenAI and Functions+Durable Task integration jobs ran with
`-n logical` (~20 workers on the hosted runner), oversubscribing the box and
collapsing the whole pytest session (all workers reporting `node down: Not
properly terminated`) in the merge queue. Pin these two jobs to `-n 4` in
python-merge-tests.yml and python-integration-tests.yml to remove the
oversubscription while keeping full coverage.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: temporarily skip flaky Python integration tests crashing the merge queue
Revert the `-n 4` xdist experiment (it did not prevent the runner crash) and
instead skip the integration tests that collapse the pytest-xdist runner in the
merge queue (all workers report `node down: Not properly terminated`):
- Azure OpenAI: flip the per-file `skip_if_azure_openai_integration_tests_disabled`
guard to an unconditional skip (integration tests only; unit tests still run).
- Azure Functions / Durable Task: skip the four specific failing tests
(test_weather_agent, test_parallel_workflow_end_to_end, test_weather_agent_with_tool,
test_conditional_branching).
Tracked for re-enablement in #6777.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: skip flaky test_math_agent_with_tool (durabletask integration)
Same empty-AgentResponse flakiness as test_weather_agent_with_tool in the same
file (AssertionError: assert 0 > 0 / empty .text). Skip it in the merge queue.
Tracked in #6777.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Refactor runner/workflow responsibilities, add concurrency guards, and fix checkpoint ancestry bug
Move runner-state ownership out of Workflow into Runner for clearer responsibilities. Add a weakref-based concurrent-run guard in Workflow and fix the stream-drop race in run_until_convergence. Fix the checkpoint ancestry bug by tracking the previous checkpoint id as runner instance state so parent pointers persist across resumed runs. Move Runner to a deprecated lazy __getattr__ export (backward-compatible with DeprecationWarning) and export CheckpointID.
* Scope runtime checkpoint storage to its owning run
Close the stream-drop race where a dropped run's deferred async-generator finalizer could leave a runtime checkpoint storage override set (inherited by a new run) or clear a successor run's storage. run() now defensively clears any stale override before starting, and _run_core only clears the override if this run still owns it (mirroring the _active_run ownership guard). Adds regression tests for both the inheritance and clobber cases.
* Collapse runtime-storage ownership into the active-run weakref
_runtime_storage_owner always held the same weakref as _active_run, so the two ownership conditions were equivalent. Derive ownership from a single owns_run = (_active_run is my_active_run) captured before the active-run clear, and remove the redundant field. No behavior change.
* Nest runtime-storage clear under the owns_run guard
Both the active-run release and the runtime-storage clear are gated on owns_run, so fold the storage clear inside the if owns_run block. No behavior change.
* Reset resume flag in a finally so it can't leak across runs
_resumed_from_checkpoint was only cleared on the success path of run_until_convergence, so a failure during a resumed run (e.g. executor failure) left it True. The next fresh run then skipped the superstep-0 checkpoint and parented later checkpoints to the stale resume point. Move the reset into a finally. Add a regression test that fails a resumed run via an executor error and asserts the next fresh run creates the superstep-0 checkpoint.
* Fix tests and formatting
* Fix formatting
* Address comments
* Update type ignore statements
* Python: Add Telegram channel for agent-framework-hosting
- Add agent-framework-hosting-telegram package with TelegramChannel
supporting polling and webhook transports, streaming edits with
Telegram Bot API rate limiting, per-chat serial workers, and
multi-modal inbound/outbound (text, photo, document, voice)
- Add local_telegram sample demonstrating multi-channel hosting with
a TelegramChannel alongside ResponsesChannel, using per-chat
FileHistoryProvider and a run_hook for Telegram persona temperature
- Fix test layout: move tests to tests/hosting_telegram/ (no __init__.py)
- Remove old [tool.mypy] section and mypy poe task; source type-checking
is handled by pyright via shared_tasks
- Update uv.lock, pyproject.toml workspace sources, and PACKAGE_STATUS.md
Fixes#6588
Refs #6265
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Address Telegram channel CI failures and review feedback
- Fix webhook secret validation to use constant-time compare_digest
- Harden webhook update parsing: require integer chat IDs and guard slash-only commands
- Fix streaming edge cases in TelegramChannel:
- prevent edit worker deadlocks when text exceeds 4096 chars
- prevent deadlock when placeholder send fails (message_id stays None)
- enforce edit throttling with minimum interval sleep
- honor send_typing_action=False in streaming mode
- always forward final multimodal output (e.g. images), while avoiding duplicate text sends
- Expand Telegram tests for slash-only command handling, non-int chat IDs, and streaming behavior (long text, final images, typing toggle)
- Fix sample/docs feedback:
- rename sample package to agent-framework-hosting-sample-local-telegram
- switch sample uv.sources from feature branch to main
- align docs/tool names with lookup_weather
- fix broken links and server run instructions in README/call_server.py
- align local_telegram app docstrings with reasoning hook behavior and strip model in responses_hook
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix TelegramChannel streaming to iterate contents for multimodal support
- Remove stale PR reference from module docstring
- Add Google-style docstring to TelegramChannel.__init__ documenting all keyword args
- Fix _stream_to_chat to iterate update.contents instead of using
getattr(update, 'text', None); text chunks are extracted from Content
items with type='text', non-text content in updates is correctly
ignored (images etc. are forwarded via the final response)
- Update _FakeStreamUpdate test helper to use contents list matching the
real AgentResponseUpdate API; add from_text/from_image class methods
- Update _FakeResponseStream to accept _FakeStreamUpdate objects directly
- Add test verifying multimodal stream updates don't corrupt text accumulator
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Split local_telegram into simple Telegram-only and new multi-channel sample
local_telegram is now a focused Telegram-only sample:
- Removes ResponsesChannel and all responses_hook code
- Removes call_server.py (no HTTP endpoint to call)
- Uses a deterministic lookup_weather tool (hash-based, not random)
- Single run_hook that strips model and raises reasoning effort
- Drops agent-framework-hosting-responses dependency
New local_multi_channel sample shows running both channels at once:
- ResponsesChannel + TelegramChannel sharing a FileHistoryProvider
- Cross-channel session resumption via previous_response_id
- call_server.py moved here (the Responses endpoint lives here now)
- Demonstrates the multi-channel coordination story
Update README table to list both samples with clear descriptions.
Also delete personal_assistant/.venv which was not tracked but caused
pyright to crawl the entire installed venv (thousands of files),
making sample pyright checks hang indefinitely.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fallback when Telegram final edit fails
- only mark final edit as sent after a confirmed 2xx edit response
- fall back to sendMessage when final edit returns a non-success status
- add regression test covering failed final edit fallback behavior
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix optional await_args typing in telegram test
- assert await_args is not None before reading kwargs in streaming fallback test
- resolves test-typing failures across mypy/pyright/ty/zuban for hosting-telegram
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: [Breaking] Refactor FileSkillsSource for depth-based discovery and predicate filters
Refactors FileSkillsSource to make script and resource discovery more flexible.
## Changes
- **Drops** resource_directories / script_directories options (preconfigured
directory whitelists).
- **Adds** search_depth option (>= 1, default 2): controls how deep the
recursive scan goes within each skill directory.
- **Adds** script_filter / resource_filter predicate options that receive a
FileSkillFilterContext (skill_name + relative_file_path), allowing
whitelist/blacklist filtering by file path.
- **Adds** FileSkillFilterContext class exported from agent_framework.
## Notes
- The Skills API is marked @experimental -- the option removals are intentional
breaking changes within the experimental surface.
- Security checks (path containment, symlink detection) are preserved and
continue to use the skill root directory as the trusted boundary.
- Ports the same refactoring from .NET PR #6109 while following Python
conventions (instance methods, Callable type hints, __slots__).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR feedback: clarify depth constants and skip nested skill directories
- Add clarifying comments distinguishing MAX_SEARCH_DEPTH (SKILL.md
discovery) from DEFAULT_SEARCH_DEPTH (per-skill resource/script scanning).
- Stop recursing into subdirectories that contain their own SKILL.md,
preventing child skill files from being attached to the parent skill.
- Add test verifying nested skill boundary is respected.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove __slots__ from FileSkillFilterContext and add type-ignore comments
- Remove __slots__ from FileSkillFilterContext per reviewer feedback —
the optimization is negligible and inconsistent with sibling classes.
- Add type: ignore[attr-defined] / ty: ignore[unresolved-attribute]
comments to test lines accessing private _resources/_scripts attributes,
matching the convention established on main.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Simplify filter predicates: remove FileSkillFilterContext, use Callable[[str, str], bool]
Address reviewer feedback:
- Remove FileSkillFilterContext class — a dedicated class for two strings
is overkill in Python. Filters now receive (skill_name, relative_file_path)
directly as positional args.
- Update docstrings to describe behavior instead of referencing private
instance attributes.
- Remove FileSkillFilterContext from exports and __all__.
- Update all test lambdas and remove TestFileSkillFilterContext class.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use DEFAULT_SEARCH_DEPTH as default argument directly
Instead of accepting int | None and resolving None to the default
internally, use DEFAULT_SEARCH_DEPTH as the parameter default value
on both FileSkillsSource.__init__() and SkillsProvider.from_paths().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: surface Gemini cached and thinking token counts in usage details
* Python: surface Bedrock cache token counts in usage details
* Python: surface Gemini cached and thinking token counts in usage details
* Python: surface Bedrock cache token counts in usage details
* Return None from Bedrock _parse_usage when no token counts are present
Matches the UsageDetails | None return annotation and the Gemini
connector's behavior, so a usage payload with no recognized keys no
longer propagates an empty mapping. Adds a regression test.
Skill content now always emits <available_resources> and <available_scripts>
blocks, using self-closing elements when empty, so models receive an
authoritative list per category and do not hallucinate resource/script names.
FileSkill now also emits its resources block.
Closes#6348
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Purview: prefer token principal for user identity
Align Purview middleware identity resolution so user-token principals are preferred before supplied message identities, while app-token flows continue to use validated fallback user IDs. Also fix the content activities user route and add regression coverage for identity precedence and route construction.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Fix user ID resolution logic in ScopedContentProcessor and add unit test for empty token user ID
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Require approvals for file-access and expose auto approval funcs for it
* Scope file-access auto-approval rules to local tools; fix base-Agent sample
Address PR #6599 review feedback:
- read_only/all_tools auto-approval rules now reject any call carrying a
server_label so they stay scoped to FileAccessProvider's local tools and
never auto-approve a same-named hosted tool.
- Expand the FileAccessProvider docstring to explain the runtime effect of
approval_mode="always_require" and point to ToolApprovalMiddleware /
create_harness_agent.
- Fix the base-Agent file_access_data_processing sample, which would otherwise
stop executing file tools under the new always_require defaults, by adding
ToolApprovalMiddleware with all_tools_auto_approval_rule.
- Add tests covering hosted (server_label) calls and update docs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Clean up comments
* Update sample after merge
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add samples for harness blog post part 1
* Add readme for python samples
* Update python instructions to match dotnet instructions
* Address PR comments
* Add link to blog posts
* Fix blog post naming.
* Add more blog post links
* Python: harden Hyperlight output capture against symlinks
Mirror the input-staging symlink hardening on the output-capture path of
HyperlightExecuteCodeTool. Output discovery now walks via the symlink-safe
_iter_real_entries instead of rglob, per-file collection validates that no
path component is a symlink and the final entry is a regular file, and file
reads use os.O_NOFOLLOW. Adds regression tests for the output path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review: reject traversal, fix listing test, harden read
- _is_safe_output_file now rejects '.'/'..' components (lexical relative_to
could otherwise escape root without a symlink)
- _read_output_file_bytes adds a cross-platform TOCTOU guard (lstat/fstat
st_dev+st_ino identity check) since O_NOFOLLOW is absent on Windows
- fix intermediate-dir-symlink test to use a relative listing path so it
exercises normalization + validation; add a parent-traversal unit test
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(durabletask): host MAF workflows on a standalone Durable Task worker
Add a host-agnostic workflow execution engine to agent-framework-durabletask so a MAF Workflow can run as a durable orchestration outside Azure Functions:
- WorkflowOrchestrationContext protocol + DurableTaskWorkflowContext adapter, the superstep orchestrator, serialization helpers, capturing runner context, and the shared non-agent activity body (including the yield-output classifier so intermediate executors are not surfaced as final outputs).
- DurableAIAgentWorker.configure_workflow auto-registers agent executors as entities, non-agent executors as activities, and the workflow orchestrator.
- plan_workflow_registration centralizes the 'what to register' decision so it can be shared across hosts.
- run_agent_coroutine runs all agent coroutines on one persistent event loop, fixing a cross-loop hang when shared chat clients/credentials bind their asyncio primitives to a dead loop.
- DurableWorkflowClient (start/await workflow + HITL discover/respond); DurableAIAgentClient stays agent-only.
* refactor(azurefunctions): delegate workflow execution to agent-framework-durabletask
AgentFunctionApp now reuses the shared orchestrator, activity body, and registration planner from agent_framework_durabletask instead of maintaining its own copies; _workflow.py becomes a thin host-specific adapter (AzureFunctionsWorkflowContext).
- Run agent entity coroutines on the shared persistent event loop, fixing the cross-loop hang.
- Relocate state-diff unit tests to the durabletask package; update entity loop tests.
* feat(core): expose durabletask workflow symbols via agent_framework.azure
Lazily re-export WORKFLOW_ORCHESTRATOR_NAME and DurableWorkflowClient from the agent_framework.azure namespace so standalone hosts can import them without depending on internal module paths.
* docs(samples): add standalone durabletask workflow and HITL samples
Add two samples under samples/04-hosting/durabletask demonstrating MAF workflows on a standalone Durable Task worker (no Azure Functions):
- 08_workflow: conditional spam-detection workflow started via DurableWorkflowClient.start_workflow / await_workflow_output.
- 09_workflow_hitl: content-moderation workflow that pauses with ctx.request_info and is resumed via DurableWorkflowClient.get_pending_hitl_requests / send_hitl_response.
Also add the durabletask workflow integration test (test_08_dt_workflow).
* fix: address PR review feedback
- Sanitize HITL external-event responses with strip_pickle_markers in the orchestrator (defense-in-depth for callers that bypass DurableWorkflowClient).
- Raise WorkflowConvergenceException when max_iterations is reached with pending messages, matching the core WorkflowRunner instead of silently returning partial output.
- Route falsy 'sent' messages (use 'is not None' instead of truthiness).
- Normalize None shared_state_snapshot/source_executor_ids in execute_workflow_activity.
- Cast Any returns in AzureFunctionsWorkflowContext to satisfy mypy/pyright.
- Fix sample docstrings to reference DurableWorkflowClient.
* fix: resolve pyright Package Checks errors
- Use typed locals instead of cast in AzureFunctionsWorkflowContext (mypy sees Any, pyright sees concrete types -> avoid reportUnnecessaryCast).
- Annotate shared_state_snapshot and cast partially-typed durabletask SDK returns / HITL custom-status parsing to satisfy reportUnknownVariableType/reportUnknownMemberType.
- Drop the dead deserialize/serialize re-export in _workflow.py and mark the intentional private _extract_message_content re-export.
* fix(durabletask): agent-executor identity and typed workflow input
Register each workflow agent entity under the executor id that the orchestrator dispatches to (instead of the agent name), so AgentExecutor(agent, id=...) works when the id differs from agent.name. The azure-functions host mirrors this.
Reconstruct the start executor declared input type from the workflow initial JSON payload in the shared engine (mirroring in-process delivery) instead of string-coercing it per host. Untrusted input is stripped of pickle markers before reconstruction to prevent deserialization RCE.
* fix(samples): type durable workflow start executors for reconstructed input
The HITL and parallel workflow samples no longer hand-parse a JSON string. Their start executors now declare their real input type (ContentSubmission / DocumentInput), which the durable engine reconstructs from the client payload before delivery.
* test(durabletask): unit coverage for registration, client, worker, and input coercion
Add unit tests for plan_workflow_registration, DurableWorkflowClient, the agent-executor identity registration (entity keyed by executor id), and the typed initial-input coercion including pickle-marker neutralization.
* test(durabletask): HITL and parallel durable workflow integration tests
Add an integration test for the standalone durabletask HITL workflow sample via a new workflow_client fixture. Re-enable the Azure Functions parallel workflow test, consolidated into one end-to-end case so the work-stealing xdist scheduler cannot spawn multiple func hosts for this sample.
* refactor(durabletask): group workflow modules into a _workflows subpackage
Move the eight workflow modules into a private _workflows/ subpackage and drop the redundant _workflow_ prefix (orchestrator.py, registration.py, activity.py, client.py, context.py, dt_context.py, runner_context.py, serialization.py). The public API and __all__ are unchanged; only direct internal-module imports were repointed (package __init__, the worker, the azure-functions shared shim, and the affected unit tests).
* fix(durabletask): harden workflow type resolution and HITL response handling
- resolve_type returns only real classes (avoids issubclass TypeError in reconstruct_to_type)
- re-wait on HITL responses rejected by pickle-marker sanitization instead of dropping the request and losing the run
- American spelling in strip_pickle_markers docstring
- unit tests for resolve_type
* fix(durabletask): treat async edge conditions as not-matched on the synchronous host
The durabletask orchestrator evaluates edge conditions synchronously and does not support async edge conditions. Such an edge is now treated as not matched (the edge is not traversed) rather than assuming a result. Adds unit coverage; full async-condition support will be handled separately.
* fix(durabletask): reconstruct typed workflow outputs at the host boundary
await_workflow_output and the Azure Functions status endpoint now decode the checkpoint-encoded outputs the shared activity produces, via a shared deserialize_workflow_output helper. The client returns the original objects; the AF endpoint emits clean domain JSON instead of checkpoint-marker dicts, keeping the two hosts consistent.
* fix(durabletask): address review findings on workflow hosting
- AF: register workflow agents through add_agent(entity_id=...) so they remain tracked in app.agents / get_agent() (restores documented behavior) while keying by the executor id the orchestrator dispatches to; mirrors DurableAIAgentWorker.add_agent.
- async bridge: treat the shared loop as reusable only while its backing thread is alive, so a dead loop thread is replaced instead of hanging future.result() forever.
- client: add get_runtime_status; the standalone HITL sample now stops polling and reports the real terminal state instead of a generic timeout.
- tests: guard send_hitl_response pickle-marker stripping and add get_runtime_status coverage.
* fix(durabletask): wait indefinitely for HITL responses, matching core
The durable workflow host previously raced HITL responses against a 72h timer and failed the orchestration on elapse. MAF core's request_info has no timeout concept (it waits for the response), and the .NET durable host waits too, so the durable Python host now does the same: it stays paused until a response arrives. Removes the hitl_timeout_hours parameter and DEFAULT_HITL_TIMEOUT_HOURS constant from both hosts. A configurable timeout can be added later once core defines the contract (what happens on elapse).
* feat(durabletask): typed workflow event streaming and async client API
Add a brokerless workflow event stream to the durable host. Each non-agent executor runs inside a durable activity that captures its real WorkflowEvents (with data payloads); the orchestrator replays them into the orchestration custom status after each superstep, and the client streams them back as typed WorkflowEvent objects with reconstructed data. Agent executors contribute synthesized invoked/completed lifecycle events.
Add async client methods run_workflow (start with optional wait) and stream_workflow (typed event iterator), plus is_replaying plumbing through the orchestration context protocol and both host adapters so live status is published only on non-replay execution.
* docs(samples): standalone durabletask workflow streaming sample
Add sample 10_workflow_streaming demonstrating the async DurableWorkflowClient API on a standalone Durable Task worker: run_workflow(wait=False) to start without blocking, then stream_workflow to consume typed WorkflowEvent objects as a WriterAgent -> ReviewerAgent -> publish pipeline runs.
* refactor(durabletask): internal-only checkpoint codec and host-scoped workflow event streaming
Two related hardening changes to the durable workflow hosting layer, plus a
rebase-restored improvement.
Internal-only serialization codec (MSRC follow-up):
- Rename serialize_value/deserialize_value -> _serialize_value/_deserialize_value
in the shared durabletask serialization module and update all call sites, so the
pickle-backed checkpoint codec is unambiguously framework-internal. Untrusted
input is still neutralized with strip_pickle_markers at the HTTP boundary.
- Remove the duplicate agent_framework_azurefunctions._serialization module and
import strip_pickle_markers from the shared durabletask module instead. Move its
unique serialization/strip-marker tests into the durabletask test suite.
Scope workflow event streaming to hosts that can carry it:
- Add WorkflowOrchestrationContext.supports_event_streaming. The standalone
DurableTask host returns True (no custom-status size cap, has a stream_workflow
consumer); the Azure Functions host returns False.
- The orchestrator now accumulates and publishes the WorkflowEvent timeline to the
orchestration custom status only when the host supports streaming. On Azure
Functions the custom status returns to its pre-streaming shape
({state[, pending_requests]}), which fixes orchestrator failures with
"The size of the JSON-serialized payload must not exceed 16 KB" and stops leaking
pickle markers into the HTTP status response. The Azure Functions status endpoint
never consumed the event stream.
Workflow start endpoint:
- Accept text/plain raw request bodies (fall back from get_json to the raw body),
restoring an improvement from main that the rebase conflict resolution dropped.
* fix(azurefunctions): scope workflow status/respond endpoints to the workflow orchestrator
The workflow/status/{instanceId} and workflow/respond/{instanceId}/{requestId}
HTTP endpoints resolved durable instances by ID only. The durable client looks up
IDs across every orchestration in the task hub (agent entities, any
user-registered orchestrations, and other apps sharing the hub), so a caller
holding one instance ID could read another orchestration's status -- including
pending HITL request payloads -- or inject external events into it.
Add AgentFunctionApp._is_workflow_orchestration() and gate both endpoints on it:
an instance whose orchestration name is not WORKFLOW_ORCHESTRATOR_NAME now returns
404 instead of leaking state or accepting events. send_hitl_response now fetches
the orchestration status and validates ownership before raising the external
event. Legitimate workflow instances are unaffected.
Mirrors the .NET fix in PR #6608.
* fix(durabletask): resolve CI typing failures
- serialization: rename _serialize_value/_deserialize_value back to
serialize_value/deserialize_value to follow the package convention for
cross-module internal helpers (matches strip_pickle_markers, resolve_type).
The leading underscore tripped pyright reportPrivateUsage on cross-module
imports under the strict source gate; internal-only status is preserved by
not exporting them from the public API.
- Remove type-ignore comments pyright flags as unnecessary
(reportUnnecessaryTypeIgnoreComment) in _worker.py, orchestrator.py,
serialization.py.
- test_08_dt_workflow: add AgentClientFactoryProtocol and annotate the
agent_client_factory fixture as type[AgentClientFactoryProtocol] (matching
test_01-07) so mypy/ty stop reporting "type has no attribute create".
- samples (08_workflow, 09_workflow_hitl): pass structured output via
FoundryChatOptions[Any](response_format=...) instead of a plain dict so the
samples pyright (basic) config accepts default_options.
---------
Co-authored-by: Gavin Aguiar <80794152+gavin-aguiar@users.noreply.github.com>