* feat(python): add Mistral chat client
Implements native Mistral support (#7366) with streaming, tool calling,
and structured output. Talks to the REST API directly over httpx: the
mistralai SDK's pinned OpenTelemetry deps conflict with the workspace.
* refactor(python): simplify Mistral client per review
Drop the streamed tool-call accumulator and multi-choice parsing in
favor of the framework's built-in fragment merging, mark n unsupported,
omit unset strict from json_schema, and leave CI secret wiring to
maintainers.
* test(python): drop n forwarding assertion
n is typed as unsupported on MistralChatOptions; the option-mapping test
still passed n, failing pyrefly/ty/zuban/mypy in CI.
* refactor(python): drop n from MistralChatOptions
n is not part of the base ChatOptions, so removing the key rejects it
without an explicit None override.
* feat(python): mark Mistral feature usage
Both clients flip the shared FeatureIndex.MISTRAL bit before each
request, matching the feature-usage telemetry other providers emit.
* fix(python): key streamed tool calls by index
Mistral omits the tool call id on continuation fragments, and the
framework only coalesces empty-id fragments into the immediately
preceding call, so interleaved parallel calls merged into the wrong
call with corrupted arguments. Accumulate fragments per (choice,
index) and emit each call only once complete.
* fix(python): restore Mistral SDK client injection
Dropping the mistralai dependency turned the embedding client's
client= parameter into a breaking change for injected SDK clients.
Add http_client= for httpx.AsyncClient and keep client= working:
httpx goes to the REST path, a duck-typed mistralai.Mistral goes
through the legacy SDK path with a DeprecationWarning until the
next major release.
* chore(python): tidy Mistral sample header
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
* Bump Python package versions for 1.13.0 release
Bump all 37 Python package projects because the CHANGELOG-driven release includes cross-package feature-usage telemetry, with core and root advancing to 1.13.0, OpenAI to 1.12.0, patch bumps for other stable packages, and 260730 stamps for alpha and beta packages. No optional beta cohort bump was applied; every prerelease package changed. Raise core floors conservatively across co-released packages.
Copilot-Session: e234a28b-c2fd-4ff4-a51d-3d8917936541
* Align co-released Python package dependencies
Update the four hosting adapter pins to the co-released agent-framework-hosting alpha and raise the Azure Functions Durable Task floor to the co-released beta.
Copilot-Session: e234a28b-c2fd-4ff4-a51d-3d8917936541
* Minimize Python release lockfile updates
Regenerate uv.lock with the pre-commit hook pinned uv version so the release changes only workspace package versions while preserving platform markers and agentlightning 0.3.0.
Copilot-Session: e234a28b-c2fd-4ff4-a51d-3d8917936541
---------
Copilot-Session: e234a28b-c2fd-4ff4-a51d-3d8917936541
* Python: Support archive-type MCP skills in MCPSkillsSource
Add `archive`-type skill support to `MCPSkillsSource` so an MCP server can
advertise packaged skills (ZIP / TAR / gzip-compressed TAR) that are
downloaded, safely unpacked to a local directory, and served like file-based
skills, while keeping the guarantee that MCP-delivered scripts are never
executed.
- Dispatch `skill://index.json` entries by `type`: `skill-md` (existing,
fetched on demand) and `archive` (new). Unknown types are skipped.
- `_ArchiveEntryLoader` downloads, extracts, and prunes archive skills and
delegates discovery to an internal `FileSkillsSource` created with no
script extensions and no runner, so bundled scripts surface as read-only
resources only.
- Hardened stdlib extraction: path-traversal (zip-slip) guard, non-regular
TAR member skipping, and file-count / uncompressed-size / download-size
limits.
- Configure via `archive_*` constructor kwargs (no options object, per Python
conventions); use `CachingSkillsSource` for refresh rather than a source
level refresh interval.
- Fix `FileSkillsSource` to treat `None` extensions as "use defaults" and an
empty tuple as "discover none" (an empty tuple previously fell back to
defaults).
Port of .NET PR #6631.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d
* Propagate non-not-found archive download errors in MCPSkillsSource
Only swallow "resource not found" MCP errors when downloading an archive
resource; re-raise every other error (auth failure, INTERNAL_ERROR,
connection drop, timeout) so a transient transport failure is not silently
turned into a missing skill. This matches the existing failure model used by
`_try_read_index` and `MCPSkill.get_resource`, and avoids a failed
`CachingSkillsSource` refresh overwriting a previously cached list with a
partial result.
Add tests asserting archive-download INTERNAL_ERROR and ConnectionError
propagate out of `get_skills`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d
* Python: Expose archive skill options on FoundryToolbox and demo in sample
- FoundryToolbox.as_skills_provider() now forwards the MCPSkillsSource archive
options (archive_skills_directory, archive_resource_extensions,
archive_resource_search_depth, archive_max_file_count, archive_max_size_bytes,
archive_max_uncompressed_size_bytes). Only explicitly-set options are
forwarded so unset ones keep the MCPSkillsSource defaults. This lets a hosted
toolbox agent redirect archive extraction to a writable directory (the default
is under the cwd, which may be read-only in a container).
- Add unit tests covering default (no options forwarded) and override forwarding.
- Update the 12_foundry_toolbox_mcp_skills sample to demonstrate all three
progressive-disclosure stages with an archive skill: escalation-policy now
ships a references/refund-matrix.md resource and is uploaded as a ZIP archive;
main.py disables load_skill and read_skill_resource approval and points
archive extraction at a temp directory. README, toolbox.yaml, and ignore files
updated accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4f14f83d-1868-45c1-be1a-12f49a58ac36
* Python: Fix ty type error in toolbox archive-option test
Cast provider._source to _FoundryToolboxSkillsSource before accessing the
private _archive_options, so the ty checker (which runs over tests) resolves
the concrete type instead of the SkillsSource base. Replaces the mypy-style
type: ignore that ty did not honor.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4f14f83d-1868-45c1-be1a-12f49a58ac36
* Rework archive-type skill support in MCPSkillsSource to unpack archives
entirely in memory instead of extracting them to a local directory, and
apply reviewer feedback.
* Python: Raise on archive member path-traversal (zip-slip)
Treat a `..` path-traversal member in an archive skill as a hostile archive
and reject the whole skill, matching how the file-count and uncompressed-size
limits reject a malformed archive (previously the member was silently skipped
while the rest of the skill still loaded).
- `_normalize_archive_member_name` now raises `ValueError` on a `..` escape;
benign degenerate entries (empty, `.`, `/`) still return None (skipped) and
absolute paths are still neutralized to relative. The raise propagates to
`_ArchiveEntryLoader._build_skill`, which already skips the skill on error.
- Update tests: traversal cases now assert a raise, and add an end-to-end test
that a zip-slip archive drops the whole skill.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d
* Python: Revert archive skill demo in toolbox MCP skills sample
Restore the 12_foundry_toolbox_mcp_skills sample to its pre-PR, skill-md-only
form (matching the .NET Agent_Step26_FoundryToolboxMcpSkills sample, which uses
skill-md and no ZIP archive):
- Revert main.py, toolbox.yaml, README.md, .azdignore, .dockerignore, and
escalation-policy/SKILL.md to the single-file SKILL.md version.
- Remove the archive demo files added by this PR (.gitignore and
escalation-policy/references/refund-matrix.md).
- Soften two README notes so they no longer claim archive skills are
unsupported/silently dropped (this PR adds archive support); instead frame
single-file SKILL.md as a focus choice and point to the archive_* options.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d
* Python: Clarify archive framing in mcp_based_skill sample README
The mcp_based_skill sample is a generic MCP consumer that discovers whatever
the server advertises; it does not itself demonstrate archive skills. Reword
the archive note so it reads as an MCPSkillsSource capability rather than a
sample feature, and fix the stale "unpacked to a local directory" claim to
"unpacked in memory" (matching the in-memory extraction implementation).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e358a4e-538f-46be-8c58-128b6182352d
Copilot-Session: 4f14f83d-1868-45c1-be1a-12f49a58ac36
* Python: Apply header_provider headers to ambient MCP requests
MCPStreamableHTTPTool.header_provider was only invoked from call_tool(),
so the initialize handshake, load_tools/load_prompts discovery, and
background pings all went out with no headers. MCP servers that require
auth on initialize (e.g. Azure AI Search knowledge-base MCP endpoints)
therefore returned 401 before any tool call could run.
Add an ambient fallback in the _inject_headers httpx request hook: when
neither the per-call ContextVar nor the active-call snapshot is set, the
hook invokes header_provider({}) so every ambient request is
authenticated. Providers that require per-call kwargs raise on the empty
dict; that is caught, logged, and the request proceeds unauthenticated,
preserving prior behavior. Calling the provider on demand also keeps
dynamic token refresh working for post-connect requests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cf0c1dbf-99bc-4f3f-bcf4-7791ce7dbe6a
* Python: address review - distinguish unset vs empty headers, warn once
Review feedback on the ambient header_provider fallback:
- Distinguish 'unset' (no active call) from 'set but empty' (call_tool
produced no headers). Use _mcp_call_headers.get(None) and the None-ness
of the snapshot instead of a truthiness check, so a provider that
legitimately returns {} during a real call is no longer re-invoked by
the ambient fallback mid-call.
- A kwargs-dependent provider raises on every ambient request (initialize,
discovery, recurring pings). Warn once per tool instance with a
traceback via _ambient_header_warning_emitted and drop subsequent
occurrences to DEBUG to avoid log spam.
Add regression tests for both behaviors.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cf0c1dbf-99bc-4f3f-bcf4-7791ce7dbe6a
* Python: narrow ambient header_provider catch to KeyError
Only the missing-per-call-kwargs case (KeyError, e.g. the
mcp_api_key_auth.py sample indexing kwargs['mcp_api_key']) is tolerated
during ambient requests. Any other exception - a token-refresh failure
or a provider bug - now propagates instead of being silently converted
into unauthenticated traffic, matching the call_tool path which does not
catch header_provider exceptions.
Add a regression test asserting a non-KeyError provider failure
surfaces from the request hook.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cf0c1dbf-99bc-4f3f-bcf4-7791ce7dbe6a
* Python: address review - raise instead of assert, simplify ambient logging
- Reword the ambient-fallback comment to describe the kwargs-dependent
provider pattern generically instead of naming a sample file, which
would go stale if the sample is renamed (also in a test docstring).
- Replace the type-narrowing assert with a RuntimeError carrying a
concise message for the unreachable no-provider state.
- Drop the warn-once/_ambient_header_warning_emitted machinery; the
KeyError ambient case is expected and benign, so log a single DEBUG
line and proceed without headers.
Update the corresponding test to assert behavior (request proceeds
without an Authorization header and no WARNING is emitted) instead of
log-count.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cf0c1dbf-99bc-4f3f-bcf4-7791ce7dbe6a
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
Copilot-Session: cf0c1dbf-99bc-4f3f-bcf4-7791ce7dbe6a
from_dict resolved the expected type identifier from the payload itself
(_get_type_identifier(value) prefers value["type"]), so the mismatch
guard could never fire: any supplied 'type' matched itself, and a payload
like {"type": "function_tool", ...} silently deserialized into a Message,
getting its type rewritten on the next to_dict. The docstring has always
promised a ValueError on mismatch.
Resolve the identifier from the class instead, matching what to_dict
emits, so a mismatched or foreign 'type' now raises as documented.
Payloads without a 'type' field and dependency-injection lookups are
unchanged: in every previously valid case the class-resolved identifier
is the same string the payload carried.
Fixes#7255
Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Preserve model emission order in AG-UI messages snapshot
* Address moonbox3's review: cover the remaining snapshot gaps
- Preopened message ids (tool-only path) now open a text segment when
the first text arrives, so their content can't drop out of the snapshot.
- A tool result closes the current tool-call segment, so
call A -> result A -> call B snapshots as two pairs in stream order.
- emitted_call_ids only marks calls actually emitted, keeping stale
segment ids eligible for the leftover fallback.
- The leftover path carries its tool results too instead of dropping them.
* Python: narrow leftover tool-call ids so pyright accepts the update
---------
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
* Python: Add GitHub Copilot BYOK sample
Demonstrates routing GitHubCopilotAgent requests through a custom OpenAI-compatible
endpoint via ProviderConfig instead of the default GitHub Copilot backend.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Python: Address BYOK sample review feedback
- Make the provider type configurable via BYOK_PROVIDER_TYPE (default "openai") instead
of hardcoding "openai" — a partial autofix commit had already updated the docstring to
document this env var but left the code hardcoded, which this finishes.
- Stop calling the endpoint "OpenAI-compatible" everywhere; Anthropic isn't OpenAI-wire-
compatible, so reword to "your own endpoint" and list the actual supported providers
(mirrors the equivalent .NET sample fix).
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Python: Fix FoundryAgent inheriting OPENAI_CHAT_MODEL for agent-reference requests (#7272)
* Python: Fix FoundryAgent inheriting OPENAI_CHAT_MODEL for agent-reference requests
* fix(foundry): update test typing annotations to pass mypy, pyrefly, and ty
---------
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Bound summarization input before provider call
SummarizationStrategy now selects complete message groups that fit a configurable summary input token budget before calling the summary client. Only messages actually sent to the summarizer are annotated and excluded, leaving oversized later groups for a later compaction pass instead of shipping the whole transcript unbounded.
Validation: uv run pytest packages/core/tests/core/test_compaction.py -k bounds_summary_input -m "not integration" failed before the implementation and passed after it; uv run pytest packages/core/tests/core/test_compaction.py -m "not integration" passed; uv run poe test -P core passed; uv run poe install completed; uv run poe check -P core passed.
* Handle oversized leading summary groups
Skip individually over-budget leading groups when selecting summarization input so a large early transcript item does not prevent later compactable groups from being summarized.
Validation: uv run pytest packages/core/tests/core/test_compaction.py -k skips_oversized_first_group -q; uv run pytest packages/core/tests/core/test_compaction.py -q; uv run poe check -P core.
* Escalate repeated summary failures
Track consecutive SummarizationStrategy failures and emit a single error once the strategy has failed three times without a successful summary. Reset the escalation state after a successful summary so only persistent failures become loud.
Validation: uv run pytest packages/core/tests/core/test_compaction.py -k 'repeated_summary_failures or resets_failure_escalation' -q; uv run pytest packages/core/tests/core/test_compaction.py -q; uv run poe check -P core.
* Refine summary input selection
Avoid rebuilding and re-tokenizing the full selected summary transcript on every candidate group while preserving complete-group selection and oversized leading group skipping.
Tighten the scripted summarizer test helper to expected Exception failures instead of BaseException.
Verification: uv run pytest packages/core/tests/core/test_compaction.py -q; uv run poe syntax -P core.
---------
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Fix Copilot Actions token environment
Expose workflow tokens through GITHUB_TOKEN so Copilot CLI uses native Actions authentication, while preserving user-token integration test support.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d9fa4e9c-c32d-42fb-8ee4-4772473e6479
* Gate Copilot integration tests explicitly
Use GitHub Actions authentication only when both GITHUB_ACTIONS and GITHUB_TOKEN are present, and require an explicit local opt-in that relies on stored Copilot login.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d9fa4e9c-c32d-42fb-8ee4-4772473e6479
---------
Copilot-Session: d9fa4e9c-c32d-42fb-8ee4-4772473e6479
OpenAI validates the Chat Completions message 'name' against
^[^\s<|\/>]+$, so an agent display name containing a space (or
< | \ / >) failed every request with a 400. Sanitize at the three
assignment sites, mirroring SanitizeAuthorName in the .NET client
(dotnet/extensions): remove characters outside [a-zA-Z0-9_], omit the
name when nothing remains, truncate to 64 characters.
Fixes#7126
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Python: fix Anthropic streaming double-counting token usage
* Python: address review on the Anthropic usage increment helper
- accumulate the emitted totals in a plain dict instead of string-cast
TypedDict views, so static checkers see real types throughout
- compute the increment through _types.add_usage_details with negated
emitted totals instead of a hand-rolled subtraction loop; keys absent
from a snapshot stay untouched, matching the partial-delta semantics
* feat(observability): add support for OpenAI cache write tokens in usage details
* feat(openai): add cache write tokens handling in usage details
* Fix test
* Python: Defer provider-injected approvals to in-run execution
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1
* Python: Remove vacuous AG-UI approval test
Drop the forged-approval test that was stripped by pending-approval validation; the real pause-approve-resume regression remains the authoritative provider-injected coverage.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1
* fix(foundry-hosting): root hosted checkpoints under durable home directory
* fix: add None guard for _checkpoint_storage_path in test
* Disable Foundry image test
---------
Co-authored-by: Tao Chen <taochen@microsoft.com>
* Allow workflow checkpoint full replayability
Seed the initial run input through the start executor's internal self-edge and record an entry checkpoint (iteration 0) before any executor runs, plus a response-entry checkpoint when responses are delivered, so a run is fully replayable from its checkpoints. Simplify the runner to only checkpoint after each superstep. Drop stale events in apply_checkpoint on restore, and deprecate the unused RunnerContext.reset_for_new_run.
* Fix type
* Add max iteration detailed doc string
* Refine comments
* Python: Fix OpenAIChatCompletionClient passing raw JSON-Schema dict response_format through unwrapped
Raw schema dicts (e.g. {"type": "object", ...}) were forwarded to the
Chat Completions API verbatim, which OpenAI rejects with a 400. The
Responses client already auto-wraps the same input. Mirror its raw-schema
detection (primitive types / schema keywords), wrap into the
{"type": "json_schema", "json_schema": {...}} envelope with
additionalProperties: false injection and title -> name promotion, and
leave already-valid response_format dicts untouched.
Fixes#7197
(cherry picked from commit dce5c3b06328fbde45eb2a9a25638af5b1ec85e3)
* Python: Add live integration coverage for raw JSON-Schema response_format dicts
Adds a response_format_raw_json_schema param to test_integration_options in
both the Chat Completions and Responses client test suites, proving the same
bare schema dict (title set, additionalProperties omitted) round-trips through
both live APIs and yields parsed structured output.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Python: Fix response format dict typing
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
_WORD_PATTERN matched only ASCII (`[a-z0-9]...`), so a message written in
CJK, Cyrillic or any other non-Latin script produced an empty keyword set.
_select_topics returns early on an empty keyword set, so non-English users
never had memory topic files loaded automatically.
Make the pattern Unicode-aware (`[^\W_][\w-]+`, a letter/digit start plus
word chars/hyphen), which is the exact Unicode generalization of the old
pattern: English tokenization is unchanged and CJK/Cyrillic text now yields
keywords.
* Fix Gemini harness tool declarations
Forward Agent Framework FunctionTool JSON Schemas to the Gemini SDK parameters_json_schema field and enable Developer API server-side tool invocation reporting when native Gemini tools are mixed with function declarations.
Preserves Vertex AI behavior and existing function-calling tool_choice config.
Validation:
- uv run --directory python poe check -P gemini
- uv run --directory python poe build -P gemini
- uv run --directory python poe test -A -m 'not integration'
- uv run --directory python pytest packages/gemini/tests/test_gemini_client.py -q -m integration (8 skipped: credential-gated)
* Python: Use typing_extensions TypedDict in Gemini tests
Use typing_extensions.TypedDict for the Gemini JSON Schema test helper so Pydantic can build the model on Python 3.11.
This keeps the CI fix scoped to the failing test compatibility issue without changing Gemini client behavior.
---------
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Python: Add TodoProvider and AgentModeProvider context provider samples
Add two Python samples under samples/02-agents/context_providers/ mirroring the
.NET samples from #7262:
- todo_provider.py: scripted walkthrough of TodoProvider that plans multi-step
work and prints the evolving todo list after each turn.
- agent_mode_provider.py: interactive loop using AgentModeProvider with a /mode
slash command, demonstrating built-in plan/execute and custom modes.
Also index both samples in the context_providers README.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8725831c-086b-475f-90e6-cdba41d59c33
* Python: Address review comments on AgentModeProvider sample
- Replace the AGENT_MODE_USE_CUSTOM env var with an in-file USE_CUSTOM_MODES
constant for choosing between built-in and custom modes.
- Use plain input() in the interactive loop instead of asyncio.to_thread.
- Update the README prerequisites to reference the in-file toggle.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8725831c-086b-475f-90e6-cdba41d59c33
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8725831c-086b-475f-90e6-cdba41d59c33
* fix(python): handle callable class middleware safely in _determine_middleware_type (#6697)
* test(python): type-annotate test middleware lists to pass test-typing checks
* Fix sub-workflow checkpoint restore to preserve sub-workflow state
Add Runner.capture_checkpoint_object/restore_from_checkpoint_object (quiescent-only nested checkpoint) and embed a sub_workflow_checkpoint in WorkflowExecutor.on_checkpoint_save/on_checkpoint_restore so a resumed parent restores each sub-workflow's mid-progress state instead of only replaying pending request-info events. Keeps a backward-compat fallback when sub_workflow_checkpoint is absent.
* Move checkpoint-object construction into the runner context
Add RunnerContext.create_checkpoint_object alongside create_checkpoint (create_checkpoint now delegates to it and persists), so Runner.capture_checkpoint_object builds the snapshot via the context instead of a one-off get_messages peek primitive. In-flight messages are captured non-destructively (per-source lists copied). The checkpoint-less capturing contexts (azurefunctions, durabletask) raise NotImplementedError to match create_checkpoint.
* Remove per-execution bookkeeping from WorkflowExecutor
The sub-workflow is a single shared instance, so per-execution ExecutionContext/request routing never provided real isolation. Delegate request/response tracking to the sub-workflow itself: can_handle accepts targeted propagated responses, _handle_response validates against the sub-workflow's pending requests and forwards responses immediately, and on_checkpoint_save embeds only the sub-workflow checkpoint (on_checkpoint_restore keeps a legacy reader for older checkpoints). Also emit the fresh-message/checkpoint-while-pending warning from FunctionalWorkflow.run to match Workflow.run.
* Drop redundant decode in WorkflowExecutor.on_checkpoint_restore
The storage backend already materializes the full checkpoint on load (FileCheckpointStorage decodes recursively; InMemoryCheckpointStorage deep-copies), so the embedded sub_workflow_checkpoint (and legacy execution_contexts) arrive already decoded - like every other executor's on_checkpoint_restore state. Remove the no-op decode_checkpoint_value calls and the now-unused import.
* Clean up
* Do not allow checkpoint storage in sub workflow
* Address comments
* Fix syntax check
* Add warning
---------
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>