Skip to content

feat(ai): steer resumable Responses - #9705

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

feat(ai): steer resumable Responses#9705
Wei Meng (m5i-work) wants to merge 3 commits into
m5i/9676-response-reconnectfrom
m5i/9676-response-steering

Conversation

@m5i-work

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

Copy link
Copy Markdown
Member

Summary

Third vertical slice of #9676, stacked on #9704.

  • adds --steer with input to revise active work or start the next background turn
  • keeps --resume message-free and uses the existing positional agent/message grammar
  • submits active steering and terminal next turns through the same saved conversation.id
  • reuses the compatible saved hosted session
  • avoids mixing conversation history with previous_response_id chains
  • reconnects to an accepted replacement if its create stream disconnects

If the saved conversation is busy and the agent enables steerable_conversations, the service queues the new turn and winds down the active handler. If it is idle, the identical request starts the normal next background turn.

Validation

  • go test ./... in cli/azd/extensions/azure.ai.agents
  • go fix ./...
  • request-shape tests cover active and terminal statuses, missing conversation, and omitted empty session
  • the same steering request path was previously validated live for two cycles before the flag was split from --resume

Stack

Fixes #9779

@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.

@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.

Two steering contract issues need addressing, plus one help-text typo.

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_response_steer.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.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 message-bearing --continue for steering or continuing background Responses.

Changes:

  • Routes message-bearing continuation to steering.
  • Reuses saved conversation/session context.
  • Persists and reconnects replacement Responses.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
invoke.go Routes steering and refreshes saved status.
invoke_response_steer.go Implements steering and reconnection.
invoke_response_steer_test.go Tests continuation request shapes.
invoke_background.go Updates active-response guidance.

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

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_response_steer.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.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:23
@m5i-work
Wei Meng (m5i-work) force-pushed the m5i/9676-response-steering branch from bbc4285 to 061fe83 Compare August 28, 2026 05:23

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 4 out of 4 changed files in this pull request and generated 2 comments.

Suppressed comments (1)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_response_steer.go:98

  • The new reconnect branch is not covered by an automated test: the end-to-end steering test only returns a complete stream. Add a test that disconnects after response.created, verifies the replacement ID was persisted, and confirms the follow request resumes that replacement from the saved cursor through completion. This is the failure mode the branch is intended to protect.
	if streamErr != nil && ctx.Err() == nil && progressPersister.latest.ResponseID != "" &&
		!isTerminalResponseStatus(progressPersister.latest.Status) && isRetryableBackgroundStreamError(streamErr) {
		return a.responsesContinueRemote(ctx)

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_response_steer.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go Outdated
Copilot AI review requested due to automatic review settings August 28, 2026 08:44
@m5i-work
Wei Meng (m5i-work) force-pushed the m5i/9676-response-steering branch from 061fe83 to 52e1300 Compare August 28, 2026 08:44

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

Steering failures need structured classification, and replacement-stream recovery lacks automated coverage.

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_response_steer.go:60

  • This turns every steering rejection into an unclassified plain error, including the conversation-lock and steering-queue failures that the lifecycle spec requires to be actionable service errors (docs/specs/long-running-agent-invoke.md:476). Decode the service error and return a structured exterrors.Service/azdext.ServiceError with a stable operation code and remediation so telemetry and the host preserve the classification and suggestion, as required by azure.ai.agents/AGENTS.md:73-111.
	if resp.StatusCode >= 400 {
		responseBody, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("POST %s failed with HTTP %d: %s\n%s", responseURL, resp.StatusCode, resp.Status, responseBody)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_response_steer.go:102

  • The create-stream reconnect path has no automated coverage; the added tests exercise only request-map construction. Add an httptest.Server case that emits the replacement identity/cursor, disconnects, and verifies the follow GET resumes that replacement and reaches terminal state. This protects the PR's central recovery guarantee and the record replacement behavior required by the lifecycle spec.
	if streamErr != nil && ctx.Err() == nil && progressPersister.latest.ResponseID != "" &&
		!isTerminalResponseStatus(progressPersister.latest.Status) && isRetryableBackgroundStreamError(streamErr) &&
		flushErr == nil && closeErr == nil {
		latest := progressPersister.latest
		return a.followBackgroundResponse(ctx, rc, store, latest, os.Stdout)
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Balanced

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

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

Steering errors need structured classification, recovery guidance, and orchestration-level tests.

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_response_steer.go:60

  • This collapses every conversation-lock or steering-queue rejection into an unclassified plain error, so the host records ext.run.failed and loses the HTTP/service metadata and any actionable remediation. docs/specs/long-running-agent-invoke.md:640,647 explicitly requires these steering failures to be actionable service errors, and docs/extensions/extensions-style-guide.md:124-149 requires service failures to be classified at the orchestration boundary. Preserve the response status/service code and return a structured exterrors service error with a useful retry suggestion.
	if resp.StatusCode >= 400 {
		responseBody, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("POST %s failed with HTTP %d: %s\n%s", responseURL, resp.StatusCode, resp.Status, responseBody)
  • Files reviewed: 4/4 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_response_steer.go Outdated
Copilot AI review requested due to automatic review settings 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.

🔵 Needs a closer look

The new create-stream recovery path lacks automated end-to-end coverage, and help omits terminal next-turn behavior.

Review details

Suppressed comments (2)

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

  • The help only describes revising an active turn, but this same command starts the next background turn when the saved Response is terminal. State both outcomes so users know they can continue after completion.
work. Use --resume without input to reconnect to saved work, or with input to revise
the current turn. Use --cancel to cancel saved work. In multi-agent projects, use

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_response_steer.go:102

  • Add an automated test for the new create-stream recovery path. The current tests only exercise the request map, so a regression where the replacement identity/cursor is not saved or the follow GET targets the superseded Response would still pass; drive an SSE stream that emits the replacement identity and then disconnects, and assert the follow uses that replacement ID and cursor.
	if streamErr != nil && ctx.Err() == nil && progressPersister.latest.ResponseID != "" &&
		!isTerminalResponseStatus(progressPersister.latest.Status) && isRetryableBackgroundStreamError(streamErr) &&
		flushErr == nil && closeErr == nil {
		latest := progressPersister.latest
		return a.followBackgroundResponse(ctx, rc, store, latest, os.Stdout)
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings 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.

🔵 Needs a closer look

Steering errors need structured classification, and the reconnect path needs direct automated coverage.

Review details

Suppressed comments (3)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_response_steer.go:60

  • This orchestration boundary returns every Foundry rejection as an unstructured error, so the host falls back to ext.run.failed and conversation-lock or steering-queue failures lose service attribution and actionable guidance. Classify the parsed service failure as an exterrors.Service/ServiceError; this is required by cli/azd/extensions/azure.ai.agents/AGENTS.md:73-98 and the steering contract at docs/specs/long-running-agent-invoke.md:476.
	if resp.StatusCode >= 400 {
		responseBody, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("POST %s failed with HTTP %d: %s\n%s", responseURL, resp.StatusCode, resp.Status, responseBody)

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

  • This wording says input always revises the current turn, but the new path starts the next background turn when the saved response is terminal. Describe both outcomes so users do not mistake a new turn for modification of completed work.
work. Use --resume without input to reconnect to saved work, or with input to revise
the current turn. Use --cancel to cancel saved work. In multi-agent projects, use

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_response_steer.go:102

  • The tests only exercise request-map construction; none executes this reconnect branch, which is the PR's core recovery behavior. Add an HTTP-level test where the POST emits the replacement identity and cursor, disconnects, and verify the follow GET targets that replacement with starting_after set to the persisted cursor.
	if streamErr != nil && ctx.Err() == nil && progressPersister.latest.ResponseID != "" &&
		!isTerminalResponseStatus(progressPersister.latest.Status) && isRetryableBackgroundStreamError(streamErr) &&
		flushErr == nil && closeErr == nil {
		latest := progressPersister.latest
		return a.followBackgroundResponse(ctx, rc, store, latest, os.Stdout)
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings 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.

🔵 Needs a closer look

The reconnect branch lacks automated coverage, and the help text misstates terminal continuation behavior.

Review details

Suppressed comments (2)

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

  • The help omits the terminal case: with a completed saved Response, input does not revise that turn; it starts the next background turn. State both behaviors so users do not mistake this for mutating completed work.
work. Use --resume without input to reconnect to saved work, or with input to revise
the current turn. Use --cancel to cancel saved work. In multi-agent projects, use

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_response_steer.go:102

  • Add automated coverage for the advertised create-stream reconnect path. The new tests only exercise buildConversationContinuationRequest, so CI would not detect this branch failing to persist and follow the accepted replacement. Feed the steering action a stream that emits the replacement identity/cursor and then disconnects, and assert the follow request targets that replacement after its cursor.
	if streamErr != nil && ctx.Err() == nil && progressPersister.latest.ResponseID != "" &&
		!isTerminalResponseStatus(progressPersister.latest.Status) && isRetryableBackgroundStreamError(streamErr) &&
		flushErr == nil && closeErr == nil {
		latest := progressPersister.latest
		return a.followBackgroundResponse(ctx, rc, store, latest, os.Stdout)
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

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

Steering errors need structured service classification, and the reconnect workflow lacks automated coverage.

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_response_steer.go:60

  • Classify steering HTTP rejections as service errors instead of returning the raw body in a plain error. The adjacent resume/cancel paths convert responseLifecycleHTTPError through classifyResponseLifecycleHTTPError (invoke_background.go:282-285, 647-658), while this path loses the operation, service, and status metadata needed for actionable 409 conversation_locked and queue failures. Add a steering operation code and apply the same classification to both the create and reconnect paths.
	if resp.StatusCode >= 400 {
		responseBody, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("POST %s failed with HTTP %d: %s\n%s", responseURL, resp.StatusCode, resp.Status, responseBody)

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_response_steer_test.go:13

  • These tests never exercise responsesSteerRemote, leaving the claimed replacement reconnect path unverified. Add an HTTP-level test that emits a replacement identity, drops the create SSE stream, and asserts the follow GET uses that replacement ID/cursor and preserves its session/conversation; the existing background lifecycle suite demonstrates this level of coverage.
func TestBuildConversationContinuationRequestUsesSameShapeForAnyStatus(t *testing.T) {
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Balanced

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

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

Reconnect resilience, actionable conflict guidance, and orchestration coverage need improvement.

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

Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 3
  • Review effort level: Balanced

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 pre-identity stream disconnect can leave an accepted replacement unrecoverable and make duplicate retries likely.

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

Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Balanced

@m5i-work

Copy link
Copy Markdown
Member Author

Superseded by the revised design in #9676 and implementation #9900. Steering is now ordinary POST /responses with the same conversation, so no dedicated CLI steering operation is needed.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
21 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/9705/azure-ai-agents.zip"
  • Version: 1.0.0-beta.14.pr.9705.6794930
  • Merge commit: 6c465d7

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.

Steer active background Responses

3 participants