Skip to content

feat(ai): resume and cancel background Responses - #9704

Closed
Wei Meng (m5i-work) wants to merge 3 commits into
mainfrom
m5i/9676-response-reconnect
Closed

feat(ai): resume and cancel background Responses#9704
Wei Meng (m5i-work) wants to merge 3 commits into
mainfrom
m5i/9676-response-reconnect

Conversation

@m5i-work

@m5i-work Wei Meng (m5i-work) commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

Second vertical slice of #9676, stacked on #9703.

  • adds --resumable --no-wait to detach after service acknowledgement and message-free --resume to reconnect to background Responses
  • adds --cancel without stopping the hosted session
  • uses the existing positional agent name for message-free resume and cancel operations
  • prevents competing turns while background work is active and handles saved terminal Responses
  • classifies final resume and cancel HTTP failures with service, status, and operation metadata

Steering is implemented separately through --steer in #9705.

Validation

  • go test ./... and go fix ./... in cli/azd/extensions/azure.ai.agents
  • live validation covered detach, reconnect, terminal follow, and cancellation

Stack

Fixes #9778

@azure-pipelines

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

@github-actions github-actions Bot added the ext-agents azure.ai.agents extension label Aug 24, 2026

@m5i-work Wei Meng (m5i-work) left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

A few correctness issues need addressing before this layer merges.

  • The stack must be restacked first: #9704 is one commit behind #9703, and the merge conflicts overwrite the throttled persister, LastSequenceNumber contract,
    esponse.cancelled handling, related tests, and the corrected specification location.

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go Outdated

@m5i-work Wei Meng (m5i-work) left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The updates address the prior findings overall; one reconnect-state edge case remains.

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_responses_stream.go Outdated
@azure-pipelines

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

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.

Pull request overview

Adds the second background Responses lifecycle slice for the Azure AI Agents extension.

Changes:

  • Adds --no-wait, message-free --continue, --cancel, and --agent-name.
  • Implements cursor-based reconnect, retries, snapshots, cancellation, and state guards.
  • Adds SSE lifecycle handling and focused tests.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
invoke.go Adds flags, validation, routing, and foreground state handling.
invoke_background.go Implements reconnect, snapshot, cancellation, and persistence logic.
invoke_background_test.go Tests persistence, cancellation, continuation, and validation.
invoke_responses_stream.go Adds resumed-stream and recovery handling.
invoke_responses_stream_test.go Tests reconnect stream edge cases.
agent_endpoint.go Builds follow, snapshot, and cancel URLs.
agent_endpoint_test.go Tests lifecycle URL construction.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_background.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_background.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_background.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_background.go Outdated
@github-actions

Copy link
Copy Markdown

📋 Prioritization Note

Thanks for the contribution! The linked issue isn't in the current milestone yet.
Thank you for logging this issue; our team is reviewing it. If you need urgent prioritization, tag Rick Winter (@RickWinter) and Kristen Womack (@kristenwomack) to let us know.

Copilot AI review requested due to automatic review settings August 28, 2026 05:18
@m5i-work
Wei Meng (m5i-work) force-pushed the m5i/9676-response-reconnect branch from 36937a6 to cd907f6 Compare August 28, 2026 05:18

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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go
Copilot AI review requested due to automatic review settings September 2, 2026 02:19

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

Foreground standalone invocation regresses, and deterministic SSE parser failures are incorrectly retried.

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

Review details

Suppressed comments (2)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_responses_stream.go:311

  • scanner.Err() also reports deterministic parser failures such as an SSE line exceeding the configured 4 MiB token limit. Marking every scanner error as a disconnect makes the follower replay the same oversized event five times and then fall back to an unbounded snapshot instead of reporting the size violation immediately. Add the retry sentinel only for transport read failures and return parser-limit errors directly.
	if err := scanner.Err(); err != nil {
		return errors.Join(errResponsesStreamDisconnected, fmt.Errorf("read Responses stream: %w", err))

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_background.go:324

  • The reconnect loop is not covered by a test that drives scripted HTTP streams end to end. Add an httptest that drops after sequence 3, verifies the next request uses starting_after=3, replays 2/3/4, and confirms only 4 is rendered and persisted through terminal completion. The current helper-only tests cannot catch wiring regressions across request creation, parsing, persistence, and retries.
		attempt, err := a.followBackgroundResponseOnce(ctx, rc, store, record, progressPersister, writer)
		if err != nil {
  • Files reviewed: 7/7 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go
Copilot AI review requested due to automatic review settings September 2, 2026 05:16
@m5i-work
Wei Meng (m5i-work) force-pushed the m5i/9676-response-reconnect branch from c2b6ef0 to a1db459 Compare September 2, 2026 05:16

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

Snapshot fallbacks can mask failed Responses, and cancellation may persist an unverified terminal state.

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

Review details

Suppressed comments (3)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_background.go:744

  • The exhausted-reconnect snapshot also converts failed, incomplete, and cancelled into successful command completion. This masks the agent failure specifically when the terminal state is learned by the final snapshot. Print the status, then return the same terminal error as the SSE path; only completed should return nil.
	if err := renderResponseSnapshot(writer, rc.name, result); err != nil {
		return errors.Join(reconnectErr, err)
	}
	if isTerminalResponseStatus(updated.Status) {
		return printResponseStatus(writer, updated.Status)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_background.go:324

  • The reconnect loop has no scripted HTTP test: existing tests exercise the counter, delay, and response classifier independently, but never verify that a dropped stream reconnects with the persisted cursor, suppresses replay, and reaches terminal state. Add an httptest.Server test around followBackgroundResponse so the integration between these helpers is covered.
	consecutiveFailures := 0
	for {
		attempt, err := a.followBackgroundResponseOnce(ctx, rc, store, record, progressPersister, writer)
		if err != nil {

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go:1529

  • Only flag validation currently covers --no-wait; no test executes this branch to prove the identity event is persisted, its actual status is printed, and later buffered output is not rendered. Add a scripted streaming-response test for the detach path, since changes to callback ordering can otherwise break resumability while all current tests still pass.
					if a.flags.noWait && progress.ResponseID != "" {
						return errBackgroundNoWait
					}
  • Files reviewed: 7/7 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go
Copilot AI review requested due to automatic review settings September 2, 2026 08:05
@m5i-work
Wei Meng (m5i-work) force-pushed the m5i/9676-response-reconnect branch from a1db459 to 6ed12b6 Compare September 2, 2026 08:05

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

Terminal-state handling can skip buffered output, and the reconnect loop lacks end-to-end regression coverage.

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

Review details

Suppressed comments (1)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_background.go:323

  • The added tests exercise retry helpers separately but never execute this reconnect loop. Add a scripted httptest.Server regression that accepts progress, drops the stream, verifies the next GET uses the persisted starting_after cursor, suppresses replay, and reaches a terminal event; otherwise the PR's central recovery path can regress while all current tests pass.
		attempt, err := a.followBackgroundResponseOnce(ctx, rc, store, record, progressPersister, writer)
  • Files reviewed: 7/7 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_background.go Outdated
Copilot AI review requested due to automatic review settings September 3, 2026 03:41
@m5i-work
Wei Meng (m5i-work) force-pushed the m5i/9676-response-reconnect branch from 6ed12b6 to 0b1730d Compare September 3, 2026 03:41

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 foreground compatibility, persistence cleanup, cancellation handling, stream rendering, and --no-wait coverage.

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

Review details

Suppressed comments (5)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go:1315

  • Ordinary foreground Responses now call this helper too, so this unconditional failure removes the existing standalone --agent-endpoint behavior: resolveRemoteContext explicitly proceeds without an azd client (invoke.go:1075-1081), and foreground invocations previously needed no response store. Require state only for --resumable; return a nil store for an ordinary foreground create so this PR does not regress that path.
	if rc.azdClient == nil {
		// The extension executable was launched directly without a parent azd process.
		// UserConfig and active-Response conflict detection are unavailable.
		return nil, responseStateUnavailable(nil)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go:1500

  • The record is deleted only after SSE parsing succeeds. Once the POST returns 2xx, the foreground turn has already been accepted and may advance or change the conversation/session; a later stream disconnect, decode error, or terminal failure returns above and leaves the previous background Response selected, so --resume can target stale work. Clear the terminal record at acceptance and join any cleanup error with the later stream error.
		if responseStore != nil {
			if err := responseStore.Delete(ctx, agentKey); err != nil {
				return fmt.Errorf("clear previous background Response: %w", err)
			}

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go:1529

  • The new --no-wait operation is only covered by flag and persister unit tests; no test executes this callback/early-return path. Add an HTTP-level regression that sends an identity event followed by buffered output and verifies the ID/status/cursor are saved, later output is not rendered, and the command returns successfully.
					if a.flags.noWait && progress.ResponseID != "" {
						return errBackgroundNoWait
					}

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_background.go:599

  • A successful response with an empty, malformed, or status-less body is treated as definitively cancelled. That terminal value makes later --resume short-circuit without checking the service, potentially hiding work that is still cancelling or active. Only persist a terminal status returned by the service; otherwise refresh the snapshot or preserve the prior nonterminal status and surface the decode problem.
	snapshot, decodeErr := decodeResponseSnapshot(body)
	if decodeErr == nil && snapshot.Status != "" {
		record.Status = snapshot.Status
	} else {
		record.Status = "cancelled"
	}

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_responses_stream.go:192

  • Resetting printed after rendering the recovery snapshot lets the terminal branch render the same snapshot again when no text delta arrives in between. A recovery response.in_progress followed directly by response.completed will therefore print the checkpoint output twice. Track “snapshot output already rendered” separately from whether a delta line is open, and skip terminal snapshot rendering when that output has already been shown.
				printed = false
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings September 3, 2026 04:28

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 persistence, cleanup, reconnect behavior, identity validation, and error classification.

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

Review details

Suppressed comments (3)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go:1535

  • This reconnect condition ignores flushErr and closeErr. When the stream disconnects at the same time cursor persistence fails, the command silently discards the persistence failure and resumes from in-memory state that was never saved, weakening later recovery. Reconnect only when both persistence operations succeeded; otherwise return the joined errors.
			if streamErr != nil && ctx.Err() == nil && progressPersister.latest.ResponseID != "" &&
				isRetryableBackgroundStreamError(streamErr) {
				latest := progressPersister.latest
				return a.followBackgroundResponse(ctx, rc, responseStore, latest, os.Stdout)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go:1488

  • A foreground Response is already accepted once this branch is reached, but the saved background record is cleared only when SSE parsing succeeds. If the accepted turn ends with response.failed or a later rendering/read error, the conversation has advanced while --resume still selects the older Response. Clear the record regardless of the stream result, then join any stream and cleanup errors.
		if responseStore != nil {
			if err := responseStore.Delete(ctx, agentKey); err != nil {
				return fmt.Errorf("clear previous background Response: %w", err)
			}

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_responses_stream.go:311

  • Scanner.Err() is not limited to transport failures; it also reports an oversized token when one SSE line exceeds the configured maximum. Adding the disconnect sentinel to every scanner error makes that deterministic protocol/size failure retry five times and potentially fall through to snapshot recovery. Return the size-limit error as non-retryable and reserve this sentinel for actual response-body I/O failures.
	if err := scanner.Err(); err != nil {
		return errors.Join(errResponsesStreamDisconnected, fmt.Errorf("read Responses stream: %w", err))
  • Files reviewed: 9/9 changed files
  • Comments generated: 4
  • Review effort level: Balanced

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go
@m5i-work

Copy link
Copy Markdown
Member Author

Superseded by #9900, which replaces invoke lifecycle flags and durable cursor state with orthogonal responses show|follow|cancel primitives. Closing this implementation in favor of the new stack.

@azure-pipelines

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

@azure-sdk-automation

Copy link
Copy Markdown
Contributor

azure.ai.agents PR build

Note

This is an unsigned development build. Install it only if you trust this PR.

Install the extension:

azd ext install "https://azuresdkartifacts.z5.web.core.windows.net/azd/extensions/pr/9704/azure-ai-agents.zip"
  • Version: 1.0.0-beta.14.pr.9704.6794928
  • Merge commit: 519a8e9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ext-agents azure.ai.agents extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reconnect to and cancel background Responses

3 participants