Skip to content

demo(agentserver): TEMPORARY - durable-agent-demo (split out of #46997, never-merged) - #47276

Draft
RaviPidaparthi wants to merge 692 commits into
mainfrom
feature/agentserver-durable-agent-demo
Draft

demo(agentserver): TEMPORARY - durable-agent-demo (split out of #46997, never-merged)#47276
RaviPidaparthi wants to merge 692 commits into
mainfrom
feature/agentserver-durable-agent-demo

Conversation

@RaviPidaparthi

Copy link
Copy Markdown
Member

Summary — TEMPORARY / DO NOT MERGE

This is the durable-agent-demo split out of the original spec 016
durability PR (#46997). It carries the azd-deployable hosted-agent
demo (34 files: bicep infra, .azure azd state, src/durable-research-agent
agent 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

Pointers

@github-actions github-actions Bot added the Hosted Agents sdk/agentserver/* label Jun 2, 2026
@RaviPidaparthi
RaviPidaparthi changed the base branch from main to feature/agentserver-durable-tasks June 2, 2026 03:33
@RaviPidaparthi
RaviPidaparthi force-pushed the feature/agentserver-durable-agent-demo branch from 756e0fe to 2553746 Compare June 2, 2026 22:50
RaviPidaparthi and others added 27 commits June 18, 2026 04:06
…te-serialization (spec 031)

Behavioral tests through real framework wiring (no mocks): pending_input_count
must reflect the live queued-steering count at the cancel boundary (SOT §13
ordering invariant), and a steer+drain must run the steered turn with no blind
PATCHes (SOT §25.1). The pending_input_count test lands RED (count==0, the
observed defect) ahead of the fix.

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

Conformance-restoration: the implementation had drifted from the SOT spec
(docs/task-and-streaming-spec.md). Fixes:

- pending_input_count (FR-001/002, SOT §12/§13): add settable _ActiveTask slot
  _pending_input_count; record it BEFORE ctx.cancel in the same-process enqueue
  and both cross-process steering polls (§13 ordering invariant); reset to the
  remaining backlog on drain. Previously read a never-written attribute -> 0.
- No blind writes (FR-005b, SOT §25.1): the queued-steering-cancel path used a
  bare provider.update with no If-Match (lost-update risk) -> now routes through
  the lock-held update primitive carrying the tracked etag.
- Lock-held update primitive (FR-005a): extract _provider_update_lock_held for
  callers that already hold the non-reentrant per-task lock; _provider_update_locked
  delegates to it.
- Drain read-inside-lock (FR-005, SOT §25.2): the steering drain now performs its
  record read + compute + write under one per-task lock hold (read was lock-free
  before, letting the lease heartbeat invalidate the pinned etag and starve the
  retry budget). Cross-process conflicts retry outside the lock.
- SOT reconciled: §12/§13 (pending_input_count is in-process observed state,
  failure-tolerant, set-before-cancel), §25.1 (no blind writes), §25.2
  (read-inside-lock rationale + two-helper split).

Validated: 646 durable + 746 durable+streaming green; responses consumer green
(2 pre-existing unrelated failures). Tests from prior commit now GREEN.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ry, provider parity

Completes the remaining spec-031 conformance work:
- Drain recovers from cross-process etag conflict (FR-006/SOT §25.3): the drain
  now also catches EtagConflict (consistent with the terminal-write + metadata
  paths) so a conflict landing on its write re-reads + retries instead of being
  abandoned by the caller's bare except. RED test reproduced the exact hosted
  'Failed to drain steering queue' failure, then GREEN.
- Metadata flush conflict retry (FR-006a/SOT §25.3): re-read + retry (≤5,
  last-write-wins on the namespace slot) instead of swallow-and-defer.
- Local/file-provider hosted-parity assertions (FR-008/009): stale-if_match ->
  hosted-identical etag_mismatch/412 classification; lease-only update bumps the
  etag; two providers over one store reproduce a deterministic cross-process
  conflict + recovery.
- Registered the new behavioral tests in the completeness meta-test (FR-016).
- FR-005c if_match-site audit recorded; FR-012 streams conformance confirmed
  already-comprehensive by audit; FR-018 guide already consistent.

Validated: 651 durable + 755 durable+streaming green; pylint unchanged (9.59/10).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…n bug (spec 031)

Hosted re-test of the invocations steering demo revealed the real drain failure
is NOT an etag race but a lease-renewal-on-suspended rejection: the multi-turn
turn writes status=suspended before the drain runs, and the hosted task store
rejects the drain PATCH's lease-renewal piggyback with 'lease renewal is only
supported for in_progress tasks'. The local provider was too permissive (allowed
renewal on {in_progress, suspended}), masking it. This commit tightens the local
provider to in_progress-only (hosted parity) + adds the drain status-transition
regression test, landing the bug RED.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…red turn runs (spec 031)

The drain promotes a queued steering input to a NEW turn, but the record was
left 'suspended' by the just-ended turn. The drain's PATCH now sets
status='in_progress' (a valid suspended->in_progress claim that the hosted store
accepts WITH lease params), instead of a bare lease renewal that the store
rejects on a suspended task. Validated on a deployed hosted agent (rapida-0687):
the steered turn now runs the new topic end-to-end; logs show 'Steering drain:
drained next input' with no 'Failed to drain'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Refresh the checked-in preview wheels so the demo's docker image ships the
pending_input_count + steering-drain (suspended->in_progress) fixes validated
on hosted (rapida-0687, s4 steering passes end-to-end).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…se renewal requires in_progress (spec 031)

Corrects §52 step 14 (drain Phase-1 PATCH MUST set status='in_progress') and
adds the §25.4 rule that the lease-extension trio is a renewal only on an
already-in_progress record, or a claim when the same PATCH flips to in_progress.
This is the doc-level root of the hosted 'Failed to drain steering queue' bug.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… (gpt-4o, env-driven)

The invocations demo committed its .azure env hardcoding the e2e-tests-westus2
project (broken ACR perms) and gpt-4.1-mini. Align it with the responses demo
which targets the working project (rapida-0687): gitignore .azure (env-driven,
no committed project pointer) and switch the model to gpt-4o in azure.yaml,
agent.yaml, agent.py, and README. Validated by deploying to rapida-0687 and
running an invocation end-to-end (clean run, gpt-4o streaming, no errors).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The default ENDPOINT was an unresolved <account>/<project> placeholder and the
script never read the azd env (despite the comment), so 'demo-client.sh start'
POSTed to a bogus host -> empty invocation_id -> immediate 'Stream disconnected'.
Now ensure_endpoint() auto-resolves ENDPOINT from
AGENT_DURABLE_RESEARCH_AGENT_INVOCATIONS_ENDPOINT (stripping the
/invocations?... tail) when the placeholder is still in place, and errors with
clear guidance if it cannot. API_VERSION stays 'v1' (verified working).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…now an offline alternative

The hosted task API no longer returns 403; the durable demos are validated
end-to-end on a hosted Foundry deployment. Reframe both durable-demo READMEs:
the azd-deployed hosted flow is the recommended path; the local file-backed kit
is an offline alternative (no longer 'recommended because hosted is broken').

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…depth gaps B1/B6/B7

- FR-001: harden the Principle XI depth gate from soft warnings.warn to a hard
  assert; broaden the detector to recognize the failed-row error idiom
  (terminal.get('error')/error.get('code')); add response.output content depth to
  the status-only completed Path-A per-cell tests (Row 1/2 Path A).
- B1 (FR-005): reset-event CONTENT — real-crash Row 11 test asserts the
  post-recovery response.in_progress reset event's response.output carries the
  corrected (seeded) items, not just that it exists.
- B6 (FR-009a): Path B graceful-handoff — assert the runtime exits gracefully
  (not the Path-C SIGKILL fallback), proving the graceful shutdown path ran.
- B7 (FR-009b): recovery precondition — a transient store error during the
  recovery pre-fetch MUST NOT drop; new fault-injecting store handler proves
  recovery proceeds (handler re-invoked, completes) and the transient fired.

All RED-first (gate failed on the 2 shape-only Path-A tests before depth added).

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

Spec 032 conformance audit follow-up:
- Add test_client_cancel_during_recovery.py (B3): real crash + real cancel
  endpoint during a recovered invocation settles to terminal cancelled.
- Reconcile CONTRACT_COVERAGE.md: correct stale GAP/TO-BE-ADDED markers to the
  tests that already close them; add a Spec 032 section mapping the new
  recovery-gap tests (B1 reset-event content, B3 client-cancel-during-recovery,
  B6 graceful-exit-not-SIGKILL, B7 transient-precondition-must-not-drop) and the
  closed-by-existing/consequence items (B4 seeding, B5 dedup, B8 created
  idempotency) to their contract clauses.
- black formatting on the Spec 032 test modules.

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

On hosted, the platform injects `agent_reference` as an AgentReference model
(a Mapping but not json.dumps-serializable). It leaked through
_split_runtime_refs into the persisted durable-task input, so
create_and_start -> _resolve_input_storage raised
`TypeError: Object of type AgentReference is not JSON serializable` and the
durable background start silently fell back to a non-durable
asyncio.create_task — meaning NO durable task was created and crash recovery
never happened on hosted.

_split_runtime_refs now normalizes a model-typed agent_reference to a plain
dict (consumers all accept AgentReference | dict and read it as a mapping; the
dict also survives cross-process recovery). Absent agent_reference stays the
{} sentinel.

This was invisible to the conformance suite because local/conformance requests
carry no agent_reference (-> {} sentinel -> serializable). Adds
TestSplitRuntimeRefsSerializable asserting the persisted durable input is
JSON-serializable when agent_reference is a model.

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

- responses wheel now carries the agent_reference durable-input
  serialization fix (durable background responses were silently degrading
  to non-durable asyncio.create_task on hosted).
- All three wheels rebuilt from clean trees: the previously checked-in
  wheels had bundled stale build/lib/** duplicates (core 612KB->157KB,
  invocations 722KB->20KB, responses 1.3MB->336KB) with no functional change
  beyond the responses fix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pin both durable demos to the shared rapida-0687 Foundry project so deploys
are reproducible:
- invocations durable-agent-demo (.azure/demo-dev)
- responses durable-responses-agent-demo (.azure/durable-responses-agent-demo)

Force-added past the .azure/.gitignore. Contains resource pointers
(subscription/resource-group/project/ACR/endpoint) only — no secrets; .env.lock
and state/ remain ignored.

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

The demo crash trigger (input='crash' -> os._exit(137)) did not check
context.is_recovery, so after a crash the durably re-delivered 'crash' input
re-fired the exit on every recovered lifetime — an infinite crash-loop for a
demo whose whole point is to crash ONCE and recover. Guard with
'not context.is_recovery' so the recovered run skips the crash and resumes to
completion. Also refreshes the azd env agent version pointer.

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

Adds a Path-C real-crash conformance test that carries an agent_reference on the
request (the hosted gateway-injected AgentReference model). It reproduces the
hosted 'AgentReference is not JSON serializable' durable-start failure LOCALLY
(durable start is provider-agnostic): without the _split_runtime_refs
normalization fix the model leaks into the durable-task input, durable start
falls back to a non-durable asyncio.create_task, the SIGKILL'd task is lost and
recovery never reaches completed (verified RED). With the fix it recovers.

Closes the gap that let the bug ship: every other durability test sent no
agent_reference ({} sentinel) or a plain string, so none exercised the model
form through durable-input serialization.

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

Reviewed all four standalone skills against the actual package surface and fixed
claims that produce broken code:

durable-task-skill:
- Remove the broken 'from ...durable import ... Suspended' (Suspended is an
  internal _Suspended sentinel, not exported -> ImportError).
- Fix local-provider env vars: the LocalFileTaskProvider roots at
  ${AGENTSERVER_DURABLE_ROOT:-~/.durable}/tasks/ and the force-local override is
  AGENTSERVER_TASKS_BACKEND=local (the doc's ~/.durable-tasks/ +
  AGENTSERVER_DURABLE_TASKS_PATH were wrong/legacy).

responses-skill:
- context.durable_metadata / DurableMetadataNamespace does not exist; the real
  handler metadata facade is context.conversation_chain_metadata
  (ConversationChainMetadataNamespace, has .flush()). Fixed surface table +
  decision shortcut.
- Import CreateResponse and ResponseEventStream from the public top-level
  azure.ai.agentserver.responses (not the private models._generated /
  streaming._event_stream modules).

streaming-skill:
- use_file_backed_replay takes storage_dir=, not root= (2 spots).

invocations-skill verified correct (invoke_handler/get_invocation_handler/
cancel_invocation_handler/ws_handler/request.state.session_id all real) — no change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
RaviPidaparthi and others added 29 commits July 15, 2026 04:20
…ec 041 A-docs)

Capture the composition learning surfaced while reworking the resilient langgraph
samples: on steer/recovery re-run from the last stable graph checkpoint (not the
latest tip), seed checkpoint_ns="" when forking through the sqlite checkpointer
(else KeyError), and use Command(resume=...) only when parked at an interrupt.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ec 041 PY-3)

The task/streaming identity rule (and tasks-guide.md, and the .NET port) specify
that a multi-turn turn gets its OWN unique input_id when the caller omits one —
but the source defaulted an omitted multi-turn input_id to the (fixed) chain
task_id, identical across turns. Source lagged the spec + its own docs.

MultiTurnTask.run/.start now mint a fresh per-turn id (input-<uuid4-hex>) when
input_id is omitted (and no If-Match precondition is in play, which requires an
explicit advancing id). The id flows through the existing plumbing: persisted as
the chain head (payload["last_input_id"], advancing every turn and surviving
recovery) and surfaced on TaskContext.input_id. One-shot tasks are unchanged
(input_id still defaults to task_id — the 1:1 invariant).

Tests: per-turn uniqueness + chain-head advance, surfaced on ctx.input_id,
caller-supplied id preserved, one-shot defaults to task_id. Updated the former
test_legacy_callers_unaffected (which pinned the old "slot not seeded" behavior)
to assert the new auto-generated seeding. Full core tasks suite: 697 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… terminal (Spec 041 PY-5)

verify_crash.py previously proved recovery primarily by scraping continuous
`azd ai agent monitor` server logs (worker-instance counts, reclaim/recovered
markers) — unreliable across a nanny restart (re-attaches to the pre-crash log
buffer, doesn't reliably follow the new container's stdout).

Now the verdict is DETERMINISTIC and log-independent: (a) confirmed hard crash
(the streaming connection dropped → sandbox died), (b) observed pre-crash
checkpointed progress, and (c) the response reaches `completed` AFTER the crash,
read via a NON-STREAMING GET snapshot poll (rs.poll_terminal — no stream-TTL
constraint, unlike the SSE reconnect which may have expired across the restart).
Continuous log capture + marker greps + the stream reconnect are retained as
SUPPLEMENTARY evidence only, and the script now exits non-zero when the
deterministic proof fails (usable as a battery gate).

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

Rebuild the checked-in core/invocations/responses preview wheels so the demo
container ships the Spec 041 library changes (PY-1/PY-2 file-backed replay,
PY-3 per-turn multi-turn input_id). Sample-only changes (A0/A1, checkpoint_ns
fixes) are not in the wheels. Built from a clean tree (no stray build/ dir).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…041 PY-6)

The responses demo battery never pinned agent_session_id, so each POST landed on
a DIFFERENT sandbox — a `crash` request killed an unrelated container, not the
one running the in-flight response. The run finished normally and merely *looked*
recovered, so the crash cases were not actually testing recovery. (agent_session_id
is a top-level create-response body property; one session id == one sandbox.)

run_suite.py:
- body() gains an agent_session_id param; new_session() mints demo-<uuid>.
- fire_crash(session) is now REQUIRED to pin the crash to the run's session;
  corrected its false "single sandbox" docstring; returns crash_session (echoed
  x-agent-session-id) + requested_session.
- case_crash_recovery / case_steering_crash pin start+steer+crash to one session
  and add crash_hit_target (crash_session == run session) as a REQUIRED part of
  the ok/recovery gate.

verify_crash.py:
- Pins the run + crash to a fresh session; crash_hit_target is now a required
  fact of recovery_proven (alongside crash_confirmed + pre-crash progress +
  authoritative completed terminal); surfaced in the verdict + failure message.
  (The latent session_id->crash_session key bug was already fixed in PY-5.)

demo-client.sh:
- Persist AGENT_SESSION_ID in .demo-session; mint_session_id() uses uuidgen with
  a python3 fallback; start mints a fresh session, steer/crash reuse it, all
  three send agent_session_id in the body so they hit the same sandbox.

verify_crash_steer.py + the __ECHO_CRASH__ self-crash proofs were already sound
(they route the crash onto the target's own task) and are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ash-confirmation (Spec 041 PY-6 follow-up)

The session-affinity fix (PY-6) made crashes actually hit the run's sandbox, which
exposed two battery MEASUREMENT bugs (the run recovered, but the battery mis-read it):

- case_crash_recovery(stream=True) read the terminal via an SSE reconnect, whose
  TTL expires across the ~150s restart window -> terminal=None (false FAIL). Now
  BOTH stream and non-stream crash cases read the authoritative terminal via a
  non-streaming GET poll (like the poll variant + verify_crash); the SSE reconnect
  is kept as supplementary stream-continuity evidence.
- verify_crash.py required the connection-drop signal (sandbox_dropped) to confirm
  the crash, but the handler flushes a terminal SSE before os._exit so the client
  often sees a clean close (drop=False) even though the sandbox genuinely died +
  restarted. crash_confirmed now accepts EITHER a dropped connection OR restart
  evidence (new worker instance / reclaim / recovered markers).

Verified live: crash recovery PROVEN (crash_hit_target=True, 2 worker instances,
reclaim+recovered markers, terminal=completed) for verify_crash + T3/T8; T1/T2/T4/
T6/T7/T16 pass. Known separate finding (pre-existing, newly exposed, NOT a
regression): T5 steer x crash leaves the steered turn in_progress after the
original recovers to completed — orchestrator steering-queue drain after a real
crash needs follow-up.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…covery, timestamps (Spec 041 PY-7/8/9)

Responses demo-client.sh fixes for the interactive (cross-terminal) demo flow —
invisible to the battery (which posts in-process and reads the session id from the
response header directly), which is why PY-6's battery verification passed while
the interactive demo still failed for users.

PY-7 (cross-terminal crash/steer targeting):
- Publish the in-flight response id to a shared .demo-session.active the instant
  it is captured mid-stream; load_session falls back to it — so a command in
  another terminal (steer/cancel/stream) can target the run that is currently
  streaming (the session file only got RESPONSE_ID when the stream ended).
- SSE sidecar temp files are now PID-private (.$$) so a concurrent invocation
  can't rm/clobber a streaming command's id/seq files.
- The interactive crash stays a BARE agent_session_id-pinned streaming crash
  (never a previous_response_id steer, which would only run AFTER the current
  turn and never interrupt it).

PY-8 (endpoint self-discovery + fail-fast):
- _azd_responses_endpoint resolves AGENT_*_RESPONSES_ENDPOINT from `azd env
  get-values` (fallback: ./.azure/<defaultEnvironment>/.env), strips the trailing
  /responses?api-version=… to the protocol base, and lifts api-version.
- An exported ENDPOINT is honored only when it is a real (non-placeholder) URL.
- _require_endpoint (invoked from ensure_token, so every network command is
  guarded) aborts with actionable guidance if the value is still a placeholder,
  so a mis-run crash can never be silently fired at a bogus host. Fixed the
  placeholder default path to …/protocols/openai.

PY-9 (client/server log correlation):
- _now_iso() (UTC ISO-8601, matching the server log clock); a "▶ triggered @"
  banner and an EXIT-trap "⏹ ended @ (elapsed Ns, exit rc)" banner to stderr
  (so they never pollute SSE stdout); the crash line is timestamped.

Verified: bash -n clean; `status` with ENDPOINT unset auto-resolves the real
…/protocols/openai endpoint (api-version=v1) and makes a live call (no
placeholder no-op); banners print with UTC timestamps + elapsed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…client (Spec 041 PY-9 parity)

The invocations resilient-agent-demo demo-client already had endpoint
self-discovery + placeholder guard (PY-8) and session pinning (PY-7); it only
lacked client-side timestamps. Add _now_iso() + the "▶ triggered @" /
"⏹ ended @ (elapsed Ns, exit rc)" stderr banners so its timeline can be lined up
against the server log, matching the responses demo-client.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eleased on PyPI)

Merged origin/main into the core branch; main had dated+released core 2.0.0b7,
invocations 1.0.0b6, responses 1.0.0b8 (all on PyPI 2026-06-28). Bump each to the
next beta so the unreleased work ships without colliding with a released version:

- core       2.0.0b7 -> 2.0.0b8
- invocations 1.0.0b6 -> 1.0.0b7
- responses  1.0.0b8 -> 1.0.0b9

Changelog hygiene: the merge collapsed our stale "## X (Unreleased)" headers
(which reused the now-released version numbers) — restored main's released dated
sections (core b7, invocations b6) below the new (Unreleased) sections, and moved
the genuinely-new work under the bumped versions (de-duplicated the bullets that
had already shipped in the released version). Added a responses b9 (Unreleased)
section (samples/docs rework; no library API change).

Bumped the invocations + responses minimum core dependency pin to >=2.0.0b8
(the release carrying the PY-1/PY-2 file-backed-replay parity and PY-3 per-turn
input_id fixes).

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

- _orchestrator.py: kept responses resilience version (superset of main's
  container-protocol/PlatformContext work + resilient-task refactor)
- _endpoint_handler.py: union of SSE symbols (encode_sse_any_event, _encode_sse,
  with_keep_alive)
- CHANGELOG.md: b9 (Unreleased) resilience work over main's dated b8/b7/b6 releases
- responses -> 1.0.0b9; core-dep pin >=2.0.0b8

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

Promotes the shared-state-root subdir resolver to the public
`azure.ai.agentserver.core` API (import + __all__ + docstring) so
protocol packages (e.g. the responses file store) can locate the
AGENTSERVER_STATE_ROOT subdirectories without reaching into the private
_config module (Spec 033 FR-007).

Note: api.md regeneration is deferred — the agentserver api.md files are
pre-existingly stale and core's cannot be regenerated with the pinned
apistub 0.3.28 (crashes on TaskMetadata, a collections.abc.MutableMapping
subclass). Tracked for the eventual PR-to-main.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…(FR-007)

- FileResponseStore.storage_dir is now optional and defaults to the public
  core resolve_state_subdir("responses"), mirroring how the core task/stream
  layers default their own dirs.
- _routing.py no longer reaches into the private core._config: the stream
  registry already defaults storage_dir internally (redundant arg dropped),
  and the response-store default now comes from FileResponseStore itself.

Clears the Spec 033 import-lint gate (test_spec033_import_lint) without any
behavior change to the resolved on-disk paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… b7, responses b9)

Refreshes the checked-in demo wheels after the origin/main merge + version
bumps and the resolve_state_subdir public-surface change.

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

- verify_crash_steer.py: retired. It used steering (previous_response_id) only
  as a crash-DELIVERY mechanism to land the crash on the lease-holding
  container — obsolete since PY-6: a bare 'crash' pinned via agent_session_id
  already lands on the same sandbox (verify_crash.py proves recovery). Steering
  resilience is now covered by a dedicated deterministic e2e test.
- Delete the stale b7/b6/b8 wheels the previous wheel-refresh commit left behind
  (git add wheels/*.whl staged the new wheels but not the old-file deletions),
  so the demo image no longer bundles duplicate/conflicting package versions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Deterministic CI test for steering-feature resilience: a steerable resilient
conversation runs turn 1, the client steers turn 2 (previous_response_id), and
a SIGKILL lands while the steered turn is mid-flight (core drain_in_progress).
Asserts both the superseded turn AND the steered turn recover to 'completed'
(steered turn carries the recovered L1 lifetime + the steered input), proving a
steered turn is not orphaned in_progress after a crash.

Uses the CrashHarness (real SIGKILL + restart) with a new deterministic
steerable handler (_steering_handler.py). Replaces the retired hosted battery
verify_crash_steer.py; reclassifies finding-t5 as a test-artifact (steering the
literal 'crash' sentinel) rather than a framework bug. Registered in
CONTRACT_COVERAGE.md.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The apiview-stub-generator (apistub 0.3.28) namespace detector mis-parses a
multi-line module docstring in a package __init__ — a closing triple-quote on
its own line leaves its parser stuck in 'docstring' mode, so the package
namespace resolves to '' and the 'azure-package-only' member filter is defeated
(startswith('') is always true). Effects:
- core: the public TaskMetadata (a collections.abc.MutableMapping subclass)
  leaked its inherited __class_getitem__ (= C-level types.GenericAlias) past the
  filter, crashing apistub (getsource on a C type) — core api.md was ungenerable
  and stale (missing the entire tasks/ + streaming/ public API).
- invocations: no crash, but inherited Starlette framework methods
  (add_middleware/add_route/__call__/...) leaked into api.md as if they were the
  package's own API (71 lines of bloat).

Fix: collapse both module docstrings to a single line (matches the sibling
responses package + repo convention e.g. azure-appconfiguration). Namespace now
resolves correctly, so:
- core api.md regenerates cleanly with core/streaming/tasks namespaces,
  TaskMetadata, and resolve_state_subdir (+418 lines).
- invocations api.md drops the leaked Starlette methods (-71 lines).

Regenerated api.md + api.metadata.yml for both (parserVersion 0.3.28).

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

Refresh the responses api.md + api.metadata.yml to the current public surface
(namespace detection already worked here — responses uses a single-line module
docstring). Captures the resilience work accumulated since the last regen:
the new store namespace (FileResponseStore, now with optional storage_dir=None),
plus hosting/streaming/models additions. parserVersion 0.3.28.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b25bbfa5-424a-4e99-a4b0-0b0154ed699d

# Conflicts:
#	sdk/agentserver/azure-ai-agentserver-core/CHANGELOG.md
#	sdk/agentserver/azure-ai-agentserver-core/api.md
#	sdk/agentserver/azure-ai-agentserver-core/api.metadata.yml
#	sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/__init__.py
#	sdk/agentserver/azure-ai-agentserver-core/pyproject.toml
#	sdk/agentserver/azure-ai-agentserver-invocations/CHANGELOG.md
#	sdk/agentserver/azure-ai-agentserver-invocations/api.metadata.yml
#	sdk/agentserver/azure-ai-agentserver-invocations/pyproject.toml
…ble-agent-demo

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b25bbfa5-424a-4e99-a4b0-0b0154ed699d

# Conflicts:
#	sdk/agentserver/azure-ai-agentserver-responses/api.metadata.yml
#	sdk/agentserver/azure-ai-agentserver-responses/tests/conftest.py
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b25bbfa5-424a-4e99-a4b0-0b0154ed699d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b25bbfa5-424a-4e99-a4b0-0b0154ed699d
Base automatically changed from feature/agentserver-durable-tasks to main July 28, 2026 05:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Hosted Agents sdk/agentserver/*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants