demo(agentserver): TEMPORARY - durable-agent-demo (split out of #46997, never-merged)#47276
Draft
RaviPidaparthi wants to merge 346 commits into
Draft
demo(agentserver): TEMPORARY - durable-agent-demo (split out of #46997, never-merged)#47276RaviPidaparthi wants to merge 346 commits into
RaviPidaparthi wants to merge 346 commits into
Conversation
756e0fe to
2553746
Compare
Phase 5 — pre-commit gate output. Black reformatted 71 files across
azure/ + tests/ + docs/ — most are pre-existing format drift between
the durable-tasks and responses branches (the merge brought files in
mixed formatting states). All changes are pure-style; tests
continue to pass (1283 + 7 skipped on the no-live sweep).
Other Phase 5 gate results (recorded in spec 023 checkbox tracking):
- pylint: 2 pre-existing import-errors flagged on
store/_foundry_provider.py + _foundry_errors.py importing
azure.ai.agentserver.core._platform_headers (a cross-package
private import; pylint can't resolve it in isolated scan mode).
Rating IMPROVED from 9.53 to 9.93.
- mypy: 3 pre-existing type errors (Optional[ResponseContext] arg
mismatches, _runtime_state Optional union-attr). All pre-spec-023.
- sphinx: no package-level conf.py exists; docstrings on the new
Spec 023 surface (_pick_primitive, _create_task_fns,
DurableResponseOrchestrator) verified to parse cleanly with valid
:param: / :keyword: tags via inspect.getdoc()+regex smoke test.
- pytest: 1283 passed / 7 skipped / 5 deselected (live).
- SOT drift re-verification: all 4 checks pass (no ctx.suspend(
in impl, no ctx.suspend( in SOT, dev guides cross-ref the SOT,
SOT has 8 implicit-suspend / @multi_turn_task references).
R-5 review (Principle XIII final-review responsibilities):
- Commit-history RED-first hygiene verified end-to-end:
1. merge (e37a1c5)
2. RED conformance tests (83deeb7)
3. implementation (8d6512f) <- turns RED tests GREEN
4. test cleanup (242e86d)
5. docs sync (aa0dbda)
6. polish (this commit)
- Every Phase 1 RED test now GREEN; no regression in 1280+ baseline.
- §6 Out-of-scope items NOT crept into the diff: confirmed no
bookkeeping-pattern unification, no sample changes, no
_orchestrator.py refactor beyond the necessary TaskConflictError
propagation tweak.
- §1.2 Constitution Check items all addressed (8 principles).
- Lint/type warnings limited to pre-existing baseline.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…p fix)
Audit follow-up to spec 023 Phase 1: the unit tests in
tests/unit/test_conversation_lock.py::TestRow5SequentialTurnsExtendChain
verify the orchestrator-dispatch contract (the correct primitive is
selected, TaskConflictError propagates) but mock the framework boundary
— they don't actually exercise two real POSTs through the chain to
verify the e2e behavior the SOT §11.1 promises.
Per spec 023 §4.1 Phase 1 step 4 depth assertion per Constitution
Principle XI, the row-5 fix promised verification of:
- chain's actual status between turns (suspended, not completed)
- turn-2's persisted response.output matches the handler's emitted output
- _responses framework metadata preserved across the turn boundary
The audit surfaced that the unit tests don't cover (b)+(c). This
commit adds the e2e coverage in
tests/e2e/test_durable_multiturn_e2e.py::TestRow5ConversationIdNonSteerableE2E:
1. test_two_sequential_turns_extend_chain_and_complete — two POSTs
on the same conversation, each reaching completed terminal.
Asserts:
- Both POSTs return 200 (NOT 409).
- Distinct response_ids per turn.
- Both turns share conversation_chain_id.
- Handler observed turn_count=1 then turn_count=2 (proves
_responses metadata persisted across the chain's
suspend/resume boundary; would be 1+1 if chain reset).
- Each turn's persisted response.output text contains that
turn's input + count (proves the actual handler output landed,
not a stale or generic value).
2. test_three_sequential_turns_extend_chain_correctly — same shape
with 3 turns to verify the chain pattern scales monotonically.
3. test_concurrent_overlap_still_returns_409 — regression guard for
the unchanged contract: concurrent overlap on the same conv_id
returns 409 conversation_locked with the documented body shape.
Uses an event-stream handler that emits response.created BEFORE
sleeping so the first POST returns 200 immediately while the
handler stays in_progress for the overlap window.
Uses the existing tests/_helpers.hypercorn_server async-context-manager
fixture so the AgentServerHost's lifespan triggers TaskManager
initialization (TestClient skips lifespan for sync code paths and
would silently fall back to the broad-exception bg fallback,
defeating the test's purpose).
Test sweep: 1286 passed (up from 1283; +3 new tests).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ng unification Phase 1 of spec 024 — work item #2 (bookkeeping pattern unification). Adds 7 structural RED tests in tests/unit/test_bookkeeping_pattern_removed.py that assert the bookkeeping primitives (_BOOKKEEPING_EVENTS, _run_bookkeeping_body, ensure_bookkeeping_event, complete_bookkeeping_task, _complete_bookkeeping_task, _shielded_runner) are gone from the production code and that Row 3 dispatch uses await TaskRun.result(). All 7 RED today; will turn GREEN after Phase 2 implementation. Adds 2 race-guard tests in tests/e2e/test_no_fast_handler_race.py that fire FAN_OUT=30 fast Row 2/Row 3 handlers in parallel and assert all reach terminal. Pre-Phase-2 GREEN-by-mitigation; post-Phase-2 GREEN-by-construction. Step 6 (Row 3 HTTP semantics) is verified via existing tests at tests/contract/test_create_endpoint.py::test_sync_handler_exception_returns_500 and test_error_source_classification.py::test_sync_handler_exception_returns_upstream per Principle XII §4 non-duplication. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Unifies handler execution across Row 1/2/3 (store=true): the handler now
runs inside the durable task body for ALL rows. The pre-Phase-2
"bookkeeping pattern" (separate durable task that just waits for the
external handler to signal completion via _BOOKKEEPING_EVENTS) is
deleted entirely.
Deletions in azure/ai/agentserver/responses/hosting/_durable_orchestrator.py:
- _BOOKKEEPING_EVENTS module-level registry
- DurableResponseOrchestrator._run_bookkeeping_body method
- DurableResponseOrchestrator.ensure_bookkeeping_event method
- DurableResponseOrchestrator.complete_bookkeeping_task method
- Fresh-entry mark-failed branch in _execute_in_task (handler now
runs through the same path as re-invoke disposition; only the
recovery branch differs)
Deletions in _orchestrator.py:
- ResponseOrchestrator._complete_bookkeeping_task method
- _bookkeeping_noop_runner function
- ensure_bookkeeping_event pre-registration call in _start_durable_background
- _complete_bookkeeping_task call in _persist_and_resolve_terminal
- The Row 2 (durable_bg=False+bg+store) double-path in run_background
(asyncio.create_task(_shielded_runner) + separate bookkeeping task)
Refactors in _orchestrator.py:
- run_background: unified path for all store=true rows — calls
_start_durable_background with disposition=re-invoke (Row 1) or
mark-failed (Row 2). Row 4 (no store) keeps plain asyncio.create_task.
- run_sync: handler runs inside durable task body; HTTP request awaits
task_run.result() (or execution_task fallback). Preserves B8/§3.1 via
record.response_failed_before_events + record.persistence_failed →
_HandlerError → HTTP 500. Preserves B17 by distinguishing server
shutdown (preserve for recovery) from client disconnect (evict +
delete from store + raise CancelledError). Synthesises S-015 failed
terminal when record.status stays in_progress after task completes.
- _live_stream: fast path now covers only `not ctx.store` (Row 4
stream). ALL ctx.store stream paths use the durable + wire_stream
pattern (was: only Row 1 stream). _unified_disposition selects
re-invoke vs mark-failed per row.
- _run_durable_stream_body: parameterised with background= kwarg (was
hardcoded True).
- _run_background_non_stream: skips transition_to when record.status
is already terminal (avoids invalid failed→in_progress when shutdown
marker beats handler). No-events fallback create_response now loads
history_ids when previous_response_id is set.
- _register_bg_execution: uses ctx.background instead of hardcoded
True; condition broadened from (bg AND store) to (store AND (bg OR
stream)) so Row 3 stream registers with background=False and
events fan out to wire_stream.
- _persist_and_resolve_terminal: emit-to-per-response-stream broadens
from (bg AND store) to (store AND stream) so Row 3 stream terminal
lands on wire_stream.
Endpoint changes in _endpoint_handler.py:
- handle_cancel: returns 404 (via fallback) for non-bg non-stream
in-flight records (Rule B16).
- handle_delete: same gating as handle_cancel.
ResponseExecution.visible_via_get (models/runtime.py): adds B16 clause
for non-bg non-stream — visible only after terminal status. Required
because the unified path adds record to runtime_state at accept-time
(vs. terminal-time pre-Phase-2).
Tests:
- tests/unit/test_bookkeeping_pattern_removed.py: 7 structural tests
now GREEN (were RED at the Phase 1 commit).
- tests/e2e/durability_contract/test_no_fast_handler_race.py: 2 race-
guard tests added in Phase 1, now in durability_contract/ dir so
they pick up the make_harness fixture.
- tests/unit/test_response_execution.py + test_runtime_state.py:
updated for the new visible_via_get B16 semantics.
- tests/e2e/durability_contract/CONTRACT_COVERAGE.md: registers
test_no_fast_handler_race.py.
Test results:
- Unit + contract + integration: 1016 / 1016 GREEN
- Durability contract suite: 37 / 37 GREEN
- E2E + interop: 320 passed / 5 skipped / 1 pre-existing baseline
failure (test_p02_path_b_graceful_recovery_with_reconnect — live
Copilot test, fails in baseline too)
- Core package: 829 passed / 5 skipped (unchanged from baseline)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds 10 RED tests across both packages for the storage-paths rename:
azure-ai-agentserver-core/tests/durable/test_storage_paths.py (6 tests):
- storage_paths module is public (PUBLIC, not _storage_paths)
- resolve_durable_subdir defaults to ~/.durable/{tasks,streams,responses}
- AGENTSERVER_DURABLE_ROOT env var override
- rejects unknown subdir kinds
- legacy AGENTSERVER_DURABLE_TASKS_PATH / STREAM_STORE_PATH no longer consulted
- _manager.py source no longer references the legacy paths
azure-ai-agentserver-responses/tests/unit/test_storage_paths_routing.py (4 tests):
- _routing.py source no longer references AGENTSERVER_STREAM_STORE_PATH
- _routing.py source no longer references AGENTSERVER_RESPONSE_STORE_PATH
- streams dir uses unified root via storage_paths
- responses dir uses unified root via storage_paths
All 10 RED at this commit; will turn GREEN after Phase 3a implementation.
Test-file rationale (Principle XII §4 non-duplication): no existing test
file covers default-path-resolution for the durable task store or the
responses-side stream/response store. The storage_paths helper is also
a NEW public module that warrants its own dedicated test file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… default
Unifies storage paths across azure-ai-agentserver-core (tasks) and
azure-ai-agentserver-responses (streams + responses). Single env var
AGENTSERVER_DURABLE_ROOT replaces three per-subsystem env vars:
- AGENTSERVER_DURABLE_TASKS_PATH (was: ~/.durable-tasks/)
- AGENTSERVER_STREAM_STORE_PATH (was: <tempdir>/agentserver_streams/)
- AGENTSERVER_RESPONSE_STORE_PATH (was: no default; was required for non-mem store)
Unified layout: ${AGENTSERVER_DURABLE_ROOT:-~/.durable}/{tasks,streams,responses}/
New PUBLIC module: azure.ai.agentserver.core.storage_paths
- DURABLE_ROOT_ENV_VAR = "AGENTSERVER_DURABLE_ROOT"
- DurableSubdir = Literal["tasks", "streams", "responses"]
- resolve_durable_root() -> Path
- resolve_durable_subdir(kind) -> Path
Phase 3a (cross-package rename):
- core/durable/_manager.py:478-484: uses resolve_durable_subdir("tasks")
- core/durable/_local_provider.py: default base_dir resolves via helper
- responses/hosting/_routing.py::_configure_streams_registry: uses
resolve_durable_subdir("streams")
- responses/hosting/_routing.py: response store default uses
resolve_durable_subdir("responses") + FileResponseStore (Phase 3b
folded in — InMemoryResponseProvider retired as default)
Phase 3b (file-backed response store as default — folded into Phase 3a
because the default path depends on the unified root resolution):
- Default store changes from InMemoryResponseProvider →
FileResponseStore(storage_dir=resolve_durable_subdir("responses"))
- Composition guard error message updated to reflect new default
Endpoint changes (preserve B16/B17 contract semantics that pre-Phase-2
were enforced by the record being absent from runtime_state):
- handle_cancel: returns 404 (via fallback) for non-bg in-flight
records (Rule B16)
- handle_delete: same gating
- _handle_get_fallback: SSE replay path checks persisted background
flag BEFORE attempting replay so non-bg streams get 400 per B2
- _handle_cancel_fallback: non-bg in-flight (status=in_progress/queued)
returns 404; terminal non-bg returns 400 "synchronous" per B1
Pipeline changes:
- _process_handler_events: pre-creation error events (B8 / B30 /
first-event contract violations) also emit to wire_stream for
unified store+stream paths so the live wire iterator sees them
- _process_handler_events: empty-handler synthesis broadens
wire_stream emit condition to ctx.store and (ctx.background or
ctx.stream)
- models/runtime.py::ResponseExecution.visible_via_get: B16 clause
covers non-bg responses regardless of stream flag (in_flight = not
visible)
Cross-package grep cleanup:
- core tests: test_input_promotion.py, test_steering_attachment_queue.py
use AGENTSERVER_DURABLE_ROOT
- responses tests: conftest.py, unit/test_streams_bootstrap.py,
unit/test_composition_guard.py,
integration/test_startup_composition_guard.py,
e2e/_crash_harness.py, e2e/durability_contract/_test_handler.py
all updated
- invocations tests + samples: _crash_harness.py,
test_durable_multiturn.py, test_durable_copilot_live.py,
samples/durable_research/{app,agent}.py all updated
Test results:
- core: 835 passed, 5 skipped (was 829 + 6 new in test_storage_paths.py)
- responses unit + contract + integration: 1015 passed / 5 pre-existing
baseline failures (down from 21 baseline failures: 16 fixed by Phase 3
cleanup; remaining 5 are pre-existing streaming-persistence-failure +
stream-disconnect tests that Phase 7 conformance closure will address)
- responses durability contract suite: 37 / 37 GREEN
- All Phase 3a RED tests turn GREEN
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d relaxation Work item #3 (default flip): - ResponsesServerOptions(durable_background: bool = True) → False - Handlers must now explicitly opt into crash recovery via durable_background=True. Documented breaking behavioural change. CHANGELOG entry required (Phase 8). Proposal #9 (composition guard relaxation): - Deleted the steerable+!durable_bg ValueError guard in _options.py - The two options are independent — steering chains extend across turns regardless of the durability disposition (the lock/queue semantics are independent of crash recovery) Tests: - tests/unit/test_options_validation.py: updated test_durable_background_defaults_true → defaults_false; added test_steerable_with_durable_background_off_does_not_raise (inverted from old test_steerable_true_requires_durable_background_for_bg) - tests/unit/test_steering_integration.py::test_steerable_requires_durable → test_steerable_with_durable_background_off_does_not_raise (inverted) - tests/integration/test_steerable_with_durable_bg_off.py (NEW): Phase 4 step 24a RED-first e2e conformance for relaxed composition. Two tests: (1) host construction with the combination succeeds; (2) three-turn chain extension on the same conversation_id all complete with the relaxed combination. Samples: all 5 durable samples (sample_17-21) + sample_22 already explicitly pass durable_background=True — no code changes needed because they were always explicit. (Dev guide updates documenting the default flip + the relaxed composition land in Phase 5 step 35.) Test results: - Unit + contract + integration: 1017 passed / 5 pre-existing baseline failures (Phase 7 will address) - Durability contract suite: 37 / 37 GREEN - E2E + interop: 320 passed / 5 skipped / 1 pre-existing baseline failure (test_p02_path_b live Copilot test — fails in baseline) - Core: 835 / 5 skipped (unchanged) - Net delta from Phase 3: +2 new tests, zero new failures Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…pec 024 Phase 5) Lands the §A APPROVED set of public-surface simplifications in a single coherent commit per Principle XII §4 non-duplication. Closes Phase 5 of spec 024 (responses re-design). ## Approved proposals applied - #4 — `max_pending` option DELETED. - #5 — `context.is_shutdown_requested` property DELETED (subsumed by #11's new `context.shutdown: asyncio.Event`). - #6 + #10 — `context.durability.*` flattened onto `ResponseContext`: `is_recovery`, `is_steered_turn`, `pending_input_count`, `durable_metadata` (typed as the new public `DurableMetadataNamespace` Protocol). - #8 — `store_disabled` option DELETED; composition guard `steerable + store_disabled` deleted with the predicate. - #11 — cancellation surface alignment (composing causes): `context.cancel: asyncio.Event` + `context.shutdown: asyncio.Event` + `context.client_cancelled: bool` + `async exit_for_recovery() -> ExitForRecoverySignal`. The `CancellationReason` enum + `cancellation_reason` property are DELETED. Handler signature is hard-rejected at decoration time if it is not `async def` or does not take exactly 2 args. - #12 — `replay_event_ttl_seconds` option DELETED; replaced with hardcoded `_REPLAY_EVENT_TTL_SECONDS = 600.0` constant in `hosting/_routing.py` (B35 ≥ 10 min replay verified GREEN). - #13 — `DurabilityEntryMode` Literal alias + `entry_mode` field DELETED. Recovery detection is now `context.is_recovery`. The `_map_entry_mode` helper is replaced by `_is_recovered_entry`. ## Source changes - `azure/ai/agentserver/responses/_options.py`: deleted parameters and composition guard. - `azure/ai/agentserver/responses/_response_context.py`: flat field surface + composing cancellation events + `exit_for_recovery()` + `DurableMetadataNamespace` Protocol + `ExitForRecoverySignal` type alias. Class-level type annotations added so `get_type_hints()` and IDEs surface the precise types. - `azure/ai/agentserver/responses/_durability_context.py`: `DurabilityContext` class + `DurabilityEntryMode` alias DELETED. `_DeveloperMetadataFacade` retained as internal impl. - `azure/ai/agentserver/responses/__init__.py`: export `DurableMetadataNamespace`, `ExitForRecoverySignal`, `FileResponseStore`; drop `CancellationReason`. - `azure/ai/agentserver/responses/models/runtime.py`: `CancellationReason` enum DELETED. - `azure/ai/agentserver/responses/hosting/_routing.py`: `_validate_handler_signature()` hard-rejects sync + 3-arg handlers at decoration time. `_dispatch_create` invokes with 2 args. `_configure_streams_registry` uses the hardcoded TTL constant. Decorator + dispatch surface aligned with the new contract. - `azure/ai/agentserver/responses/hosting/_endpoint_handler.py`: cancel-bridge sets `context.client_cancelled` / `context.shutdown` instead of stamping `cancellation_reason`. Disconnect monitor + cancel endpoint + shutdown handler all switched to the new composing surface. `_create_response_context` aliases `context.cancel` with the execution-context cancellation signal. - `azure/ai/agentserver/responses/hosting/_durable_orchestrator.py`: stops constructing `DurabilityContext`; assigns flat fields directly on `ResponseContext`; cancel-bridge maps `ctx.shutdown` → `context.shutdown.set() + cancel.set()` and `ctx.cancel` (steering pressure) → `cancel.set()` ONLY (no cause flag). `_map_entry_mode` replaced by `_is_recovered_entry` boolean helper. - `azure/ai/agentserver/responses/hosting/_orchestrator.py`: terminal routing reads `context.shutdown.is_set()` / `context.client_cancelled` instead of the deleted enum. All 3 `self._create_fn(...)` invocations updated to 2-arg. ## Sample updates (Principle IX) - Samples 17, 18, 19, 20, 21, 22 (durable) all updated: 2-arg handler signature, `context.cancel.is_set()` cancellation observation, flat `context.is_recovery` / `context.durable_metadata` access, new shutdown-event surface via `_simulate_shutdown`. - Samples 18 + 19 helpers (`_open_session`, `_completed_phase_index`, `_build_resumption_response`) take `context` instead of `durability`. - All 17 samples import cleanly. ## Test updates - 25-test RED suite `tests/unit/test_phase5_api_simplification.py` — ALL GREEN. - Obsolete `tests/unit/test_cancellation_reason.py` + `tests/unit/test_durability_context.py` DELETED. - `tests/unit/test_durable_orchestrator.py` rewritten for `_is_recovered_entry` + flat-context model. - Bulk-conversion script applied across `tests/contract/`, `tests/integration/`, `tests/e2e/`: 3-arg → 2-arg handler signatures, `cancellation_signal.X` → `context.cancel.X`, `context.cancellation_reason == X` → cause-boolean checks, `context.durability.X` → flat field equivalents. - Durability-contract harness (`tests/e2e/durability_contract/`) updated to drop `store_disabled` env knob and pass flat-context semantics through. ## Final test results - Unit: 617/617 GREEN. - Contract: 372/377 GREEN (5 pre-existing baseline failures: streaming-persistence-failure + stream-disconnect — unchanged from Phase 4 baseline; addressed by Phase 7 conformance gap closure). - Integration: 39/39 GREEN. - Interop: 62/62 GREEN. - E2e (excluding hosted-only): 188/189 GREEN (1 skip). - Durability-contract suite: 37/37 GREEN. - Total: 1315/1320 GREEN (5 pre-existing baseline). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ion (spec 024 Phase 6) Re-architect docs/responses-durability-spec.md for the post-spec-024 reality (bookkeeping unified into the task body in Phase 2; flat recovery + steering surface + composing cancellation surface in Phase 5). The spec is now standalone, language-agnostic, and replicable by a non-Python port. ## Sections rewritten ### §6 — Perpetual conversation-scoped task - §6 intro: dropped "two equivalent architectures" framing; one unified architecture described once. - §6.1 (Row 1): clarified disposition is `re-invoke` for crash recovery. - §6.2 (Rows 2/3): rewritten as "handler runs inside the task body with disposition=mark-failed" — same shape as §6.1. - §6.4 (Implementation note: handler execution model): DELETED. - §6.5 (Bookkeeping completion-event pre-registration): DELETED. - §6.6 (Primitive selection matrix): renumbered to §6.4. ### §7 — Recovery dispatch - §7.1 (re-invoke): rewritten for flat `context.is_recovery` / `context.durable_metadata` surface; dropped `entry_mode`, `retry_attempt`, `DurabilityContext.metadata` references. - §7.2 (mark-failed): rewritten as "task body persists failed and returns"; "completion event signal" references deleted. ### §8 — Handler-side recovery contract - §8 surface table rewritten for the flat fields: `is_recovery`, `is_steered_turn`, `pending_input_count`, `durable_metadata`. Old fields (`entry_mode`, `retry_attempt`, `was_steered`, `pending_inputs`, `metadata`) deleted. - §8.1 metadata semantics: documents the namespace-callable shape on `durable_metadata` and `flush()` durability fence; reserved `_`-prefix rule retained. ### §10 — Cancellation - Rewritten end-to-end for the composing-cause surface (`context.cancel: Event`, `context.shutdown: Event`, `context.client_cancelled: Bool`, `exit_for_recovery()` method). - Cause matrix added (5 trigger rows × 3 surface columns). - Steering pressure documented as "no cause flag" (matches task primitive contract). - `context.exit_for_recovery()` recovery-exit primitive documented: handlers MUST propagate via `return`, sentinel return value is framework-recognised. - §10.1 (Cancellation × recovery composition) updated for new surface; `STEERED` / `CLIENT_CANCELLED` / `SHUTTING_DOWN` enum rows replaced with cause-boolean equivalents. ### §3 — Dispatch matrix - Row 2/3 descriptions: "Bookkeeping-only durability" → "Crash-failed durability" (no more bookkeeping concept). - Termination paths table: "bookkeeping no-op / signal complete" → "task body returns"; "Bookkeeping body proactively persists" → "Task body persists"; Path C row updated. ### §13 — Worked sequences - Row 2 sequence: "ALSO start bookkeeping task with disposition= mark-failed (pre-register completion event)" + "asyncio.create_task (_shielded_runner)" → "start durable task with disposition=mark- failed" + "task body invokes handler (handler runs INSIDE the body)". - Recovery branch: "re-fire bookkeeping task body" → "re-fire task body" with `context.is_recovery=True`. ### §11 — Steering - `was_steered=True/False` → `is_steered_turn=True/False`. - Steering-pressure cancel signal described as "context.cancel Event set, no cause flag" (matches §10 surface). ### §14 — Conformance items - C-PERPETUAL: dropped "bookkeeping body MUST race three signals" language; describes shutdown-without-explicit-exit_for_recovery path. - C-DURABILITY-CTX: renamed and rewritten for the flat surface; type-annotated as `DurableMetadataNamespace` Protocol. ### §17 — Composition constraints - §17.3 (`steerable_conversations=true × durable_background=false`): rewritten to describe the relaxed composition (Phase 4) — handler runs inside the task body just like Row 1, only the disposition differs. - §17.4 (`background=false + steerable`): described as handler-in-task-body with HTTP request awaiting via `TaskRun.result()`. ### Other surface cleanups - `cancellation_signal` (handler arg) → `context.cancel event` throughout normative clauses. - `DurabilityContext` references → "recovery + steering context (flat fields on the response context)" with the type list inlined. - `replay_event_ttl_seconds` reference reframed as framework-internal with the "≥ 10 min" rule pinned to behaviour-contract Rule B35. - `entry_mode="recovered"` → `context.is_recovery=True`. ## Audit pass The §6 description of bookkeeping no longer exists. The §8 surface matches the implementation. The §10 cancellation contract matches the composing-event shape exposed by `ResponseContext`. Every mention of deleted symbols (`CancellationReason`, `DurabilityContext`, `store_disabled`, `max_pending`, `replay_event_ttl_seconds`, `entry_mode`, `retry_attempt`, `was_steered`, `pending_inputs`, `cancellation_signal`) has been rewritten or deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Closes spec 024 Phase 7. Lands:
1. **New conformance test suite** at
`tests/conformance/test_cancellation_cause_booleans.py` (10 tests,
all GREEN). Maps each §10 cause trigger to its observable surface
on `ResponseContext`:
- No-cancellation baseline shape
- Client cancel endpoint sets `client_cancelled=True` + fires `cancel` event
- Composing causes (client_cancelled + shutdown both set together)
- Steering pressure has no cause flag
- Handler signature validation (2-arg async accepted; sync 2-arg + 3-arg
async + 3-arg sync all rejected at decoration time)
- `exit_for_recovery()` raises outside durable context
- `ExitForRecoverySignal` sentinel exported and non-None
2. **Fix to `_orchestrator.py::_process_handler_events` Phase-1
persistence-failed branch**: non-bg streaming now emits the standard
`response.created → response.failed` sequence (with
`error_code=storage_error`) per B27 first-event invariant, instead
of the pre-spec-024 standalone `error` SSE event that violated B27.
Bg+stream retains the standalone `error` event (the HTTP request
hasn't returned a queued response yet, so promising a
`response.failed` would be incorrect — the client never observes
the response envelope at all). This fixed the long-standing
`test_streaming_terminal_persist_fails` baseline failure.
## Test results
- Unit: 617/617 GREEN
- Contract: 374/378 GREEN (4 pre-existing baseline failures —
environment-edge-case disconnect timing + runtime state lookup
after stream finalize; carry over to Phase 11 release notes)
- Integration: 39/39 GREEN
- Interop: 62/62 GREEN
- E2e (excluding hosted): 188/189 GREEN (1 skip)
- Durability-contract suite: 37/37 GREEN
- Conformance: 10/10 GREEN (new suite)
Total: 1325/1330 GREEN. +10 vs Phase 5 baseline. -1 baseline failure
fixed via §3.2 storage_error wire-format alignment.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Apply repository-wide black configuration (`eng/black-pyproject.toml`, line-length=120) across the responses package — 25 files reformatted: `azure/ai/agentserver/responses/` hosting orchestrator + endpoint handler + response context, plus tests + samples that were touched during spec 024 Phase 5/6/7. Test sweep unchanged: 1325 passed / 4 pre-existing baseline (no regressions from reformat). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…024 Phase 9 + 10)
Address all BLOCKER findings surfaced by the consolidated rubber-duck
self-audit pass (R-5/R-6/R-7/R-8/R-10).
## Production-code fixes
### `ResponseContext.conversation_chain_id` honours `steerable_conversations`
Pre-audit the property hardcoded `steerable=True` when calling
`derive_chain_id`, producing the wrong chain id for non-steerable
deployments + `previous_response_id`-based requests. Fix:
- Added `steerable: bool = False` parameter to `ResponseContext.__init__`.
- Stashed on `self._steerable`.
- `conversation_chain_id` property now passes `self._steerable` to
`derive_chain_id`.
- `_endpoint_handler._create_response_context` wires
`steerable=runtime_options.steerable_conversations` through to the
constructor.
- Removed the outdated "this property assumes steerable=True semantics"
docstring note.
### SOT spec final stale-field cleanup
- `pending_inputs` → `pending_input_count` throughout normative clauses.
- `ctx.entry_mode == "recovered"` → `context.is_recovery=True`.
- `durability_ctx` → "flat-field assignment on context".
- `retry_attempt=1` references in worked sequences removed.
### Sample 18 stale reference
- `entry_mode == "recovered"` → `context.is_recovery == True` in the
module docstring's recovery-flow description.
### `streaming/README.md` stale reference
- `options.replay_event_ttl_seconds` → `_REPLAY_EVENT_TTL_SECONDS = 600.0`
hardcoded framework constant. Notes Rule B35 (≥10 min replay)
compliance explicitly.
### `_routing.py` docstring stale example
- Legacy `def my_handler(request, context, cancellation_signal):`
example updated to `async def my_handler(request, context):`.
## Test updates
### `test_conversation_chain_id`
- `_make_context` helper now defaults `steerable=True` (the existing
tests in this module all assert steerable-chain semantics).
- New test `test_chain_id_non_steerable_uses_response_id_via_property`
pins the post-audit non-steerable behaviour: when `steerable=False`
the property returns `response_id` even with `previous_response_id`
set (per SOT §4.1).
## Self-audit findings deferred to handoff
- **B17 internal-test contradiction**: two contract tests have
contradictory expectations for non-bg + store=true + client disconnect:
- `test_e6_disconnect_then_get_returns_not_found` expects GET 404
(the current behaviour, pre-spec-024 baseline).
- `test_e12_stream_disconnect_then_get_returns_cancelled` expects
GET 200 with status=cancelled (matches behaviour-contract Rule
B17 per rubber-duck reading).
Resolution requires the spec author to pick the canonical
interpretation. Left to handoff — neither test was introduced by
spec 024.
## Final test results
- Unit: 619/619 GREEN (+2 new chain_id non-steerable assertions)
- Contract: 374/378 GREEN (4 pre-existing baseline)
- Integration: 39/39 GREEN
- Interop: 62/62 GREEN
- E2e (excluding hosted): 188/189 GREEN (1 skip)
- Durability-contract: 37/37 GREEN
- Conformance: 10/10 GREEN
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… 11) Document every breaking change, public-API addition, architectural simplification, and bug fix landed by the spec 024 commit sequence (commits 0334b98 through b69096f) under the 1.0.0b7 (Unreleased) section. ## Breaking Changes documented - Default `durable_background` flip to False (B18 behaviour shift) - File-backed response store as new default - Unified storage root + AGENTSERVER_DURABLE_ROOT env var - Handler signature: 2-arg async only (sync + 3-arg hard rejected) - Cancellation surface: cause-boolean composition replaces CancellationReason enum + cancellation_reason property - Recovery + steering fields flattened onto ResponseContext (DurabilityContext + DurabilityEntryMode + entry_mode + retry_attempt + was_steered + pending_inputs + metadata removed) - ResponsesServerOptions simplified (max_pending, store_disabled, replay_event_ttl_seconds removed; composition guard relaxed) ## Public-API additions documented - DurableMetadataNamespace Protocol - ExitForRecoverySignal type alias - FileResponseStore export ## Architectural simplifications documented - Bookkeeping unification (handler always in task body) - SOT spec architectural rewrite ## Bug fixes documented - Sequential turn 409 conversation_locked fix (carried from prior session) - conversation_chain_id non-steerable bug fix (this session's audit) - B27 wire-format alignment for non-bg streaming Phase-1 persistence failure Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Phase 8 + 10)
Tick the deferred quality-gate sub-steps by actually running them.
## Results
- **bandit**: GREEN — 0 HIGH, 0 MEDIUM severity findings; 21 LOW
(expected — assert statements in tests/samples).
- **mypy**: GREEN for spec 024 code — all 22 errors are pre-existing
`import-untyped` for `azure.ai.agentserver.core` modules (no
`py.typed` shipped). Zero spec 024 errors.
- **pyright**: GREEN for spec 024 code after fixing 3 Phase 5
regressions:
- `_durable_orchestrator._execute_in_task`: type-narrowing assertions
for `record`, `context`, and `self._runtime_state` after the
reconstruction block.
- `_orchestrator._run_background_non_stream` callsite (fallback
runner): `ctx.context` is always non-None in practice but typed
Optional; `# type: ignore[arg-type]` (with explicit reasoning).
- `_orchestrator.dispatch_acceptance_hook` callsite: same pattern.
Only remaining errors are pre-existing generated-code issues in
`_patch.py` (temperature field type override).
## Doc cleanup
- `streaming/README.md` operator-instruction updated:
`AGENTSERVER_STREAM_STORE_PATH` → `AGENTSERVER_DURABLE_ROOT`
(the spec-024 unified storage-root env var). Pre-spec-024 var
explicitly called out as deprecated.
## Spec checkbox closure
All Phase 0-10 sub-checkboxes now `[x]`. The only remaining `[ ]`
boxes in the spec are non-actionable for this session:
- 3 PR-to-main creation tasks (explicit user actions; branch NOT
pushed per user direction)
- 6-line spawn-loop checklist (procedural template for future
sub-agent fan-out runs, not actual tasks)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Closes all BLOCKERS and most CONCERNS / MISSING IMPLEMENTATIONS surfaced by the final rubber-duck self-audit pass. ## BLOCKERS fixed ### Version bump 1.0.0b7 → 1.0.0b8 - ``_version.py``: VERSION constant bumped. - ``CHANGELOG.md``: section heading bumped to 1.0.0b8 (Unreleased). Spec 024 mandated this bump; pre-audit it was missed. ### B17 contract conformance (non-bg disconnect persists cancelled) - ``_orchestrator.py`` (sync background path): on non-bg + client disconnect, persist a ``cancelled`` snapshot via ``update_response`` and leave the in-memory record in cancelled state instead of deleting from the provider + evicting. With ``store=true`` GET now returns 200 + status=cancelled per behaviour-contract Rule B17. With ``store=false`` GET still returns 404. - ``test_e6_disconnect_then_get_returns_not_found`` renamed + rewritten to ``test_e6_disconnect_then_get_returns_cancelled`` — the prior assertion encoded the pre-spec-024 (wrong) behaviour; the new assertion matches B17 + sibling ``test_e12_stream_disconnect_then_get_returns_cancelled``. ### Handler signature validation tightened - ``_routing.py::_validate_handler_signature``: ``*args``-style handlers are now hard-rejected at decoration time with ``TypeError``. Pre-audit the validator silently accepted them (returning early), bypassing the "exactly two positional parameters" contract. ### SOT spec stale-field cleanup (final pass) - ``docs/responses-durability-spec.md``: scrubbed remaining references to ``retry_attempt``, ``durability_ctx``, ``ctx.entry_mode``, ``ctx.retry_attempt``, and Python-implementation file paths in §21 change-discipline normative clauses. The doc is now fully language-agnostic and free of pre-spec-024 field names. ## CONCERNS addressed ### DurableMetadataNamespace Protocol widened to MutableMapping shape - ``_response_context.py::DurableMetadataNamespace``: added ``__iter__``, ``__len__``, ``keys``, ``values``, ``items``, ``clear``, ``pop``, ``setdefault``, ``update``. Matches the underlying ``_DeveloperMetadataFacade``'s ``MutableMapping`` surface — handler code that calls e.g. ``context.durable_metadata.clear()`` (sample 22) now typechecks cleanly against the Protocol annotation. ### Stale production-code comments scrubbed - ``_durable_orchestrator.py``: dropped pre-spec-024 ``_shielded_runner`` + "bookkeeping pattern / body / task" references from module docstring + ``_one_shot_response_task`` + ``_persist_crash_failed`` docstrings. Deletion-acknowledgement comments retained. - ``_orchestrator.py``: dropped vestigial ``_bookkeeping_noop_runner`` comment block; clarified the post-spec-024 task-body completion flow in the inline comment near ``_persist_and_resolve_terminal``; updated the non-bg stream-interrupted shutdown-recovery comment to drop the "bookkeeping task" framing. ## MISSING IMPLEMENTATIONS — closed via 11-test audit-closure suite New file ``tests/conformance/test_spec_024_audit_closure.py`` pins: 1. ``test_default_store_is_file_backed`` + ``test_default_store_uses_default_durable_root_when_env_unset`` — work item #1 (default response store is file-backed under ``${AGENTSERVER_DURABLE_ROOT:-~/.durable}/responses/``). 2. ``test_client_cancelled_observed_by_handler_after_cancel_endpoint`` — §10 cause matrix end-to-end via TestClient + polling pattern; verifies live ResponseContext exposes ``client_cancelled=True`` AND ``cancel.is_set()`` AND ``shutdown.is_set()=False`` after the /cancel endpoint fires. 3. ``test_durable_metadata_protocol_includes_mutable_mapping_methods`` + ``test_concrete_metadata_facade_satisfies_protocol_at_runtime`` — pins the Protocol widening + runtime conformance. 4. ``test_handler_signature_rejects_var_positional`` + ``test_handler_signature_rejects_kwargs_only`` — pins the tightened validator. 5. ``test_exit_for_recovery_sentinel_propagates_through_dispatch`` — §10 ``context.exit_for_recovery()`` sentinel behaviour via the TestClient fallback path (raises RuntimeError outside a real durable context, proving the dispatch wired it through). 6. ``test_is_steered_turn_set_on_drain_reentry_via_orchestrator`` — spec 024 Proposal #10/#13 wire-up: durable orchestrator copies ``ctx.is_steered_turn`` to ``context.is_steered_turn`` on every entry; ``is_recovery`` stays False for "resumed" entries. 7. ``test_proposal_9_steerable_durable_off_does_not_raise`` + ``test_proposal_9_steerable_durable_off_host_constructs_cleanly`` — spec 024 Proposal #9 composition-guard relaxation pinned at both options-level and host-construction-level. ## Test results - Unit: 619/619 GREEN - Contract: 374/378 GREEN (4 pre-existing baseline failures — test_e12 stream-disconnect-then-get + the 3 streaming-persistence edge cases; documented as Hypercorn timing / runtime-state edge cases, NOT introduced by spec 024) - Integration: 39/39 GREEN - Interop: 62/62 GREEN - E2e (excluding hosted): 188/189 GREEN (1 skip) - Durability-contract: 37/37 GREEN - Conformance: 21/21 GREEN (+11 audit-closure tests this commit) Total: 1338/1343 passing (99.6%). +11 vs Phase 9 baseline. test_e6 moves from "asserting pre-spec-024 wrong behaviour" to GREEN under the new B17-conformant code path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…fix stale field names + defaults Two consecutive rubber-duck audits surfaced the same root cause across all 7 public docs (README, samples/README, streaming/README, dev guide, handler-implementation-guide, SOT spec, CHANGELOG): the docs were still describing pre-spec-024 surface (DurabilityContext nested object, CancellationReason enum, cancellation_signal 3rd handler arg, durable_background=True default, InMemoryResponseProvider default, legacy env vars) and were leaking internal-spec terminology (specs/ paths are gitignored). User flagged two cross-cutting issues: 1. Public docs MUST NOT reference 'spec 024', 'Phase N', 'Proposal #N', 'pre-spec-024', 'behaviour-contract Rule B#', CHANGELOG-internal references, etc. (specs/ is internal/gitignored). 2. Durability features have not shipped — no 'breaking change' / 'behaviour change' framing. Describe current behaviour, not history. ## Bulk cleanup (across all public docs) - Stripped 'spec 024 / Phase N / Proposal #N / pre-spec-024 / post-spec-024' references. - Stripped 'behaviour-contract Rule BN' citations. - Stripped CHANGELOG-internal references. - Reframed 'breaking change' language to 'contract change' where the context is forward-looking; deleted the framing entirely where it was describing the current spec changes (durability never shipped). ## README.md - ResponseContext field table rewritten to expose the actual public surface (was advertising deleted `is_shutdown_requested` field; missing all the cancellation + recovery + steering fields). - 'Durability' paragraph reframed as opt-in via `ResponsesServerOptions(durable_background=True)` (was claiming it as automatic default). - Samples table: corrected 4 broken paths (`samples/durable_claude/agent.py` → `samples/sample_17_durable_claude.py` etc.) and added 2 missing samples (19 streaming, 20 steering, 22 multiturn). - Handler example: 3-arg sync signature → 2-arg async. - 404 troubleshooting reference: 'expired TTL' → unified storage root. ## samples/README.md - Added samples 17-22 (the durable + steerable samples) to the index with their explicit ResponsesServerOptions opt-ins; clarified that durable + steerable behaviour requires explicit opt-in since both default to False. - Added 'Enabling durability and steering' guidance section. ## docs/durable-responses-developer-guide.md - 'Overview': 'durable_background=True (the default)' → 'opt-in' with explicit default-is-False callout. - 'What is durable_metadata for' example: fixed the false 'auto-flushed' claim; explicitly recommend `await context.durable_metadata.flush()` between watermark write and side effect. - Configuration table: corrected `durable_background` default to False; removed deleted options (`store_disabled`, `replay_event_ttl_seconds`). - Configuration matrix: 'entry_mode="recovered"' → 'context.is_recovery == True'. - 'Provider configuration for local-dev recovery testing': removed reference to nonexistent `LocalDurableProvider`; updated response store and stream store guidance to current defaults. - 'DurabilityContext API' section deleted; replaced by 'Recovery + steering surface on ResponseContext' describing flat fields. - 'Notes on Metadata' rewritten for `context.durable_metadata`. - 'What you get on recovered entry': removed `context.durability.X` references; described cause-boolean cancellation surface. - 'Layered Concerns' section: deleted `DurabilityContext` and `CancellationReason` references; described composing-cause surface and flat recovery fields. - 'Watermark before side effects' best-practice: corrected to `context.durable_metadata` + explicit flush. ## docs/handler-implementation-guide.md - ~40 handler signature occurrences (sync def + 3-arg async/sync) rewritten to 2-arg async at decoration. - '*args' / sync-handler hint deleted; documented hard-rejection at decoration time. - 'Cancellation' section rewritten end-to-end for the composing-cause surface (cancel + shutdown Events, client_cancelled bool, steering pressure with no cause flag, `exit_for_recovery()` recovery primitive). - 'Advanced Pattern (pre-entry steering)' replaced with branch-on-cause pattern; bare `return` on shutdown changed to `return await context.exit_for_recovery()`. - 'Default Pattern' uses `context.cancel.is_set()`. - 'TextResponse Handlers' cancellation example uses `context.cancel.is_set()`. - 'ResponseContext' field table rewritten for the actual public surface (was advertising deleted `cancellation_reason` + `durability` fields); added cancellation + recovery + steering fields. - 'Durability' section: 'Library', 'Handler', 'Recovery Loop', 'What the Library Does', 'What the Handler Does' all rewritten for flat context surface and `exit_for_recovery()` primitive. - 'Default Pattern (recovery-aware)' rewritten: drops `CancellationReason` import, removes nested durability local, branches on flat cause booleans, uses `exit_for_recovery()` on shutdown deferral. - 'Recovery × Cancellation Composition' rewritten for new surface. - 'Configuration' table: corrected `durable_background` default; removed deleted `replay_event_ttl_seconds`. - 'Check Cancellation in Loops' best-practice: 'cancellation_signal' → 'context.cancel.is_set()'. - 'Expecting the Library to Hand You a Snapshot' antipattern: removed `durability.last_snapshot` references. ## docs/responses-durability-spec.md - §3 `durable_background` defaults: 'true' → 'false' with the opt-in semantics explained (matches the implementation in `_options.py`). - Stripped remaining `retry_attempt` / `durability_ctx` / `ctx.entry_mode` references from worked sequences. - Removed `_durable_orchestrator.py` / `_orchestrator.py` / `_endpoint_handler` / `_crash_harness` repo-file-path mentions from §21 'Change discipline' (the SOT spec is language-agnostic). ## azure/ai/agentserver/responses/streaming/README.md - Code example: 'options.replay_event_ttl_seconds' → '_REPLAY_EVENT_TTL_SECONDS hardcoded 600.0' constant. - 'AGENTSERVER_STREAM_STORE_PATH is no longer consulted' aside removed; replaced with positive description of unified AGENTSERVER_DURABLE_ROOT. ## CHANGELOG.md (1.0.0b8 entry rewritten) - Reframed from 'spec 024 — responses re-design' headers to neutral 'Breaking Changes' + 'New Public API Surface' + 'Bugs Fixed'. - BREAKING CHANGES section now lists ONLY the items that actually affect existing 1.0.0b6/b7 consumers: 1. Handler signature: async 2-arg (was sync OR async 3-arg). 2. Default response store: file-backed (was in-memory). - NEW PUBLIC API SURFACE describes the additions (ResponseContext fields, DurableMetadataNamespace Protocol, ExitForRecoverySignal, FileResponseStore export, durable_background + steerable_conversations options, AGENTSERVER_DURABLE_ROOT env var) as ADDITIONS, not 'breaking changes' (none of these ever shipped). - Removed bookkeeping/Model A/B/spec internal-history paragraphs. - 'Other Changes' duplicate header collapsed. ## Test sweep - Unit: 619/619 GREEN - Conformance: 20/20 GREEN - (Contract/integration/interop/e2e/durability-contract suites unchanged by docs-only edits) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…couple shutdown from cancel Two surface fixes raised in audit review: 1. **Shutdown and cancel are distinct signals.** The framework was firing ``context.cancel.set()`` immediately after every ``context.shutdown.set()`` so handlers awaiting the cancel event would wake on shutdown too. That conflated two semantically different signals — shutdown demands ``exit_for_recovery()`` (or a quick failed/incomplete emit), while cancel demands a graceful finish or status-aware terminal. Handler expectations are different for each. Decoupled all five framework call-sites that paired the two; shutdown now fires only ``context.shutdown.set()`` and handlers that care about both must observe each surface independently. 2. **Restored the shipped 1.0.0b6 ``cancellation_signal`` 3rd positional handler argument.** This was already shipped on origin/main (``_routing.py``, ``_endpoint_handler.py``, ``_orchestrator.py`` all reference ``cancellation_signal`` as the 3rd positional Event). The branch-local change to a 2-arg signature with ``context.cancel`` was needlessly breaking shipped consumers. The only surface change for shipped users is now the sync→async restriction — that's the single genuine breaking change. (Confirmed via ``git show origin/main:.../_response_context.py`` that ``cancellation_reason`` was NEVER shipped — branch-local addition, already removed in a prior pass.) ## Framework changes - ``_response_context.py``: dropped ``cancel: asyncio.Event`` from the public surface and ``__init__``. The Event is now framework-private on ``_cancellation_signal`` (used by ``/cancel`` endpoint and disconnect monitor). Handlers observe the same Event via their 3rd positional ``cancellation_signal`` parameter — the framework wires them to be the SAME Event instance. - ``_routing.py``: ``_validate_handler_signature`` now requires exactly 3 positional args (was 2); ``CreateHandlerFn`` type alias re-aliased to ``Callable[[CreateResponse, ResponseContext, asyncio.Event], ...]`` (the shipped 1.0.0b6 shape); ``_dispatch_create`` takes and forwards the cancellation_signal as the 3rd positional arg to the user handler. - ``_orchestrator.py``: all 4 dispatch call-sites (``_run_background_non_stream``, ``_run_sync_inner``, ``run_sync``, ``_run_durable_stream_body``) now pass cancellation_signal as the 3rd positional arg. - ``_endpoint_handler.py``: ``context.cancel = ctx.cancellation_signal`` alias replaced with ``context._cancellation_signal = ctx.cancellation_signal``; per-request ``/cancel`` endpoint fires ``response_context._cancellation_signal.set()`` (private path); the graceful-shutdown loop at line 1722 stops calling ``response_context.cancel.set()`` — only fires ``response_context.shutdown.set()``. - ``_durable_orchestrator.py``: the bridge between task primitive ``ctx.cancel`` / ``ctx.shutdown`` and the handler-facing surface no longer cross-pollinates. Shutdown branch only fires ``context.shutdown.set()``; cancel branch only fires the local ``cancellation_signal`` Event (which is the same Event the handler observes as its 3rd positional arg). The race resolver in the ``_bridge()`` task does the same — picks one surface based on which ctx Event won the race. ## Samples (5 durable samples + 9 transitive) - 5 ``_simulate_shutdown`` helpers now fire only ``context.shutdown.set()`` (was ``context.shutdown.set(); context._cancellation_signal.set()`` — paired with the old conflated framework behaviour). - All 22 samples migrated to the 3-arg handler signature via AST rewrite. Sample-body ``context.cancel.*`` references converted to ``cancellation_signal.*``. Helper functions whose 2nd arg happens to be named ``context`` (but which aren't response handlers) were explicitly excluded — over-patched ``_send_input_if_not_in_session`` in sample 18 was reverted. ## Tests (~80 test files) - AST-driven sweep migrated all handler signatures + bodies. Test fixtures that build a fake ``ResponseContext`` were rewritten to set ``ctx._cancellation_signal`` (the new private framework path). - ``test_phase5_api_simplification.py``: rewrote ``test_context_has_cancel_event`` to instead assert the public ``cancel`` field was removed and ``_cancellation_signal`` is framework-private. ``test_context_has_shutdown_event`` now also asserts ``shutdown is not _cancellation_signal``. - ``test_cancellation_cause_booleans.py``: flipped ``test_three_arg_handler_hard_rejected`` → ``test_three_arg_async_handler_accepted`` (the 3-arg shape is now required). Added ``test_two_arg_async_handler_hard_rejected`` for the new reject path. - ``test_spec_024_audit_closure.py``: ``test_handler_signature_rejects_kwargs_only`` updated to test rejection of ``(*, request, context, cancellation_signal)`` against the new "three positional" error message. - ``test_steerable_chain_validation.py``: reverted over-patched ``fake_run_background(self, ctx, cancellation_signal)`` stub back to ``(self, ctx)`` — that stub patches an orchestrator method, not a handler. - 5 recovery_sample test ``_drive`` helpers updated to pass ``context._cancellation_signal`` as the 3rd positional arg to the handler (they exercise samples directly). ## Docs - ``CHANGELOG.md``: Breaking-changes section pared down to the single genuine break for shipped 1.0.0b6 consumers (sync handlers rejected — async-only). The 3-arg shipped signature is preserved. Reframed ``ResponseContext`` description: shutdown and cancellation signal are independent surfaces; the cancellation Event is delivered as the 3rd positional handler arg. - ``README.md``: handler example now shows the shipped 3-arg async signature with ``cancellation_signal: asyncio.Event``. Rewrote the "Handlers MUST be" paragraph for the new shape. Removed the ``cancel`` row from the ResponseContext property table and added an explicit "the cancellation signal is delivered as the 3rd positional handler argument" note. - ``docs/handler-implementation-guide.md``: ``Cancellation`` section rewritten end-to-end. Two surfaces (``cancellation_signal`` for cancel triggers, ``context.shutdown`` for shutdown), cause-flag ``client_cancelled`` for distinguishing client-cancel vs steering. Cause matrix updated: shutdown row now shows ``cancellation_signal: not set, shutdown: set``. Default pattern updated to observe both events in the work loop. Long ResponseContext type stub updated: removed ``cancel`` field, retained shutdown + client_cancelled. - ``docs/responses-durability-spec.md``: §10 (Cancellation) rewritten for the decoupled surface. Cause matrix updated. SOT spec describes the contract language-agnostically. - ``docs/durable-responses-developer-guide.md``: cancellation-contract paragraph in "Layered Concerns" rewritten for the two-surface model. - All 2-arg ``async def handler(request, context)`` doc examples rewritten to 3-arg via regex sweep. All ``context.cancel.X`` doc references rewritten to ``cancellation_signal.X``. ## Test sweep - Unit: 619/619 ✅ - Conformance: 21/21 ✅ - Contract: 374/378 ✅ (4 pre-existing baseline) - Integration: 39/39 ✅ - Interop: 60/60 ✅ - Durability-contract: 37/37 ✅ - Recovery samples (17-21 mocked): 20/20 ✅ - Durable e2e subset: 29/29 ✅ - Other e2e subset: 64/65 ✅ (1 was the over-patched stub, fixed) - Pyright: 0 new errors (4 pre-existing in generated models) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rver-responses-spec016 # Conflicts: # sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/durable/_local_provider.py # sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/durable/_manager.py # sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/storage_paths.py # sdk/agentserver/azure-ai-agentserver-core/tests/durable/test_storage_paths.py
…rver-durable-agent-demo
…rver-durable-agent-demo # Conflicts: # sdk/agentserver/wheels/azure_ai_agentserver_core-2.0.0b7-py3-none-any.whl # sdk/agentserver/wheels/azure_ai_agentserver_invocations-1.0.0b6-py3-none-any.whl
…rver-responses-spec016
…ck + CHANGELOG cleanup + correct hosted-vs-local storage framing
Four cleanups on the responses branch following audit review:
## 1. Remove Claude sample altogether
- Deleted ``samples/sample_17_durable_claude.py`` and its mocked
recovery test ``tests/e2e/test_recovery_sample_17_mocked.py``.
- Stripped sample-17 / Claude SDK references from ``samples/README.md``,
``README.md``, ``docs/durable-responses-developer-guide.md``, and
``docs/handler-implementation-guide.md`` (kept generic "upstream
framework" language).
## 2. Simplify steering detection in samples + tests
The previous pattern was a double-negation:
if cancellation_signal.is_set():
if cancellation_signal.is_set() and not context.client_cancelled and not context.shutdown.is_set():
# steering branch
After the shutdown/cancel decoupling, the negation is no longer
necessary — shutdown does NOT fire cancellation_signal, so when the
cancel signal IS set the cause is either client-cancel or steering.
The clean test for steering is:
if cancellation_signal.is_set() and context.pending_input_count > 0:
# steering branch
Applied to samples 18, 20, 21 and to
``tests/e2e/test_recovery_contract.py``.
Also tightened the pre-entry / mid-stream cancel-or-shutdown checks
in samples 18, 19, 20, 21. Previously these only observed
``cancellation_signal``; with shutdown now a distinct surface, the
handlers also need ``or context.shutdown.is_set()`` so the early
return / loop break fires on shutdown too.
## 3. CHANGELOG cleanup (1.0.0b8 entry)
- Reframed away from "Breaking Changes" — durability hasn't shipped
and no released consumer is affected.
- Reorganised as "Features Added" (durable + steerable conversations,
ResponseContext recovery + steering surface, DurableMetadataNamespace
Protocol, ExitForRecoverySignal alias, FileResponseStore export,
local-dev file-backed default, AGENTSERVER_DURABLE_ROOT env var) +
a single genuine "Breaking Changes" entry (sync handlers rejected).
- Removed dev-iteration internal details: "ephemeral=False eliminated",
"DurableResponseOrchestrator now registers two task primitives", "the
shutdown-mid-handler branch now calls ctx.exit_for_recovery() instead
of raising CancelledError", and the four bug-fix entries that all
describe in-flight dev-loop fixes for the unshipped durability work.
## 4. Correct hosted-vs-local storage framing
- ``CHANGELOG.md``: "Default response store is now file-backed" was
misleading — that change only affects local development. In hosted
deployments the default remains the Foundry hosted responses storage
API, unchanged. Reframed: "Local-development default response store
changed from in-memory to file-backed."
- ``README.md`` 404-troubleshooting note: distinguishes hosted (Foundry
storage) from local (file-backed under AGENTSERVER_DURABLE_ROOT).
- ``docs/durable-responses-developer-guide.md`` configuration section:
rewrote response-store + task-store bullets to lead with hosted
behaviour (Foundry API) and then describe the local-dev default
(file-backed). Also mentions the new ``AGENTSERVER_TASKS_BACKEND``
operator override for forcing one provider in either env.
## Test sweep
- Unit + Conformance + Contract + Integration + Interop: 1113/1117
GREEN (same 4 pre-existing Hypercorn streaming-disconnect baseline
failures unrelated to these changes).
- Recovery sample tests (18, 19, 20, 21) + recovery contract: 34/34
GREEN. Test fixtures that simulate steering pre-entry updated to
stamp ``pending_input_count = 1``; those that simulate shutdown
pre-entry updated to set only ``context.shutdown`` (no longer set
``_cancellation_signal``).
- Black formatted.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tence resilience + B11/B17 client-cancel override) Three root-cause fixes that turn the 4 pre-existing baseline failures (non-bg streaming persistence-resilience + B11/B17 client-cancel override) GREEN without regressing the existing 1109 inner-suite tests. ## Root cause 1 — ``state.handler_events`` missing the synthesised ``response.failed`` In ``_process_handler_events``, the non-bg streaming Phase-1 persistence failure branch emits ``response.created`` followed by ``response.failed`` to the SSE wire and stamps ``state.bg_record.status = "failed"``. But ``state.handler_events`` only had the ``response.created`` event appended — the synthesised ``failed_normalized`` was emitted to the wire and yielded to the SSE iterator but never recorded in the event list. ``_finalize_stream`` Path B then runs and rebuilds a fresh ``ResponseExecution`` from ``state.handler_events`` via ``_extract_response_snapshot_from_events``. Because the event list only contains ``response.created``, the rebuilt record gets ``status="in_progress"`` and OVERWRITES the in-memory record we just stamped as ``"failed"``. Subsequent GETs then see ``visible_via_get == False`` (non-bg requires a terminal status) and return 404 — regressing the persistence-failure resilience contract from §4.2 / §4.3. Fix: append ``failed_normalized`` to ``state.handler_events`` right after it's emitted to the wire, so the subsequent finalize sees a terminal in the event list and rebuilds the record with ``status="failed"``. ## Root cause 2 — B11/B17 client-cancel override didn't apply when the handler emitted a terminal ``_persist_and_resolve_terminal`` derived the terminal status from the handler's emitted events without checking ``context.client_cancelled``. For non-bg streaming, the disconnect monitor sets ``client_cancelled`` + fires the cancellation_signal. A well-behaved handler observes the signal, breaks its work loop, and emits its own terminal (often ``response.completed`` with the partial output that fit before the disconnect). The framework was honoring that terminal — so a client disconnect that should have resulted in ``status=cancelled`` per B11 + B17 instead surfaced as ``status=completed``. The override block at line 1822 only fires for the handler-emitted-no- terminal case (``not _has_terminal_event``). For the handler-emitted-something-else-but-client-cancelled case, no override ran. Fix: in ``_persist_and_resolve_terminal``, after computing ``status`` from the events, check ``context.client_cancelled`` and — if set and ``status != "cancelled"`` — rebuild ``response_payload`` from ``_build_cancelled_response`` and force ``status = "cancelled"``. Replace ``state.pending_terminal`` with the override event so SSE wire emission and persistence are consistent. ## Root cause 3 — test assertion checked the wrong flag ``test_cancel__stream_disconnect_sets_handler_cancellation_signal`` asserted ``not handler_completed.is_set()``, but the handler under test always sets ``handler_completed`` in its post-loop close-events block — even on cancellation, because Python ``break`` from the work loop falls through to the post-loop emit_text_done / emit_done / response.incomplete sequence. The assertion can only ever be true if ``asyncio.CancelledError`` propagates and kills the handler mid- execution, which is timing-dependent on Hypercorn's disconnect detection. The test's contract is "B17 propagates client disconnect through the asyncio.Event surface to the handler's work loop". The right flag to assert on is ``handler_cancelled`` — set inside the loop when the handler observes ``cancellation_signal.is_set()``. Switched the assertion accordingly. ## Test sweep - Unit + Conformance + Contract + Integration + Interop: 1117/1117 GREEN (was 1113/1117 with 4 pre-existing baseline failures). - E2e durable + recovery (incl. durability_contract, recovery_contract, recovery_sample_18/19/20/21, durable_*, stream_recovery, cancellation_policy, shutdown_status): 133/133 GREEN, 3 skipped. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ACKEND entries; drop "Phase 5 follow-up" internal notes
Two cleanups for the unreleased 2.0.0b7 entry:
1. Document the two public additions landed in
``[agentserver] core: unified storage_paths + AGENTSERVER_TASKS_BACKEND override``
under ``Features Added``:
- Public ``azure.ai.agentserver.core.storage_paths`` module
- ``AGENTSERVER_TASKS_BACKEND`` operator override
2. Strip internal-iteration references ("Phase 5 final-cleanup
follow-up PR", "SOT spec §B3") from the ``ephemeral=`` /
``steerable=`` / ``ctx.suspend`` deprecation bullets. The
transitional warnings are sufficient to communicate intent
without pointing at internal phase scheduling.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rver-durable-agent-demo
…rver-responses-spec016
…dering (close GET race window) Two ordering tightenings on the non-bg streaming Phase-1 persistence- failure path, from rubber-duck review of the prior commit: 1. **``_register_bg_execution`` early-stamps the full storage-error snapshot**, not just ``status="failed"``. The prior commit set ``execution.status = "failed"`` but left ``execution.response`` as the initial ``response.created`` in_progress snapshot. A concurrent GET landing between that early stamp and the later non-bg ``_process_handler_events`` re-stamp (which installs the proper ``storage_error`` envelope) could observe ``status=failed`` with an inconsistent body. Fixed by installing the full ``_build_failed_response(error_code="storage_error", ...)`` snapshot AND the status together in the early-stamp block. 2. **``_process_handler_events`` non-bg-stream branch now appends the ``failed_normalized`` terminal to ``state.handler_events`` BEFORE emitting/yielding it**, and uses the existing ``_normalize_and_append`` helper for the build + validate + append step. The previous order was emit → yield → append, which left a window where a generator close immediately after the yield (e.g. ASGI cancellation on the client side) would leave the event list holding only ``response.created``; ``_finalize_stream`` Path B's snapshot reconstruction would then regress ``status="failed"`` back to ``status="in_progress"``. The validator also now sees the terminal event (the prior raw-append bypassed ``state.validator.validate_next``). The in-memory record's snapshot + status stamp is also moved BEFORE the wire emit/yield in the same branch, so a GET racing the post- yield finalize observes a consistent ``status=failed error.code=storage_error`` envelope regardless of which side wins the race. ## Test sweep - Unit + Conformance + Contract + Integration + Interop: 1117/1117 GREEN. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t storage lifetime (protocol 2.0.0) The resilient background/streaming response body outlives the inbound HTTP request and re-runs on cross-process crash-recovery. The x-agent-foundry-call-id captured on the originating CreateResponse call binds the response in Foundry storage for its ENTIRE lifetime, so every storage operation (create / update / checkpoint / terminal / recovery) MUST replay that SAME call_id. Previously the resilient path persisted only user_id_key and reconstructed the platform context with call_id=None (and a dispatch-time strip forced it to None), so post-request and recovered storage writes sent a missing/mismatched call_id and were rejected with "does not match an active turn" (hosted T2/T3/T15). Fix: capture ctx.call_id into ResilientResponseInput, persist it as durable task input (_K_CALL_ID), and reconstruct PlatformContext(user_id_key, call_id) via the single derivation site so the create-time call_id is replayed everywhere. Removed the dispatch-time call_id strip and the synthetic streaming ctx call_id=None. x-agent-user-id remains NOT forwarded to 1P storage (container-side partitioning only) — the provider still forwards call_id only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ntserver-durable-agent-demo # Conflicts: # sdk/agentserver/azure-ai-agentserver-responses/azure/ai/agentserver/responses/store/_foundry_provider.py
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace stale isolation-key references with the protocol 2.0.0 model: - handler-implementation-guide: ResponseContext.isolation (IsolationContext) -> platform_context (PlatformContext: user_id_key + call_id). - responses-resilience-spec: persisted-boundary table lists user_id_key + call_id (create-time call_id replayed on every storage call incl. recovery; user_id_key is per-user partition, never forwarded to storage) instead of user_isolation_key/chat_isolation_key. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ntserver-durable-agent-demo
…rver-responses-spec016
…nge #5) The conversation chain id (== the resilient task_id) now follows the system's IdGenerator convention instead of an opaque `resilient-resp-<32-hex>` wrapper: - conversation_id present -> cchain_{partition(conversation_id)}{scope} - steerable, no conv_id -> rchain_{partition(prev_resp or response)}{scope} - everything else (one-shot) -> the response_id verbatim The prefix acts as the discriminator (cchain/rchain/native caresp never collide); the embedded 18-char partition key co-locates the chain with its responses; a deterministic 32-char alnum `scope` digest of (agent_name \x1f session_id) fills the native entropy slot. agent_name (DNS <=63) and session_id (any <=128) are hashed (too long/arbitrary to embed); the constrained field is placed first so the pair encodes injectively. `task_id == conversation_chain_id` exactly (the `resilient-resp-` wrapper and the whole-composite truncated hash are gone). Deterministic + reconstructable on recovery (pure function of persisted inputs); stays within the Task API charset/limit. Rides Spec 038's no-back-compat decision — old-format tasks also lack payload.schema_version and are pruned by the recovery scan. SOT (responses-resilience-spec.md §4.1) updated. Chain-id unit + e2e tests updated to the native format. Responses suite green (1118 unit/ contract/conformance + 63 e2e; the 1 import-lint failure is pre-existing). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ntserver-durable-agent-demo
…ain ids) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
….2 + diagram + walkthrough) Update the remaining resilient-resp-<hash> task-id references to the native task_id == conversation_chain_id form (e.g. rchain_AB12...) in 4.2, the turn-1/turn-2 ASCII diagram, and the crash-recovery walkthrough. Completes the 4.1 derivation change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ntserver-durable-agent-demo
…rver-responses-spec016
… (Spec 038) Update the primitive-owned-payload list to payload.steering (core key renamed). The _responses / _framework namespaces keep their leading underscore (functional discriminators). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ntserver-durable-agent-demo
…rver-responses-spec016
…k API charset on derived task_id
Code-review Finding 2 (LOW): in case 3 (non-steerable, no conversation_id) the task_id is a verbatim response_id, which may be client-supplied via x-agent-response-id and is only structurally validated upstream (not charset-validated). The core primitive's _validate_task_id permits '.'/':', which the Public Task API charset ^[a-zA-Z0-9_-]{1,128}$ rejects. Replace the length-only assert (stripped under python -O; its comment over-promised charset protection) with a real ValueError enforcing strict charset+length. Add a paired test.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ntserver-durable-agent-demo
Prevents accidental commits of battery run logs/sessions (they contain endpoint URLs + session ids and are pure test output). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…es branch The resilient-responses-agent-demo hosted sample belongs on the demo branch; only its 3 gitignored build-context wheels were accidentally force-added here during an earlier merge. Remove them (the sample's own .gitignore marks this dir 'never committed — source of truth is sdk/agentserver/wheels/'; build.sh regenerates them at deploy). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The build-context wheels dir is gitignored ('never committed — source of truth is sdk/agentserver/wheels/') but 3 were force-added. build.sh regenerates them from the central wheels at deploy time.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ntserver-durable-agent-demo # Conflicts: # sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient-agent-demo/src/resilient-research-agent/wheels/azure_ai_agentserver_invocations-1.0.0b6-py3-none-any.whl # sdk/agentserver/wheels/azure_ai_agentserver_responses-1.0.0b8-py3-none-any.whl
…tion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t wheels
Same policy as the responses demo: the build-context wheels dir is gitignored ('never committed — source of truth is sdk/agentserver/wheels/') and regenerated by build.sh; 2 were force-added. Remove them for consistency.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rver-responses-spec016
…ntserver-durable-agent-demo
…inals; fix crash-failed persistence
The crash-failed recovery path (_persist_crash_failed) synthesized a bare-bones
failed ResponseObject with output=[] and no agent_reference, then wrote it via
update_response. The Foundry store validates agent_reference on every write and
rejected the marker, leaving durable responses stuck in_progress after a crash /
SIGTERM (spec §3.1 Termination paths B/C).
Root-cause fix + broader convergence (the handler owns the response object; the
framework may only set status, attach error on failed, or clear output on
cancel — per the SOT behaviour contract):
- Add shared terminal-overlay helpers to models/runtime.py
(apply_failed_terminal / apply_cancelled_terminal / resolve_failed_response /
resolve_cancelled_response): overlay onto the handler's persisted snapshot,
preserving agent_reference, model, output (failed "may be partial"),
metadata, conversation, instructions, tools, usage, sampling params, etc.;
clear completed_at; clear output + error on cancel (B11). Fall back to the
bare-bones builder only when no snapshot ever existed (crash before
response.created).
- Converge all ~15 terminal-construction sites in _orchestrator.py and
_endpoint_handler.py (storage_error, handler-exception, S-015, cancel
winddown, client-disconnect cancel, shutdown sweep, and the wire-event
synthesizers) onto the shared helpers so no site drops handler-owned fields.
- _persist_crash_failed: reuse the already-fetched snapshot and overlay when
present; synthesize (carrying agent_reference + model from the task input)
only when the response is confirmed absent; never clobber a progressed
snapshot on an unknown store-read error (confirmed-absent vs unknown, with a
create-only fallback).
- Error-payload hygiene: the crash/shutdown ResponseError is now {code, message}
only — dropped the internal `type` and `additionalInfo.shutdown_reason` so no
framework-internal lifecycle detail leaks to customers. shutdown_reason stays
internal (message selection + logs).
Updated SOT docs (responses-resilience-spec §7.2/§7.3 + diagram + C-SERVER-ERROR,
resilience-contract Path C, resilient-responses-developer-guide) and tests
(preserve-on-failed, cancel clears output, no leaked error fields). Adds
tests/unit/test_terminal_overlay.py.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ntserver-durable-agent-demo
…y + crash-failed fix
Rebuilds the checked-in azure-ai-agentserver-{core,invocations,responses}
wheels so the demo image ships the responses-branch changes (crash-failed
agent_reference persistence, terminal-overlay fidelity, error-payload
hygiene). Verified via full hosted battery on rapida-5196: 16/16 pass
(crash-recovery, cancel, steering, failed-terminal cases all green).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…a-5196) The battery/deploy target `-can-dev` (Foundry project rapida-5196, RG a-rapida-canada-central) was previously a local-only azd env that was lost on branch switches and never committed — while `.azure/config.json` already defaults to it. This tracks the env (endpoints, resource IDs, ACR connection acr-p5i746fm6qawc, model gpt-5.4-nano) so the deploy target survives across sessions and matches the battery's hardcoded rapida-5196. Contains only Azure resource identifiers/endpoints (no keys/secrets), consistent with the already-tracked resilient-responses-agent-demo env. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary — TEMPORARY / DO NOT MERGE
This is the
durable-agent-demosplit out of the original spec 016durability PR (#46997). It carries the azd-deployable hosted-agent
demo (34 files: bicep infra, .azure azd state,
src/durable-research-agentagent code, build/demo-client scripts).
Status
🚨 This PR is not intended for merge. The demo lives here purely so
it isn't lost from the working set; we use it temporarily as a
reference deployment while the durable-task primitive matures.
Scope
sdk/agentserver/azure-ai-agentserver-invocations/samples/durable-agent-demo/only (34 files). Plus whatever else came from the original split-point
branch — see the next section for cleanup needed.
What this branch needs before any potential reuse
durable-agent-demo/directory(everything else should be discarded by reverting to
origin/mainfor those paths, since the core+invocations work belongs in feat(agentserver): resilient task primitive + event streaming (core 2.0.0b7, invocations 1.0.0b6) #46997
and the responses work belongs in PR feat(agentserver): WIP - responses package durable orchestration (split out of #46997) #47275).
Pointers
samples/durable_research)derived from this demo is shipping in PR feat(agentserver): resilient task primitive + event streaming (core 2.0.0b7, invocations 1.0.0b6) #46997 — the demo here
remains as the fuller azd-deployable reference.