Skip to content

[agentserver] Enable resilient tasks in invocations LRA samples + add minimal hello samples - #48878

Open
Nathandrake229 wants to merge 17 commits into
mainfrom
users/naman/enable-resilient-tasks-invocation-samples
Open

[agentserver] Enable resilient tasks in invocations LRA samples + add minimal hello samples#48878
Nathandrake229 wants to merge 17 commits into
mainfrom
users/naman/enable-resilient-tasks-invocation-samples

Conversation

@Nathandrake229

Copy link
Copy Markdown
Member

What & why

The durable-task subsystem is strictly opt-in as of azure-ai-agentserver-core 2.2.0b1: AgentServerHost only constructs the TaskManager (and runs the crash-recovery scan) when set_resilient_tasks_enabled(True) is called before host startup. With the switch off, get_task_manager() / <task>.start() raise TaskManagerNotInitialized and durable execution + crash recovery are inactive.

The resilient invocations samples declare durable tasks (@task / @multi_turn_task) but never enable the subsystem, and there is no host auto-enable for invocations. As shipped, they would raise TaskManagerNotInitialized and would not actually run as long-running/crash-recoverable agents.

Changes

Enable the subsystem in the existing resilient samples (one line, at import time, before app = InvocationAgentServerHost()):

  • resilient_research/app.py
  • resilient_multiturn/app.py
  • resilient_langgraph/app.py
from azure.ai.agentserver.core.tasks import set_resilient_tasks_enabled
set_resilient_tasks_enabled(True)

Add two minimal LRA samples that depend on only azure-ai-agentserver-core + azure-ai-agentserver-invocations (no LLM, no azure-ai-projects, no langgraph) — a low-footprint on-ramp:

  • resilient_hello_world — smallest start-and-poll LRA: a durable @task that counts N steps, checkpointing to a state store after each, and resumes on entry_mode == "recovered".
  • resilient_hello_forever — minimal indefinite durable worker: a while True loop with per-iteration checkpointing, graceful-shutdown handling (ctx.shutdownexit_for_recovery()), and a cancel path.

Verification (local, against core 2.2.0b1)

  • All five resilient invocations samples import with the durable subsystem enabled.
  • resilient_hello_world and resilient_hello_forever: resume from their checkpoint after a hard kill (recovery re-enters with entry_mode == "recovered" and continues past the crash point rather than restarting).
  • resilient_hello_forever: stops on cancel.
  • resilient_research: streams end-to-end (token/stage events) once enabled.

Notes

  • No package/shipped-code changes — samples only; no CHANGELOG entry needed.
  • For contrast, the responses resilient samples already call set_resilient_tasks_enabled(True); this brings the invocations samples in line.

…les + add minimal hello samples

The durable-task subsystem is strictly opt-in as of azure-ai-agentserver-core
2.2.0b1: AgentServerHost only constructs the TaskManager when
set_resilient_tasks_enabled(True) is called before host startup. Without it,
<task>.start() raises TaskManagerNotInitialized and there is no crash recovery.

The resilient invocations samples declared durable tasks but never enabled the
subsystem, so as shipped they would not run as long-running agents.

Changes:
- resilient_research / resilient_multiturn / resilient_langgraph: call
  set_resilient_tasks_enabled(True) at import time (before host startup).
- Add resilient_hello_world: the smallest start-and-poll LRA (core + invocations
  only, no LLM), demonstrating durable checkpointing + crash recovery.
- Add resilient_hello_forever: a minimal indefinite (while True) durable worker
  with graceful-shutdown handling and cancel.

Verified locally against core 2.2.0b1: all five samples import with the durable
subsystem enabled; hello_world and hello_forever resume from their checkpoint
after a hard process kill; hello_forever stops on cancel; resilient_research
streams end-to-end.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8abde5e2-e955-42bc-957b-7bf6877cd753
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
10 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@Nathandrake229
Nathandrake229 enabled auto-merge (squash) September 3, 2026 06:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Invocation identity, polling, post-timeout cancellation, and lifecycle test coverage have unresolved moderate issues.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Enables resilient task hosting in existing invocation samples and adds minimal finite and indefinite durable-worker examples.

Changes:

  • Enables resilient tasks before host startup in three samples.
  • Adds checkpointed start/poll and start/poll/cancel examples.
  • Documents recovery behavior and minimal dependencies.
File summaries
File Description
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_research/app.py Enables resilient tasks.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_multiturn/app.py Enables resilient tasks.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_langgraph/app.py Enables resilient tasks.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/requirements.txt Declares minimal dependencies.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md Documents finite resilient execution.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py Implements start and polling handlers.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/agent.py Implements checkpointed finite work.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/__init__.py Initializes the sample package.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/requirements.txt Declares minimal dependencies.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/README.md Documents indefinite resilient execution.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py Implements start, polling, and cancellation handlers.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/agent.py Implements the checkpointed worker.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/__init__.py Initializes the sample package.
Review details

Suppressed comments (12)

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/README.md:26

  • This timeout description does not match the task implementation or the cooperative watchdog contract. Expiry only sets ctx.timeout_exceeded and ctx.cancel; the loop deliberately continues and therefore is not re-entered. Reserve recovery here for process crashes and graceful redeploys.
It also sets `timeout=timedelta(days=7)` (the maximum per-turn budget), because
each *turn* is watchdog-bounded (default 1 day). When any interruption occurs —
crash, redeploy, or turn-budget expiry — the task is re-entered with
`ctx.entry_mode == "recovered"` and resumes from its checkpointed iteration.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/README.md:61

  • Strict opt-in was introduced in core 2.1.0b1 and is present in stable 2.1.0, not first in 2.2.0b1. Update this version reference.
As of `azure-ai-agentserver-core` **2.2.0b1** the durable-task subsystem is
**strictly opt-in**. Before host startup:

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/agent.py:85

  • This description of timeout behavior is incorrect. The core watchdog is cooperative-only: it sets timeout_exceeded and cancel but neither interrupts the handler nor releases its lease, so this branch simply ignores the signal and keeps executing; no recovered re-entry occurs at seven days. Update this comment and the README/module documentation to state that timeout is intentionally ignored, or implement a supported turn-boundary strategy.
            #    A cancel sets ``ctx.cancel`` without ``timeout_exceeded``; the
            #    per-turn watchdog also sets ``ctx.cancel`` but WITH
            #    ``timeout_exceeded`` — in that case we do NOT stop, so the
            #    framework re-enters (recovered) and the loop keeps going.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:50

  • The core CHANGELOG places strict opt-in in 2.1.0b1, and stable 2.1.0 includes it. Correct the stated version boundary.
# Resilient tasks (durable execution + crash recovery) are strictly opt-in as of
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` builds the

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:49

  • The implementation does not use task metadata or call ctx.metadata.flush(); it writes each checkpoint with FoundryStateStore.set_item(). As written, this explanation directs readers to an API that is absent from the sample.
2. The task loops, sleeping between steps and calling `ctx.metadata.flush()`
   after each — a **durable checkpoint**.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:73

  • Recovery reads completed_steps from the explicit FoundryStateStore, not from ctx.metadata. Keep the recovery walkthrough aligned with agent.py so users know where their checkpoint belongs.
   reads `completed_steps` from `ctx.metadata`, and continues from the next

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:75

  • The checkpoint remains after task completion, so the polling handler returns completed rather than 404. This second recovery walkthrough should not promise a response that the code cannot produce.
   `completed_steps` climb past where it crashed (then `404` when it finishes).

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:56

  • This behavior dates to core 2.1.0b1 and is already in stable 2.1.0, per the core CHANGELOG. Replace 2.2.0b1 so the sample states the correct minimum behavior boundary.
As of `azure-ai-agentserver-core` **2.2.0b1** the durable-task subsystem is
**strictly opt-in**. `AgentServerHost` builds the `TaskManager` (and runs the

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py:47

  • Core's CHANGELOG records this opt-in change under 2.1.0b1, and stable 2.1.0 already contains it. Correct the version boundary here.
# Resilient tasks (durable execution + crash recovery) are strictly opt-in as of
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` builds the

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_langgraph/app.py:86

  • The strict opt-in behavior was introduced in core 2.1.0b1 and is included in stable 2.1.0, not first introduced in 2.2.0b1. Correct this compatibility statement.
# Resilient tasks (durable execution + crash recovery) are strictly opt-in as of
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` constructs the

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_multiturn/app.py:49

  • The strict opt-in behavior was introduced in core 2.1.0b1 and is present in stable 2.1.0, according to the core CHANGELOG. Replace 2.2.0b1 here to avoid documenting the wrong compatibility boundary.
# Resilient tasks (durable execution + crash recovery) are strictly opt-in as of
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` constructs the

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_research/app.py:104

  • The core CHANGELOG places the strict opt-in breaking change in 2.1.0b1 (and it is included in stable 2.1.0), not 2.2.0b1. Correct the version so users do not infer that this behavior requires an unreleased 2.2 build.
# Resilient tasks (durable execution + crash recovery) are strictly opt-in as of
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` constructs the
  • Files reviewed: 11/13 changed files
  • Comments generated: 7
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

- Key both samples by the platform invocation id (request.state.invocation_id)
  instead of a sample-only ?task_id= query param, so standard clients poll/cancel
  via GET/POST /invocations/{invocation_id}. Updated docstrings and READMEs.
- hello_world GET: when no checkpoint exists yet, consult get_active_run() and
  return in_progress/0 while the run is active instead of an immediate 404 right
  after POST.
- hello_world README: the durable checkpoint persists after the ephemeral task
  record is cleaned up, so a completed run polls as "completed" (removed the
  incorrect 404-on-completion contract and a stale ctx.metadata reference).
- hello_forever cancel robustness: replace the timeout_exceeded-based stop
  condition (which could not stop the worker after the per-turn watchdog fired,
  and where the cancel path does not set cancel_requested) with a durable stop
  marker written by the cancel endpoint and checked by the worker — robust across
  the per-turn timeout and across recovery.

Re-verified locally against core 2.2.0b1: hello_world polls in_progress
immediately after POST and ends completed; hello_forever stops on cancel.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8abde5e2-e955-42bc-957b-7bf6877cd753
Copilot AI review requested due to automatic review settings September 3, 2026 07:08
@Nathandrake229

Copy link
Copy Markdown
Member Author

Thanks for the review — pushed d88c1b9 addressing the inline comments:

Invocation-id keying (app.py, both samples) — both now key the durable task by request.state.invocation_id and address it via GET/POST /invocations/{invocation_id}, dropping the sample-only ?task_id= query param. Docstrings, READMEs, and curl examples updated. Verified a standard client can POST, read the returned invocation_id, and poll /invocations/{invocation_id}.

hello_world 404 right after POSThandle_get now consults get_active_run() when no checkpoint exists yet and returns in_progress with completed_steps: 0 while the run is active. Verified: the immediate GET after POST returns in_progress/0, not 404.

hello_world README 404-on-completion — corrected. The durable checkpoint persists after the ephemeral @task record is cleaned up, so a finished run polls as completed. Removed the wrong 404 contract and a stale ctx.metadata reference.

hello_forever cancel after the timeout watchdog — good catch. The cancel path (get_active_run().cancel()) sets the cancel event but not cancel_requested, and after the per-turn watchdog fires timeout_exceeded stays true, so neither boolean reliably identifies an explicit stop. Replaced the boolean check with a durable stop marker written by the cancel endpoint (separate key, no ETag race with the checkpoint) and checked by the worker — robust across the per-turn timeout and across recovery. Verified: cancel transitions the worker to stopped.

Automated coverage (agent.py comments) — these two samples are currently validated manually against core 2.2.0b1 (start → checkpoint poll → hard-kill recovery → completion for hello_world; start → cancel → stop, and crash → resume for hello_forever). Adding contract/E2E coverage parallel to the existing resilient samples is a reasonable follow-up; happy to add it in this PR if you'd prefer it here rather than a follow-up.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

One or more issues must be addressed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (11)

Previously missed (3) — in code that hasn't changed since the last review.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:67

  • A syntactically valid JSON scalar or array reaches data.get and raises AttributeError, producing a 500 response. Reject non-object bodies with a 400 response before reading name.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py:66
  • A valid non-object JSON body makes data.get raise, a nonnumeric steps value makes int raise, and steps <= 0 completes without ever writing a checkpoint so subsequent polling returns 404. Validate that the body is an object and that steps is positive, returning a client error instead of a server error or an unpollable run.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/agent.py:21
  • This claims turn-budget expiry re-enters the task, but the watchdog is cooperative and this handler keeps looping after ctx.timeout_exceeded is set. Correct the module documentation so it does not promise recovery behavior the implementation cannot provide.

This issue also appears on line 95 of the same file.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/README.md:26

  • The timeout watchdog is cooperative: it only sets ctx.timeout_exceeded and ctx.cancel; it never interrupts or re-enters the handler. This loop deliberately ignores a timeout without a stop marker, so the documented turn-budget-expiry recovery does not occur. Describe the actual behavior instead.
It also sets `timeout=timedelta(days=7)` (the maximum per-turn budget), because
each *turn* is watchdog-bounded (default 1 day). When any interruption occurs —
crash, redeploy, or turn-budget expiry — the task is re-entered with
`ctx.entry_mode == "recovered"` and resumes from its checkpointed iteration.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/agent.py:98

  • The durable stop marker is only read after this process-local ctx.cancel event is set. If cancellation reaches another replica (where get_active_run() returns None) or the process dies after writing the marker but before signaling the run, recovery starts with a fresh unset event and this worker ignores the persisted stop forever. Check the marker independently on every iteration.
            if ctx.cancel.is_set():
                if await store.get_item(stop_key) is not None:
                    logger.info("stop requested — stopping at iteration %d", n)
                    return {"name": name, "iterations": n, "stopped": True}

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/agent.py:61

  • Existing resilient invocation samples have structural and end-to-end tests, but this new infinite worker has no automated recovery or cancellation coverage. Add it to the structural gate and test both crash resume and cancellation, especially cancellation from a process that does not own the active run.
@task(name="hello_forever", timeout=timedelta(days=7))
async def hello_forever(ctx: TaskContext[dict]) -> dict[str, Any]:

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:73

  • Ctrl-C initiates graceful host shutdown, so this procedure does not exercise the hard-crash recovery path described by the sample and PR. Use an actual hard kill so the verification matches the claimed scenario.
2. Poll until `completed_steps` is a few in, then **kill the process** (Ctrl-C).

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/agent.py:45

  • The repository has a structural gate plus end-to-end recovery tests for every existing resilient invocation sample, but this new sample is absent from both. Add it to test_resilient_samples_structure.py and cover checkpoint continuation through the crash harness so the core behavior demonstrated here cannot regress.
@task(name="hello_world")
async def hello_world(ctx: TaskContext[dict]) -> dict[str, Any]:

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_langgraph/app.py:91

  • These statements are contradictory: when langgraph_session.start() raises TaskManagerNotInitialized, the agent does not continue in a non-durable mode. Clarify that execution cannot start and crash recovery is unavailable.
# startup (i.e. at module-import time). Without it, ``get_task_manager()`` /
# ``langgraph_session.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_multiturn/app.py:54

  • These statements are contradictory: when session_workflow.start() raises TaskManagerNotInitialized, the agent does not continue in a non-durable mode. Clarify that execution cannot start and crash recovery is unavailable.
# startup (i.e. at module-import time). Without it, ``get_task_manager()`` /
# ``session_workflow.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_research/app.py:109

  • These statements are contradictory: when deep_research.start() raises TaskManagerNotInitialized, the agent does not continue in a non-durable mode. Clarify that execution cannot start and crash recovery is unavailable.
# startup (i.e. at module-import time). Without it, ``get_task_manager()`` /
# ``deep_research.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.
  • Files reviewed: 11/13 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Extend the invocations sample test suite to cover the two new minimal
long-running-agent samples and guard the opt-in flag this PR adds.

Structure tests (test_resilient_samples_structure.py):
- Register resilient_hello_world and resilient_hello_forever: directory
  existence, required files (agent.py, app.py, requirements.txt, README.md),
  and no-retired-name references.
- Add a regression gate asserting every resilient sample's app.py calls
  set_resilient_tasks_enabled(True). Durable tasks are strictly opt-in since
  core 2.1.0b1; without the call get_task_manager() raises
  TaskManagerNotInitialized and recovery is silently disabled.

E2E tests (in-process, no LLM / cloud, cross-platform):
- test_resilient_hello_world.py: run-to-completion checkpoints every step;
  a fully-completed checkpoint skips all work (unchanged ETag); a partial
  checkpoint resumes at the next step.
- test_resilient_hello_forever.py: the worker ticks and checkpoints, then an
  explicit cancel plus durable stop marker stops it terminally; a pre-existing
  checkpoint makes it resume past the seeded cursor instead of restarting.

These mirror the existing test_resilient_multiturn.py in-process pattern
(real TaskManager over the local file provider at tmp_path) and touch only
tests/ — no shipped SDK source changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8abde5e2-e955-42bc-957b-7bf6877cd753
Copilot AI review requested due to automatic review settings September 3, 2026 07:22
Addresses PR review: the GET handler reported `stopped` whenever
`get_active_run()` returned None on the polled replica. For an indefinite
worker that keeps running (possibly on a different replica) until explicitly
cancelled, process-local ownership is not a reliable terminal signal. Derive
status from the durable stop marker instead, so every replica agrees: present
=> stopped, absent => running. 404 only when neither checkpoint nor marker
exists.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8abde5e2-e955-42bc-957b-7bf6877cd753

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved moderate issues affect durable cancellation, input validation, dependency compatibility, and regression coverage.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (15)

Previously missed (13) — in code that hasn't changed since the last review.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/agent.py:101

  • The durable marker is only read while this process's in-memory cancel event is set. If the process crashes after the endpoint writes the marker but before run.cancel() is delivered, recovery creates a fresh context with ctx.cancel unset and the worker ignores the persisted stop forever. Check the marker independently on every iteration so it actually survives crash/recovery as advertised.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:67
  • A syntactically valid JSON body can be a list, string, or null, in which case data.get(...) raises AttributeError and the sample returns a 500. Validate that the decoded body is an object and return a 400 for unsupported shapes.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/requirements.txt:8
  • These unbounded requirements can silently resolve to incompatible future major versions. The package itself constrains core to >=2.1.0,<3.0.0 (pyproject.toml:24), and shipped invocation samples constrain invocations to <2.0.0 (for example samples/simple_invoke_agent/requirements.txt:1); apply the same compatibility bounds here.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py:66
  • steps accepts zero or negative values, so the task completes without ever writing a checkpoint. Because one-shot task records are deleted on completion, the documented poll endpoint then returns 404 for a run that successfully returned 202. Reject non-positive (and non-object/non-integer) input with 400 before starting the task.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/requirements.txt:8
  • These unbounded requirements can silently resolve to incompatible future major versions. The package itself constrains core to >=2.1.0,<3.0.0 (pyproject.toml:24), and shipped invocation samples constrain invocations to <2.0.0 (for example samples/simple_invoke_agent/requirements.txt:1); apply the same compatibility bounds here.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/README.md:26
  • The timeout watchdog does not interrupt or re-enter this handler. It only sets cooperative context flags, which the current loop ignores when no stop marker exists, so turn-budget expiry does not cause the recovery described here.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/README.md:60
  • The opt-in behavior was introduced in core 2.1.0b1, not 2.2.0b1 (see azure-ai-agentserver-core/CHANGELOG.md:28-44). The README should identify the first affected release correctly.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/agent.py:21
  • Core does not interrupt or re-enter a task when this timeout expires; its watchdog only sets cooperative flags, and this loop continues when no stop marker exists. Rewrite this explanation unless the handler is changed to implement an actual rollover path.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:50
  • The opt-in behavior was introduced in core 2.1.0b1, not 2.2.0b1 (see azure-ai-agentserver-core/CHANGELOG.md:28-44). Keeping the wrong version here makes the sample's compatibility guidance inaccurate.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:58
  • The opt-in behavior was introduced in core 2.1.0b1, not 2.2.0b1 (see azure-ai-agentserver-core/CHANGELOG.md:28-44). The README should identify the first affected release correctly.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py:48
  • The opt-in behavior was introduced in core 2.1.0b1, not 2.2.0b1 (see azure-ai-agentserver-core/CHANGELOG.md:28-44). Keeping the wrong version here makes the sample's compatibility guidance inaccurate.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_multiturn/app.py:49
  • The opt-in behavior was introduced in core 2.1.0b1, not 2.2.0b1 (see azure-ai-agentserver-core/CHANGELOG.md:28-44). Keeping the wrong version here misleads users about which released core versions require this call.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_research/app.py:104
  • The opt-in behavior was introduced in core 2.1.0b1, not 2.2.0b1 (see azure-ai-agentserver-core/CHANGELOG.md:28-44). Keeping the wrong version here misleads users about which released core versions require this call.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_langgraph/app.py:86

  • The opt-in behavior was introduced in core 2.1.0b1, not 2.2.0b1 (see azure-ai-agentserver-core/CHANGELOG.md:28-44). Keeping the wrong version here misleads users about which released core versions require this call.
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` constructs the

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_langgraph/app.py:89

  • Core's CHANGELOG records the strict opt-in under 2.1.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28-44), not 2.2.0b1. Also, without the switch langgraph_session.start() raises before work begins; it does not run non-durably. Correct both claims so the sample accurately identifies affected users and failure behavior.
# Resilient tasks (durable execution + crash recovery) are strictly opt-in as of
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` constructs the
# ``TaskManager`` ONLY when this switch is on, and it must be set before host
# startup (i.e. at module-import time). Without it, ``get_task_manager()`` /
# ``langgraph_session.start()`` raise ``TaskManagerNotInitialized`` and the agent
  • Files reviewed: 14/16 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings September 3, 2026 07:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A critical cross-replica cancellation defect and unresolved request-validation and dependency issues remain.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (16)

Previously missed (6) — in code that hasn't changed since the last review.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:67

  • A valid non-object JSON body such as [] or null reaches data.get and becomes a 500 response. Validate that the decoded body is an object and return 400 for unsupported shapes.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/requirements.txt:8
  • These unconstrained requirements can be satisfied by an older installed package that lacks the local state-store/task behavior this no-cloud sample needs. Use the package's supported release ranges, consistent with pyproject.toml:24 and the bounded requirements in samples/simple_invoke_agent/requirements.txt:1.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py:66
  • Invalid request bodies are either silently accepted as defaults (malformed JSON) or turn into 500s ([], non-numeric steps), and non-positive steps produce no checkpoint so later polls return 404. Validate the JSON object and positive integer before starting the task, returning 400 for invalid input.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/requirements.txt:8
  • These unconstrained requirements can be satisfied by an older installed package that lacks the local state-store/task behavior this no-cloud sample needs. Use the package's supported release ranges, consistent with pyproject.toml:24 and the bounded requirements in samples/simple_invoke_agent/requirements.txt:1.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/README.md:26
  • Turn-budget expiry does not cause re-entry: the watchdog is cooperative-only and this handler deliberately continues after its signal. This walkthrough therefore promises a recovery mode that users will not observe.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/agent.py:21
  • Core's watchdog is cooperative-only and explicitly does not end or re-enter an ignoring handler (tasks/_manager.py:1613-1616). This description incorrectly promises recovery at turn-budget expiry; the current loop simply continues in the same turn after the signal.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/README.md:60

  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28-40). Stating the later version can mislead users of 2.1.x into omitting the required switch.
As of `azure-ai-agentserver-core` **2.2.0b1** the durable-task subsystem is

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:50

  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28-40). Stating the later version can mislead users of 2.1.x into omitting the required switch.
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` builds the

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:58

  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28-40). Stating the later version can mislead users of 2.1.x into omitting the required switch.
As of `azure-ai-agentserver-core` **2.2.0b1** the durable-task subsystem is

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py:48

  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28-40). Stating the later version can mislead users of 2.1.x into omitting the required switch.
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` builds the

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_langgraph/app.py:86

  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28-40). Stating the later version can mislead users of 2.1.x into omitting the required switch.
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` constructs the

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_langgraph/app.py:91

  • A direct @task.start() call does not degrade to non-durable execution when the manager is disabled; it raises and the task does not run, as core's contract states. Remove the claim that this agent still runs non-durably.
# ``langgraph_session.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_multiturn/app.py:49

  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28-40). Stating the later version can mislead users of 2.1.x into omitting the required switch.
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` constructs the

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_multiturn/app.py:54

  • A direct @task.start() call does not degrade to non-durable execution when the manager is disabled; it raises and the task does not run, as core's contract states. Remove the claim that this agent still runs non-durably.
# ``session_workflow.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_research/app.py:104

  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28-40). Stating the later version can mislead users of 2.1.x into omitting the required switch.
# azure-ai-agentserver-core 2.2.0b1: ``AgentServerHost`` constructs the

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_research/app.py:109

  • A direct @task.start() call does not degrade to non-durable execution when the manager is disabled; it raises and the task does not run, as core's contract states. Remove the claim that this agent still runs non-durably.
# ``deep_research.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.
  • Files reviewed: 14/16 changed files
  • Comments generated: 1
  • Review effort level: Balanced

…dent

Addresses two PR review comments:

- test_resilient_samples_structure.py: the resilient-tasks opt-in guard matched
  a substring, which a comment or a late call would satisfy. Parse the module
  AST instead and assert a real set_resilient_tasks_enabled(True) call precedes
  InvocationAgentServerHost() construction (the switch is read at host build
  time, so a later call is too late).

- resilient_hello_forever/agent.py: the worker only read the durable stop marker
  when ctx.cancel was set. A cancel routed to a different replica writes the
  marker but cannot set the owning process's ctx.cancel, so that worker would
  run forever while polling reported 'stopped'. Check the marker every iteration
  independently of ctx.cancel (unconditional tick sleep avoids a busy loop after
  the per-turn watchdog sets ctx.cancel). Updated the sample docstring and added
  an e2e test proving the marker alone stops the worker.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8abde5e2-e955-42bc-957b-7bf6877cd753
Copilot AI review requested due to automatic review settings September 3, 2026 07:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical checkpoint isolation and multiple correctness, dependency, and test issues remain unresolved.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (10)

Previously missed (6) — in code that hasn't changed since the last review.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:102

  • A successful POST can still be followed by a transient 404: start() schedules the worker, but the worker awaits store creation/read before writing its first checkpoint, so this branch can run before either item exists. This is especially visible when polling is routed to another replica. Persist an initial durable iterations: 0 record as part of starting the invocation so every replica can distinguish a just-started worker from an unknown ID.

This issue also appears in the following locations of the same file:

  • line 107
  • line 124
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/requirements.txt:8
  • These unbounded requirements can resolve to a future incompatible major release, contrary to the package's sample dependency convention (samples/simple_invoke_agent/requirements.txt:1) and the runtime Core bound (pyproject.toml:24). Keep the sample dependency-light while constraining it to the compatible release lines.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py:66
  • steps <= 0 starts successfully but executes no loop iteration, so no checkpoint is ever created; after the ephemeral task record is removed, every poll returns 404 instead of completed. Reject non-positive step counts before starting the task (or persist a completion checkpoint for zero steps).
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/requirements.txt:8
  • These unbounded requirements can resolve to a future incompatible major release, contrary to the package's sample dependency convention (samples/simple_invoke_agent/requirements.txt:1) and the runtime Core bound (pyproject.toml:24). Keep the sample dependency-light while constraining it to the compatible release lines.
    sdk/agentserver/azure-ai-agentserver-invocations/tests/e2e/test_resilient_hello_forever.py:34
  • This collection-time environment mutation is process-global and is never restored, so merely collecting this module can make unrelated tests run as non-hosted. The fixture already removes this variable with monkeypatch, which restores it after each test; remove the module-level mutation.
    sdk/agentserver/azure-ai-agentserver-invocations/tests/e2e/test_resilient_hello_world.py:37
  • This collection-time environment mutation is process-global and is never restored, so merely collecting this module can make unrelated tests run as non-hosted. The fixture already removes this variable with monkeypatch, which restores it after each test; remove the module-level mutation.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/README.md:26

  • The timeout claim is incorrect for this implementation. Task timeouts are cooperative: the watchdog only sets ctx.timeout_exceeded and ctx.cancel, while this loop ignores both, so budget expiry does not interrupt or re-enter the task. Document that the worker intentionally continues after the watchdog, or add explicit timeout handling consistent with the desired lifecycle.
It also sets `timeout=timedelta(days=7)` (the maximum per-turn budget), because
each *turn* is watchdog-bounded (default 1 day). When any interruption occurs —
crash, redeploy, or turn-budget expiry — the task is re-entered with
`ctx.entry_mode == "recovered"` and resumes from its checkpointed iteration.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/agent.py:52

  • FoundryStateStore is agent-scoped by default and explicitly has no built-in session isolation, but this constant shares checkpoints and stop markers across every session. A caller-supplied invocation ID can therefore collide with another session, including making one session's cancel marker stop another worker. Scope the store name by session and persist that session ID in the task input for recovery.
CHECKPOINT_STORE = "hello_forever_checkpoints"

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:107

  • The stop marker records that cancellation was requested, not that the worker has terminated. handle_cancel writes it before run.cancel(), so an immediate poll reports stopped while the owner may still be sleeping or executing another iteration. Use separate durable stop_requested and terminal stopped state (the worker should write the latter immediately before returning), and report cancelling until that acknowledgement exists.
            "status": "stopped" if stop_marker is not None else "running",

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:126

  • This creates a durable stop marker even when the invocation never existed. Consequently POST /unknown/cancel returns success and the subsequent GET reports a fabricated stopped invocation, whereas the invocations contract tests require unknown cancellation to return 404. Verify the durable task/checkpoint before writing, and also reject an already-present stop marker.
    store = await FoundryStateStore.get_or_create(CHECKPOINT_STORE)
    try:
        await store.set_item(f"{invocation_id}{STOP_SUFFIX}", {"stop": True})
  • Files reviewed: 14/16 changed files
  • Comments generated: 3
  • Review effort level: Balanced

… docs

Addresses three PR review comments:

- Session isolation (hello_world + hello_forever): the checkpoint store was a
  single global name, so another session reusing a caller-supplied invocation id
  could read or overwrite a run's progress. Namespace the store by the
  invocation's session id (checkpoint_store_name(session_id)) as the other
  resilient samples do, and carry session_id in the durable task input so
  recovery re-enters with the same scope. app.py reads request.state.session_id
  in start/poll/cancel; READMEs and curl examples pass ?agent_session_id=.

- Opt-in structure gate: ast.walk accepted a set_resilient_tasks_enabled(True)
  call nested in an uncalled def/class. Inspect only direct module-level
  expression/assignment statements so the guard enforces import-time execution
  before InvocationAgentServerHost() construction.

- hello_forever timeout docs: the per-turn watchdog is cooperative (only sets
  ctx.timeout_exceeded/ctx.cancel); the loop ignores it, so turn-budget expiry
  cannot cause re-entry. Corrected the module docstring and README to describe
  crash/redeploy recovery only.

Tests updated to pass session_id and use the session-scoped store name.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8abde5e2-e955-42bc-957b-7bf6877cd753
Addresses this round of PR review comments:

- Multi-user isolation (both samples): a single agent session can serve multiple
  users, but the identity used only session + caller-supplied invocation id, so
  two users reusing an invocation id shared the TaskManager record, checkpoint,
  and stop marker. durable_task_id now also incorporates request.state.user_id;
  the checkpoint store is opened via a new open_checkpoint_store() helper with
  user_isolation=True + the explicit user id; user_id is carried in the durable
  input so recovery reopens the same partition.

- call-id for recovery (both samples): recovered tasks have no inbound request,
  and TaskManager restores x-agent-foundry-call-id only from input["call_id"].
  Persist request.state.call_id in the durable input; updated the handler-test
  request stub accordingly.

- Non-atomic seed (both samples): the get-then-set initial checkpoint could let
  concurrent retries clobber an advanced checkpoint. Seed with create_item
  (atomic create) and treat FoundryStorageConflictError as "already seeded".

- Test global-flag leak: importing the app modules flips the process-global
  set_resilient_tasks_enabled(True); the handler-test fixture now saves and
  restores resilient_tasks_enabled() so the flag does not leak across tests.

Task-level e2e tests updated to open the store via the sample's
open_checkpoint_store (session + user) and pass user_id in inputs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8abde5e2-e955-42bc-957b-7bf6877cd753
Copilot AI review requested due to automatic review settings September 3, 2026 10:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Malformed byte bodies can return 500, and the structural test enforces an invalid lifecycle ordering.

Review details

Suppressed comments (12)

Previously missed (10) — in code that hasn't changed since the last review.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:81

  • json.loads() raises UnicodeDecodeError for a non-UTF-8 byte body, so this handler returns 500 for malformed input despite its 400 contract. Catch that exception alongside JSONDecodeError.

This issue also appears on line 112 of the same file.
sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py:68

  • json.loads() raises UnicodeDecodeError for a non-UTF-8 byte body, so this handler returns 500 for malformed input despite its 400 contract. Catch that exception alongside JSONDecodeError.

This issue also appears on line 111 of the same file.
sdk/agentserver/azure-ai-agentserver-invocations/tests/test_resilient_samples_structure.py:331

  • This ordering requirement does not match the host lifecycle. AgentServerHost reads the flag inside its ASGI lifespan (core/_base.py:312-350), not during InvocationAgentServerHost() construction, so app = InvocationAgentServerHost(); set_resilient_tasks_enabled(True) is valid but this test rejects it. Keep the module-level-call check, but remove the constructor-order assertion and update the surrounding explanation.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/README.md:68
  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28,36-45). Using the later version misleads users of 2.1.0b1 into thinking the switch is unnecessary.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:66
  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28,36-45). Using the later version misleads users of 2.1.0b1 into thinking the switch is unnecessary.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:62
  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28,36-45). Using the later version misleads users of 2.1.0b1 into thinking the switch is unnecessary.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py:53
  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28,36-45). Using the later version misleads users of 2.1.0b1 into thinking the switch is unnecessary.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_langgraph/app.py:86
  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28,36-45). Using the later version misleads users of 2.1.0b1 into thinking the switch is unnecessary.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_multiturn/app.py:49
  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28,36-45). Using the later version misleads users of 2.1.0b1 into thinking the switch is unnecessary.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_research/app.py:104
  • The opt-in breaking change shipped in core 2.1.0b1, not 2.2.0b1 (azure-ai-agentserver-core/CHANGELOG.md:28,36-45). Using the later version misleads users of 2.1.0b1 into thinking the switch is unnecessary.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:114

  • After cancellation, hello_forever returns normally, so its one-shot TaskManager record is deleted, but this non-expiring checkpoint and stop marker remain. A retry with the same invocation ID then swallows this conflict, starts a fresh task, returns 202, and that task immediately exits on the stale marker. Detect the existing stop marker here and return the already-stopped invocation instead of starting a new worker.
        await store.create_item(task_id, {"name": name, "iterations": 0})
    except FoundryStorageConflictError:
        pass  # already seeded by a concurrent request or a previous attempt

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py:115

  • A completed one-shot task is deleted by the framework (core/tasks/_manager.py:2656-2660), while this checkpoint remains. A retry of the same invocation ID after completion therefore reaches this conflict branch, then successfully creates a new task instead of hitting TaskConflictError; it can return 202 started while doing no work, or continue the old checkpoint with different input. Treat an already-completed checkpoint as the existing invocation rather than starting another task.
        await store.create_item(
            task_id, {"name": name, "steps": steps, "completed_steps": 0}
        )
    except FoundryStorageConflictError:
        pass  # already seeded by a concurrent request or a previous attempt
  • Files reviewed: 15/17 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings September 4, 2026 04:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Moderate correctness and test-isolation issues must be resolved before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (6)

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/requirements.txt:8

  • These unbounded requirements can resolve incompatible future major releases. This package explicitly constrains sample dependencies (CHANGELOG.md:40-43), and neighboring published-package samples use bounded ranges (for example samples/basic_voice_agent/requirements.txt:1); add compatible minimum and upper bounds here as well.
azure-ai-agentserver-core
azure-ai-agentserver-invocations

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:76

  • Ctrl-C performs graceful host shutdown, not a hard crash. This task never checks ctx.shutdown or calls exit_for_recovery(), so after the shutdown grace period TaskManager cancels it and deletes the ephemeral one-shot task record (core/tasks/_manager.py:2076-2101); a restart then has nothing to recover. Use an actual hard kill in this crash-recovery walkthrough, or add graceful-shutdown handling to the task.
2. Poll until `completed_steps` is a few in, then **kill the process** (Ctrl-C).

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/requirements.txt:8

  • These unbounded requirements can resolve incompatible future major releases. This package explicitly constrains sample dependencies (CHANGELOG.md:40-43), and neighboring published-package samples use bounded ranges (for example samples/basic_voice_agent/requirements.txt:1); add compatible minimum and upper bounds here as well.
azure-ai-agentserver-core
azure-ai-agentserver-invocations

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_langgraph/app.py:91

  • This fallback description is incorrect for invocations: get_task_manager() raises and this .start() call is not caught, so the request fails rather than running the agent non-durably (core/tasks/_manager.py:289-306). Reserve the non-durable fallback wording for protocols that explicitly swallow this exception.
# ``langgraph_session.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_multiturn/app.py:54

  • This fallback description is incorrect for invocations: get_task_manager() raises and this .start() call is not caught, so the request fails rather than running the agent non-durably (core/tasks/_manager.py:289-306). Reserve the non-durable fallback wording for protocols that explicitly swallow this exception.
# ``session_workflow.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_research/app.py:109

  • This fallback description is incorrect for invocations: get_task_manager() raises and this .start() call is not caught, so the request fails rather than running the agent non-durably (core/tasks/_manager.py:289-306). Reserve the non-durable fallback wording for protocols that explicitly swallow this exception.
# ``deep_research.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.
  • Files reviewed: 15/17 changed files
  • Comments generated: 17
  • Review effort level: Balanced

Addresses the latest reviewer pass on the LRA samples:

- Terminal failure state (both samples): a one-shot task record is deleted on
  terminal exit, so a task that raised left the durable checkpoint below its
  target and polling reported in_progress/running forever. The task now persists
  an explicit status (in_progress/completed/failed, plus stopped via marker for
  the forever worker) and the poll handler surfaces it. hello_world skips the
  terminal write when already finalized (idempotent, no ETag churn on recovery).

- Reuse-after-terminal (both samples): create_item is now the authoritative
  existence gate. Because start() would NOT conflict after a completed/stopped
  one-shot record is deleted, a reused invocation id previously spun up a new task
  against a stale checkpoint/marker. On create_item conflict we now return 409
  with the current status and never start a second task; start() is only reached
  when we actually created the record.

- Invalid UTF-8 body -> 400 (both samples): json.loads on non-UTF-8 bytes raises
  UnicodeDecodeError; caught alongside JSONDecodeError so malformed bytes are 400,
  not 500.

- Version fix: the opt-in breaking change landed in core 2.1.0b1, not 2.2.0b1.
  Corrected across hello_world, hello_forever, langgraph, multiturn, research
  (app.py comments + READMEs).

- Structure test: the opt-in switch is read at host lifespan start, not at
  InvocationAgentServerHost() construction, so the enable-before-construction
  ordering assertion was not meaningful. Now only requires a real module-level
  set_resilient_tasks_enabled(True) that executes on import.

- Tests: removed module-level os.environ.pop(FOUNDRY_HOSTING_ENVIRONMENT) from the
  three e2e files (collection-time global mutation outside pytest's restoration);
  the fixtures already delenv via monkeypatch. Added handler coverage for reuse
  409, failed-status polling, and invalid-UTF-8 400.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8abde5e2-e955-42bc-957b-7bf6877cd753
Copilot AI review requested due to automatic review settings September 4, 2026 05:36
Copilot stopped reviewing on behalf of Nathandrake229 due to an error September 4, 2026 05:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Copilot was unable to run its full agentic suite in this review.

Pull request overview

Copilot reviewed 15 out of 17 changed files in this pull request and generated 5 comments.

Copilot AI review requested due to automatic review settings September 4, 2026 05:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

One or more issues must be addressed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (7)

Previously missed (2) — in code that hasn't changed since the last review.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/requirements.txt:8

  • Bound both dependencies to compatible release lines. Unbounded requirements make this sample resolve future incompatible major versions, contrary to the package convention demonstrated by samples/basic_voice_agent/requirements.txt:1 and recorded in CHANGELOG.md:42-43.
    sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/requirements.txt:8
  • Bound both dependencies to compatible release lines. Unbounded requirements make this sample resolve future incompatible major versions, contrary to the package convention demonstrated by samples/basic_voice_agent/requirements.txt:1 and recorded in CHANGELOG.md:42-43.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:234

  • This treats every existing checkpoint as an active worker. If the checkpoint already has status == "failed", cancel writes a stop marker; subsequent GETs prioritize that marker and incorrectly change the terminal result from failed to stopped. An already stopped worker is also reported as cancelling. Check the durable status and existing marker first, and return the existing terminal state without writing a new marker.
        if await store.get_item(task_id) is None:

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:76

  • Ctrl-C initiates the host's graceful lifespan shutdown, but this task never checks ctx.shutdown or calls exit_for_recovery(). After the shutdown grace period, TaskManager cancels the execution and deletes the ephemeral task record, so the restart described here has nothing to recover. Either add graceful-shutdown handling to the task or instruct users to perform an actual hard kill.
2. Poll until `completed_steps` is a few in, then **kill the process** (Ctrl-C).

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_langgraph/app.py:91

  • This says the agent runs non-durably, but langgraph_session.start() raises TaskManagerNotInitialized, so no task execution occurs. Reserve the non-durable fallback wording for protocol paths that explicitly catch this exception.
# startup (i.e. at module-import time). Without it, ``get_task_manager()`` /
# ``langgraph_session.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_multiturn/app.py:54

  • This says the agent runs non-durably, but session_workflow.start() raises TaskManagerNotInitialized, so no task execution occurs. Reserve the non-durable fallback wording for protocol paths that explicitly catch this exception.
# startup (i.e. at module-import time). Without it, ``get_task_manager()`` /
# ``session_workflow.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_research/app.py:109

  • This says the agent runs non-durably, but the preceding call raises TaskManagerNotInitialized, so the durable task does not run at all. Reserve the non-durable fallback wording for protocol paths that explicitly swallow this exception.
# ``deep_research.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.
  • Files reviewed: 15/17 changed files
  • Comments generated: 3
  • Review effort level: Balanced

A new minimal (core + invocations only, no LLM) long-running-agent sample that
sits between hello_world (finite, runs to completion) and hello_forever
(indefinite, must be stopped): a FINITE job that would finish on its own but can
be cancelled mid-run.

Cancel is cooperative and durable: the cancel endpoint writes a durable cancel
marker to a separate state-store key; the task reads it before each step and, if
present, records status="cancelled" and stops early. Using a durable marker
(instead of the in-process ctx.cancel event) makes cancellation correct
cross-replica and across crash/redeploy, and keeping it in its own key avoids
racing the checkpoint's ETag.

Built to the same robustness bar as the other minimal samples: user-isolated,
session-scoped store; SHA-256 composite task id; atomic create_item existence
gate returning 409 on reuse; explicit terminal status (completed/cancelled/
failed) so polling never sticks at in_progress; UTF-8 / body-shape / positive-
integer-steps validation returning 400; call_id carried for recovery.

Registered in the structure test's minimal-sample set. Adds task-level e2e tests
(run to completion, cancel before start, cancel mid-run, recovery honours a
pending cancel) and a handler-level lifecycle test (start -> cancel -> cancelling
-> cancelled, reuse 409, 404, 400). Also hardened test_hello_world_handlers to
drain the background job to a terminal state before the reuse-409 assertion.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8abde5e2-e955-42bc-957b-7bf6877cd753
Copilot AI review requested due to automatic review settings September 4, 2026 06:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Moderate issues remain in start-failure reconciliation, checkpoint cursor handling, and dependency constraints.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (5)

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:140

  • The checkpoint is created before start(), but only TaskConflictError is handled. If provider access or scheduling raises any other exception, the request returns 500 while the durable record remains running; retries are permanently rejected and polling reports a worker that does not exist. Reconcile the seed on every start failure, while handling the possibility that a remote start was accepted before a transport error.
    try:
        await hello_forever.start(
            task_id=task_id,

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:76

  • The documented Ctrl-C path does not demonstrate crash recovery for this task. TaskManager.shutdown() signals graceful shutdown, waits up to its grace period, then cancels handlers that do not call exit_for_recovery(); cancellation deletes the ephemeral task record, leaving nothing for the startup recovery scan. Use an abrupt process/container kill in these instructions, or make the task shutdown-aware.
2. Poll until `completed_steps` is a few in, then **kill the process** (Ctrl-C).

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/agent.py:181

  • completed is captured only at task entry and is never advanced by the loop. If a later checkpoint or terminal write raises after earlier steps succeeded, this handler overwrites the durable cursor with that stale entry value, so polling regresses and the recorded resume point is lost. Use the just-fetched current item as the failure cursor.
                        "completed_steps": completed,

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/app.py:144

  • The checkpoint is created before start(), but only TaskConflictError is handled. If provider access or scheduling raises any other exception, the request returns 500 while this durable item remains in_progress; retries then hit the existence gate with 409 and polling reports a task that never started. Reconcile the seed on every start failure (for example, persist an explicit failed-to-start state or safely roll it back while accounting for an ambiguous remote accept).
    try:
        await hello_world.start(
            task_id=task_id,

sdk/agentserver/azure-ai-agentserver-invocations/tests/test_resilient_samples_structure.py:56

  • This makes resilient_cancellable part of the required shipping surface, but the PR description says that exactly two minimal samples are being added and does not mention or verify this third sample. Please either update the PR scope/verification to include the cancellable sample or remove it from this change.
    "resilient_cancellable",
  • Files reviewed: 20/23 changed files
  • Comments generated: 6
  • Review effort level: Balanced

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor
[Pilot] PR Pipeline Failure Analysis

What failed

Azure Pipeline build 6788933 (python - pullrequest / "Build Analyze" job) failed at the CSpell spell-check step. Tests passed for the new resilient_hello_world / resilient_hello_forever samples and their e2e tests (all 20 collected tests in this artifact passed), but CSpell flagged the word fstore as unknown in 5 locations within sdk/agentserver/azure-ai-agentserver-invocations/tests/e2e/test_resilient_hello_handlers.py (lines 157, 158, 159, 235, 236), causing the job to exit non-zero.

Relevant pipeline output
CSpell: Files checked: 5, skipped: 15, Issues found: 5 in 1 file.
##[error]sdk/agentserver/azure-ai-agentserver-invocations/tests/e2e/test_resilient_hello_handlers.py:157:5 - Unknown word (fstore)
##[error]sdk/agentserver/azure-ai-agentserver-invocations/tests/e2e/test_resilient_hello_handlers.py:158:16 - Unknown word (fstore)
##[error]sdk/agentserver/azure-ai-agentserver-invocations/tests/e2e/test_resilient_hello_handlers.py:159:15 - Unknown word (fstore)
##[error]sdk/agentserver/azure-ai-agentserver-invocations/tests/e2e/test_resilient_hello_handlers.py:235:5 - Unknown word (fstore)
##[error]sdk/agentserver/azure-ai-agentserver-invocations/tests/e2e/test_resilient_hello_handlers.py:236:16 - Unknown word (fstore)
##[error]Spelling errors detected. To correct false positives or learn about spell checking see: https://aka.ms/azsdk/engsys/spellcheck
##[error]PowerShell exited with code '1'.

Recommended next steps

  • Rename the identifier fstore to a dictionary-recognized word (e.g. store or file_store) in test_resilient_hello_handlers.py, or add fstore to the repository's CSpell custom word list (e.g. .vscode/cspell.json or a root word-list file) if the abbreviation should be preserved.
  • This is a pure spell-check/lint failure — no test logic, build, or compilation errors were found; all 20 tests in the artifact passed before CSpell ran.
  • See https://aka.ms/ci-fix

Automated fix: Requested

Generated by Pipeline Analysis Next Steps · auto · 42.6 AIC · ⌖ 2.84 AIC · ⊞ 9.6K ·

CI (Analyze/cspell): renamed the test variable `fstore` -> `failed_store`
(cspell flagged the coined word), fixing the failing Analyze stage.

Substantive sample fixes from review:

- Orphaned-seed recovery (all three minimal samples): the invoke handler seeds a
  durable record via create_item before start(). If start() crashed (or the
  process died) after the seed but before the TaskManager record was durable, the
  record stayed nonterminal forever, every retry got 409, and polling reported
  in_progress/running with nothing to recover. Now: create_item conflict with a
  TERMINAL status returns 409 (idempotent); a NONTERMINAL status falls through and
  (re-)schedules idempotently (start() recovers the orphan; TaskConflictError
  means a task is already running). On a non-conflict start() failure we delete
  the seed we created this request so a retry starts cleanly (safe under an
  ambiguous remote accept, since retry + TaskConflictError still converges).

- Progress rollback on failure (hello_world, cancellable): the failure handler
  wrote the entry-time completed_steps (often 0), rolling durable progress
  backward. Now a local `done` cursor advances after each successful checkpoint
  and is used when recording failure.

Nits:
- Structure test AST guard now accepts set_resilient_tasks_enabled(enabled=True)
  (keyword form), not just positional True.
- e2e tests use an autouse monkeypatch.syspath_prepend fixture instead of an
  unrestored sys.path.insert (no cross-test leakage).
- requirements.txt for the three minimal samples now use compatible ranges
  (core>=2.1.0b1,<3.0.0; invocations>=1.2.0b1,<2.0.0) matching package convention.
- hello_world README: clarify the TaskManager task id / checkpoint key is a hashed
  composite of user+session+invocation, not the raw invocation id.
- cancellable README: the crash-durable-cancel demo requires SIGKILL, not Ctrl-C
  (graceful shutdown lets the task observe the marker and exit before restart).

Added a handler test locking in orphan-seed recovery (202, runs to completion).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8abde5e2-e955-42bc-957b-7bf6877cd753
Copilot AI review requested due to automatic review settings September 4, 2026 07:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Six critical and four moderate findings remain in persistence concurrency, local storage, recovery, and cancellation behavior.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (3)

Previously missed (2) — in code that hasn't changed since the last review.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:262

  • This writes a stop marker even when the durable record is already failed or already stopped. Because handle_get gives the marker precedence, cancelling a failed worker permanently changes its visible status from failed to stopped and hides its error; repeated cancellation also reports cancelling for a terminal worker. Check the persisted status and existing marker before writing, and return the existing terminal state instead.
    sdk/agentserver/azure-ai-agentserver-invocations/tests/test_resilient_samples_structure.py:56
  • This introduces resilient_cancellable as a third complete sample, but the PR description and verification plan say that only resilient_hello_world and resilient_hello_forever are being added. Please either document and verify this additional scope or move it to a separate change.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:83

  • Ctrl-C initiates graceful host shutdown rather than a crash. This task does not observe ctx.shutdown; it either finishes during the grace period or is cancelled afterward, and one-shot cancellation deletes its TaskManager record, so the documented restart will have nothing to recover. Tell users to use a hard kill here, consistent with the cancellable sample's recovery instructions.
2. Poll until `completed_steps` is a few in, then **kill the process** (Ctrl-C).
  • Files reviewed: 20/23 changed files
  • Comments generated: 9
  • Review effort level: Balanced

…otent

Addresses this review pass across all three minimal samples:

- Store-name length (agent.py): a protocol session id can be 256 chars, and the
  local FoundryStateStore backend base64-encodes the whole store name into one
  filename, overflowing the 255-byte NAME_MAX (ENAMETOOLONG) for this no-cloud
  sample. checkpoint_store_name now hashes the session component (SHA-256) so the
  name is fixed-width while remaining unique per session.

- Failure write defeated checkpoint CAS (agent.py): the failure handler re-read
  the latest ETag before writing status=failed, so a stale/lost execution could
  clobber a newer owner's progress and mark the live run failed. It now writes
  with THIS execution's last-owned etag; if the etag moved, the best-effort write
  simply fails instead of overwriting newer state.

- Non-idempotent orphan recovery (app.py): the nonterminal-conflict re-schedule
  path used the retry request's name/steps instead of the persisted durable
  input, so a retry reusing the invocation id with a different body could shorten,
  extend, or roll back an in-flight job (e.g. 5/10 retried with steps=3). It now
  reuses the persisted parameters (name/steps for hello_world & cancellable, name
  for hello_forever) before rescheduling. Strengthened the orphan-recovery test to
  POST a different body and assert the persisted params win (finishes at 5/5).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8abde5e2-e955-42bc-957b-7bf6877cd753
Copilot AI review requested due to automatic review settings September 4, 2026 07:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The stop-state reporting and resilient-task opt-in regression guard have unresolved moderate-severity correctness issues.

Review details

Suppressed comments (8)

Previously missed (2) — in code that hasn't changed since the last review.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_forever/app.py:228

  • The stop marker records a stop request, not that the worker has exited. handle_cancel can write it while hello_forever is still sleeping (and it may checkpoint another iteration), so this immediately reports terminal stopped; it also masks a persisted failed status because the marker wins. Persist status: stopped in the worker before returning, report cancelling while only the marker exists, and preserve failed precedence.
    sdk/agentserver/azure-ai-agentserver-invocations/tests/test_resilient_samples_structure.py:290
  • This accepts any literal True anywhere in the call, so invalid shapes such as set_resilient_tasks_enabled(value=False, typo=True) satisfy the regression guard; the documented enabled=True keyword would itself raise because the public parameter is named value. Match exactly one positional True or value=True.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_cancellable/README.md:1

  • The PR description says this change adds two minimal samples (resilient_hello_world and resilient_hello_forever), but this introduces a third substantial cancellable sample and associated tests. Please update the title/description and change list to reflect this scope, or split this sample into a separate PR.
# Minimal resilient long-running agent — **cancellation**

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_hello_world/README.md:83

  • Ctrl-C initiates graceful host shutdown rather than the hard crash this procedure intends to demonstrate. Because hello_world does not handle ctx.shutdown with exit_for_recovery, it can continue during the shutdown grace period, making this recovery walkthrough nondeterministic; use SIGKILL as in the verified scenario.
2. Poll until `completed_steps` is a few in, then **kill the process** (Ctrl-C).

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_langgraph/app.py:91

  • These statements contradict each other: when get_task_manager()/.start() raises TaskManagerNotInitialized, this invocation agent does not run non-durably—it does not start. Describe the fail-fast behavior so users do not expect an in-process fallback that this API does not provide.
# ``TaskManager`` ONLY when this switch is on, and it must be set before host
# startup (i.e. at module-import time). Without it, ``get_task_manager()`` /
# ``langgraph_session.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_multiturn/app.py:54

  • These statements contradict each other: when get_task_manager()/.start() raises TaskManagerNotInitialized, this invocation agent does not run non-durably—it does not start. Describe the fail-fast behavior so users do not expect an in-process fallback that this API does not provide.
# ``TaskManager`` ONLY when this switch is on, and it must be set before host
# startup (i.e. at module-import time). Without it, ``get_task_manager()`` /
# ``session_workflow.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.

sdk/agentserver/azure-ai-agentserver-invocations/samples/resilient_research/app.py:109

  • These statements contradict each other: when get_task_manager()/.start() raises TaskManagerNotInitialized, this invocation agent does not run non-durably—it does not start. Describe the fail-fast behavior so users do not expect an in-process fallback that this API does not provide.
# startup (i.e. at module-import time). Without it, ``get_task_manager()`` /
# ``deep_research.start()`` raise ``TaskManagerNotInitialized`` and the agent
# runs non-durably with no crash recovery — defeating the purpose of a resilient
# long-running agent.

sdk/agentserver/azure-ai-agentserver-invocations/tests/test_resilient_samples_structure.py:56

  • The PR description says this adds two minimal samples and verifies five resilient samples, but this third sample plus its implementation/tests makes six. Update the title, description, and verification scope to include resilient_cancellable, or remove it from this PR, so the reviewed scope matches what ships.
    "resilient_cancellable",
  • Files reviewed: 20/23 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

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.

3 participants