Support invocations_ws in agent init#9009
Conversation
|
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. |
There was a problem hiding this comment.
Pull request overview
Adds invocations_ws support and improves non-interactive agent initialization.
Changes:
- Adds protocol support for code and image deployments.
- Adds Azure context flags and region fallback updates.
- Enhances unified sample adoption and model configuration.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
schemas/azure.ai.agent.json |
Documents invocations_ws. |
internal/pkg/agents/agent_yaml/map_test.go |
Tests API protocol serialization. |
internal/cmd/init.go |
Adds flags and image protocol synthesis. |
internal/cmd/init_test.go |
Tests image protocol handling. |
internal/cmd/init_locations.go |
Merges live and embedded regions. |
internal/cmd/init_locations_test.go |
Tests region merging. |
internal/cmd/init_from_code.go |
Adds protocol and Azure context support. |
internal/cmd/init_from_code_test.go |
Tests protocol selection. |
internal/cmd/init_foundry_resources_helpers.go |
Applies Azure context flags and adjusts model resolution. |
internal/cmd/init_adopt.go |
Applies agent and model overrides during adoption. |
internal/cmd/hosted-agent-regions.json |
Adds westus2. |
cspell.yaml |
Adds VOICELIVE spelling. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/resolver.go:694
- azd-code-reviewer:
nilis howappendInvokeLocalSecondaryrepresents every multi-service project, so returning true here still emits an unqualified local invoke hint when all services areinvocations_ws/activity, and in mixed projects the runtime prompt can select a non-invocable service and fail. Inspect the service list in the multi-service path: suppress the hint when none are locally invocable, and otherwise ensure the suggestion targets only an invocable service (or avoid the ambiguous unqualified hint). Add all-non-invocable and mixed-protocol init tests.
func serviceSupportsAzdInvoke(svc *ServiceState) bool {
return svc == nil || (svc.Protocol != ProtocolInvocationsWS &&
svc.Protocol != ProtocolActivity &&
svc.Protocol != ProtocolActivityLegacy)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/state.go:477
- azd-code-reviewer: This only reads the deprecated
config:bag, but newly initialized services—including the linked sample—storekindandprotocolsinAdditionalProperties.AgentDefinitionFromServiceexplicitly prefers that unified inline shape (internal/project/agent_definition.go:182-201), so theseinvocations_wsservices resolve to an empty protocol and the resolver still emits unsupportedazd ai agent invokehints. PreferAdditionalPropertieswhen it has a non-emptykind, then fall back toConfig; the new “inline” tests should populateAdditionalPropertiesso they exercise the real shape.
func loadServiceProtocolFromConfig(svc *azdext.ServiceConfig) string {
if svc == nil || svc.Config == nil {
return ""
}
data, err := yaml.Marshal(svc.Config.AsMap())
jongio
left a comment
There was a problem hiding this comment.
Re-reviewed the four commits since my last pass, and my earlier notes are resolved. The image manifest test now defers cleanup() so it reads the file after the assertions, which was what turned BuildAndTest red. Next-step resolution treats both activity and the legacy activity_protocol spelling as non-invocable across the init, run, and deploy resolvers, so no unusable azd ai agent invoke hints are emitted. Protocol detection now reads the inline azure.yaml service config before falling back to the on-disk agent.yaml, which covers the image flow that stores the definition inline. Local build and the internal/cmd/... test suite pass, and CI is green.
jongio
left a comment
There was a problem hiding this comment.
Re-reviewed the changes since my last approval (the inline-config protocol loading, activity handling, and invoke-hint suppression).
loadServiceProtocol now checks the inline service config before falling back to agent.yaml on disk, and loadServiceProtocolFromBytes keeps the precedence order-independent: responses > invocations > activity > invocations_ws. serviceSupportsAzdInvoke consistently suppresses the local/deploy invoke hints for invocations_ws, activity, and activity_protocol across ResolveAfterRun, ResolveAfterDeploy, and appendInvokeLocalSecondary, with the mixed multi-agent case keeping invocable services. The image-manifest path now accepts activity and the --protocol help is driven by knownProtocolNames(), so the earlier questions about activity support and a static help list are handled.
Build and the nextstep, cmd, and agent_yaml test packages all pass locally. Looks good to me.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/state.go:466
- azd-code-reviewer: When an inline/config definition exists but declares no recognized protocol, this falls through to the legacy
agent.yaml. Runtime resolution does not do that:AgentDefinitionFromServiceuses the inline definition directly (internal/project/agent_definition.go:182-201), and an empty protocol list defaults toresponses(internal/pkg/agents/agent_yaml/map.go:393-397). A stale file declaringinvocations_wswould therefore suppress valid invoke hints for an agent that actually deploys asresponses. Only consult disk when no service definition exists.
if protocol := loadServiceProtocolFromConfig(svc); protocol != "" {
return protocol
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/state.go:545
- azd-code-reviewer: The new protocol selection omits the supported
a2aprotocol. For a valid mixed definition such asinvocations_wsplusa2a, this scan selectsinvocations_ws, andResolveAfterDeploysuppresses the invoke hint even thougha2ais remotely invocable (agent_api.AgentProtocol.IsInvocable). Track local and remote invocability separately so deploy can selecta2awhile local hints remain suppressed, and add this mixed-protocol case.
for _, p := range hosted.Protocols {
switch strings.TrimSpace(p.Protocol) {
case ProtocolResponses:
return ProtocolResponses, multiProtocol
case ProtocolInvocationsWS:
cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/state.go:474
- azd-code-reviewer: Falling back based on
protocol != ""breaks the inline-definition precedence. If an authoritative inline service has an empty, malformed, or currently unrecognized protocol list, runtime resolution still uses that inline definition, but this code reads a staleagent.yamland may emit commands for a different protocol. Return whether an inline/config definition was found separately from whether a recognized protocol was selected, and only consult disk when no inline definition exists.
func loadServiceProtocolInfo(projectPath string, svc *azdext.ServiceConfig) (string, bool) {
if protocol, multiProtocol := loadServiceProtocolFromConfig(svc); protocol != "" {
return protocol, multiProtocol
}
jongio
left a comment
There was a problem hiding this comment.
Left one inline note on the multi-protocol invoke-hint path. It is a narrow edge case and partly pre-existing, so non-blocking, but worth a look before merge. The protocol-selection precedence and the activity/activity_protocol handling in this push line up with the earlier feedback.
jongio
left a comment
There was a problem hiding this comment.
Re-reviewed the incremental changes since my last look. The mixed-protocol handling I flagged is sorted: precedence stays responses > invocations > activity > invocations_ws regardless of the order the manifest declares them, activity and the legacy activity_protocol both suppress invoke hints, and invokeProtocolFlag now emits an explicit --protocol on all three invoke paths (ResolveAfterRun, ResolveAfterDeploy, appendInvokeLocalSecondary) for multi-protocol services.
The build break from the missing ServiceState.MultiProtocol field is fixed in e610152. go build ./... and go test ./internal/cmd/... both pass locally for the extension.
jongio
left a comment
There was a problem hiding this comment.
Re-reviewed the incremental changes since my last look. The mixed-protocol handling I flagged is sorted: precedence stays responses > invocations > activity > invocations_ws regardless of the order the manifest declares them, activity and the legacy activity_protocol both suppress invoke hints, and invokeProtocolFlag now emits an explicit --protocol on all three invoke paths (ResolveAfterRun, ResolveAfterDeploy, appendInvokeLocalSecondary) for multi-protocol services.
The build break from the missing ServiceState.MultiProtocol field is fixed in e610152. go build ./... and go test ./internal/cmd/... both pass locally for the extension.
jongio
left a comment
There was a problem hiding this comment.
Re-reviewed the incremental changes since my last look. The mixed-protocol handling I flagged is sorted: precedence stays responses > invocations > activity > invocations_ws regardless of the order the manifest declares them, activity and the legacy activity_protocol both suppress invoke hints, and invokeProtocolFlag now emits an explicit --protocol on all three invoke paths (ResolveAfterRun, ResolveAfterDeploy, appendInvokeLocalSecondary) for multi-protocol services.
The build break from the missing ServiceState.MultiProtocol field is fixed in e610152. go build ./... and go test ./internal/cmd/... both pass locally for the extension.
jongio
left a comment
There was a problem hiding this comment.
Re-reviewed the next-step changes layered on since my last pass.
The multi-protocol path now qualifies the emitted command with --protocol when a service declares more than one protocol, so a responses + invocations_ws service no longer produces an unqualified azd ai agent invoke that protocolFromContainerAgent would reject. Legacy activity_protocol is handled alongside the canonical activity value in both classification and invoke suppression, and protocol resolution now reads inline azure.yaml config before falling back to the service manifest. The test-cleanup ordering bug is fixed and BuildAndTest is green.
One non-blocking note: a hand-authored manifest declaring invocations_ws + a2a classifies as websocket-only and drops the post-deploy a2a invoke hint, since a2a isn't in the classifier switch. That isn't reachable through init today (a2a isn't an offered protocol), so it's fine to handle separately if it ever becomes one.
Replace context.Background() with t.Context() in the two new inline AdditionalProperties assemble-state tests, per the Go 1.26 test-context convention. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 480247f9-174e-40ba-ab27-9a688d0675bf
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/resolver.go:341
- azd-code-reviewer: This early return leaves
azd ai agent runwith a dangling prompt forinvocations_ws:run.go:1094prints “Agent ready. In another terminal, try:” before calling this resolver, whileprintNextIfTerminalemits nothing for an empty list. Resolve the suggestions before printing that header (or return a protocol-appropriate next step) so WebSocket runs do not end with an empty instruction.
if !serviceSupportsAzdInvoke(svc) {
return nil
cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/resolver.go:694
- azd-code-reviewer: In the multi-service init path,
svcis deliberately left nil, so treating nil as supported still emitsazd ai agent invoke --localwhen every service isinvocations_wsor Activity. Selecting any service then fails because none is locally invocable. Handle the multi-service case explicitly and suppress the hint when no service supports local invocation; add an all-WebSocket regression case.
func serviceSupportsAzdInvoke(svc *ServiceState) bool {
return svc == nil || (svc.Protocol != ProtocolInvocationsWS &&
svc.Protocol != ProtocolActivity &&
svc.Protocol != ProtocolActivityLegacy)
jongio
left a comment
There was a problem hiding this comment.
The follow-up commits since my last pass resolve the non-invocable-protocol handling I flagged. loadServiceProtocolFromBytes now applies a stable, order-independent priority (responses > invocations > activity > invocations_ws), and serviceSupportsAzdInvoke suppresses the invoke hint for invocations_ws and both activity / activity_protocol spellings. Multi-protocol services emit an explicit --protocol flag via invokeProtocolFlag so the suggested invoke command is unambiguous, and inline azure.yaml definitions are picked up through loadServiceProtocolFromConfig in addition to agent.yaml. The --protocol help text is generated from knownProtocolNames() so it stays in sync with the known set.
Verified go build ./... and go test ./internal/cmd/... pass against the current HEAD.
| } | ||
| out = append(out, Suggestion{ | ||
| Command: fmt.Sprintf("azd ai agent invoke --local %s", invokeArg), | ||
| Command: fmt.Sprintf("azd ai agent invoke --local %s%s", invokeProtocolFlag(svc), invokeArg), |
There was a problem hiding this comment.
Formatting, does there need to be a space between the "%s" fields?
jongio
left a comment
There was a problem hiding this comment.
The follow-up I flagged earlier is resolved: non-invocable protocols (activity, activity_protocol, invocations_ws) now suppress the azd ai agent invoke next-step hint, and serviceSupportsAzdInvoke gates every emission path (ResolveAfterRun, ResolveAfterDeploy, appendInvokeLocalSecondary). Protocol precedence responses > invocations > activity > invocations_ws is order-independent and locked by the reversed-order tests. Multi-protocol services emit an explicit --protocol flag with the resolved protocol, and inline service config now feeds detection with an agent.yaml fallback. Coverage across suppression, multi-protocol flag emission, inline-config precedence, and legacy activity is solid.
Summary
invocations_wsas a selectable protocol forazd ai agent initwhile keeping the default protocol asresponses.--protocol invocations_wsfor bring-your-own-image--imageinit.invocations_wsin agent help/schema descriptions.protocol_versions.Sample used for validation
Official sample:
https://github.com/microsoft-foundry/foundry-samples/tree/main/samples/python/hosted-agents/bring-your-own/invocations_ws/hello-world
Validation
go test ./internal/cmd/...fromcli/azd/extensions/azure.ai.agentsgo test ./...fromcli/azd/extensions/azure.ai.agentsgo build ./...fromcli/azd/extensions/azure.ai.agentscspell lint "extensions/**/*.go" "extensions/**/*.md" --config ./.vscode/cspell.yaml --no-progressfromcli/azdFixes #9173