Commit df29300
feat(ai-agents): add --agent-endpoint flag to invoke command (#8028)
* ai agents: --agent-endpoint flag for ephemeral remote invokes
Adds a new --agent-endpoint flag to 'azd ai agent invoke' that accepts
the full Foundry agent invocation URL printed by 'azd up' / 'azd deploy'
and lets the user invoke a deployed agent from any directory without an
azd project on disk.
* Parses the URL strictly: requires https, the *.services.ai.azure.com
Foundry host, the canonical /api/projects/.../agents/.../endpoint/
protocols/<protocol>[?api-version=...] path, no explicit port, and a
non-empty api-version when present.
* Derives the protocol (invocations or openai/responses) from the URL
and rejects any flags that have no meaning in ephemeral mode (--local,
positional name, --port, --protocol, --new-session, --new-conversation).
* Body validation runs before bearer-token acquisition so local input
errors surface ahead of any auth round-trip.
* Prints continuation hints for both server-assigned --session-id and
auto-created --conversation-id so users can preserve multi-turn state
on the next invoke.
* Adds buildResponsesURL / buildInvocationsURL helpers and unit tests
covering api-version propagation and URL-encoding of session ids.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ai agents: address review feedback on --agent-endpoint
- agent_endpoint.go: replace ad-hoc segment-by-segment path validation
with a single regex match (matches existing projectResourceIdRegex
style elsewhere in the package).
- agent_endpoint.go: introduce agentEndpointHint constant and use it
everywhere the previous 'pass the agent endpoint printed by azd up
or azd deploy' message appeared. Now points users at
'azd ai agent show', which persistently prints the endpoint URL.
- invoke.go: collapse validateAgentEndpointFlags' six-case switch into
a generic table-driven loop over disallowed flags.
- agent_endpoint_test.go: update unknown_protocol_tail expectation to
match the unified regex error message.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ai agents: address multi-model code review findings
- invoke.go: restore the `Invocation:` print line that was lost during
the rebase. Previously, `invocationsRemote` always printed the
`x-agent-invocation-id` header so users could correlate the call
for tracing. The rebased version only persisted it (and only in
project mode), so `--agent-endpoint` callers and project-mode
callers both lost the visible handle. Restore the print and keep
the persist as an extra step in project mode.
- agent_endpoint.go: reject `%2F` (or other encoded path separators)
inside the project segment of `--agent-endpoint`. The regex captures
`[^/]+` against the escaped path, so an encoded slash slipped through
validation and `url.PathUnescape` then materialized a literal `/`
in the project name. Add a `ContainsAny(name, "/\\")` check to
match the strictness already applied to the agent segment. Add a
test case covering `proj%2Fother`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ai agents: enable global-config persistence for --agent-endpoint
After PR #8034 the session/conversation store is keyed by an endpoint-derived agentKey in global UserConfig (env-independent). This wires --agent-endpoint into that store so ephemeral invokes auto-resume across calls.
Changes:
- Add buildEphemeralAgentKey: a stable, query-string-free key derived from the parsed projectEndpoint+agentName. Distinct '/ephemeral' suffix so it never collides with project-mode '/remote' keys.
- resolveRemoteContext (ephemeral): best-effort attach the parent azd daemon and set rc.agentKey. Standalone runs (no daemon) silently fall back to no-persistence.
- responsesRemote / invocationsRemote: drop the local agentKey computation; use rc.agentKey. Tighten OpenAPI-spec fetch to project mode only (no on-disk side effect for ephemeral).
- Drop --new-session / --new-conversation from validateAgentEndpointFlags so users can reset stored IDs in ephemeral mode. Add warnIneffectiveResetFlags to log a no-op warning when standalone.
- Continuation hints now require resp.StatusCode<400 so failed invokes don't tell users to continue a never-created conversation (review feedback).
- Hint gate widened to (agentKey == '' || azdClient == nil) so it fires whenever persistence is genuinely unavailable, not just when the daemon is missing.
Tests: TestBuildEphemeralAgentKey covers URL-variant stability (canonical, trailing-slash, mixed case host) and the project-mode key-collision contract. The two dropped --new-* validation cases removed from TestAgentEndpointFlagValidation.
Live-verified against the deployed responses agent: invokes 1+2 share session+conversation IDs and the agent recalls prior turns; invoke 3 with --new-session --new-conversation produces fresh IDs; invoke 4 with a URL variant (no ?api-version) hits the same persisted entry.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ai agents: replace ephemeral hint fallback with help-pointer tip
The standalone-mode hint code (printEphemeralSessionHint /
printEphemeralConversationHint) only fired when the parent azd daemon
was unreachable -- a path that does not occur in normal user flow
(�zd ai agent invoke ... always spawns the daemon). With persistence
working under #8034 those hints were essentially dead code.
Remove both helpers (and their tests / unused captureStdout helper /
net/http import) and replace with a single concise tip printed after a
successful invoke when persistence is active in both responsesRemote
and invocationsRemote:
(tip: pass --new-session or --new-conversation to reset; see
`azd ai agent invoke --help`)
Tests + lint clean. Live verified end-of-output ordering against
hello-world-python-responses.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* azure.ai.agents: protocol-aware reset hint for invoke
The post-invoke tip and reset-flag handling now match each protocol's
actual memory model:
- Responses protocol keeps the existing tip mentioning both
--new-session and --new-conversation (it uses the Foundry
Conversations API for multi-turn memory).
- Invocations protocol prints a tip that only mentions --new-session,
since memory is bound to the session and --new-conversation has no
observable effect on this path.
- If the user does pass --new-conversation while invoking an
invocations endpoint, a stderr note explains that the flag is a
no-op for this protocol and points to --new-session instead.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* azure.ai.agents: address PR review findings on --agent-endpoint
- agent_endpoint.go: agent-name validation now delegates to
agent_yaml.ValidateAgentName so --agent-endpoint enforces the same
deployable-name format as the rest of the extension. Previously
underscores and unbounded lengths slipped through local validation
only to fail later as 404s. The bespoke isValidAgentNameSegment
helper and its unit test are removed; a new
TestParseAgentEndpoint_RejectsInvalidAgentNames covers underscore,
length>63, and leading/trailing hyphen rejection.
- invoke.go (warnIneffectiveResetFlags): switched from log.Printf to
fmt.Fprintln(os.Stderr, ...). The extension silences the standard
logger unless debug mode is enabled (setupDebugLogging redirects to
io.Discard), so the previous warning never reached users in
standalone --agent-endpoint mode.
- invoke.go (invocationsRemote): removed the saveContextValue(...,
"invocations") call. validateStoreField only allows "sessions" and
"conversations", so the persistence call always failed silently. The
invocation ID is still printed for trace correlation; we just no
longer pretend to persist it.
- config_store.go: rewrote a doc comment to use "scopes" instead of
"lifecycles" so the cspell pipeline passes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ai agents: replace buildEphemeralAgentKey with buildAgentKey
Per PR review (trangevi): unify ephemeral and project-mode key builders.
buildAgentKey(ep, name, '', false) yields the same canonical shape used
elsewhere '<ep>/agents/<name>/versions/latest/remote' and inherits
the segment validation logic for free.
- Remove buildEphemeralAgentKey helper (config_store.go)
- Update sole call site in invocations setup (invoke.go)
- Drop now-redundant ephemeral-specific tests; URL-variant stability
remains covered by TestNormalizeEndpoint_StripScheme and
TestBuildRemoteAgentKeyFromEndpoint
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ai agents: fix --agent-endpoint help-text example to use responses URL
Per PR review (trangevi): the example used the /protocols/invocations URL
paired with a plain "Hello!" body, but most invocations samples expect a
JSON request body. Switch the example to the responses-protocol URL
(/protocols/openai/responses) which matches the plain-string body shape
and aligns with the other "Hello!" examples in this help block.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ai agents: remove warnIneffectiveResetFlags (dead in normal use)
The warning only fired when running the extension binary standalone with --agent-endpoint and a reset flag (no parent azd daemon). End-user invokes always go through the host and persist via gRPC, so the warn was effectively dead for the supported flow.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ai agents: address wbreza PR review nits + add resolveRemoteContext test
- Rename url locals to respURL/convURL to avoid shadowing the net/url
package import (invoke.go: responsesRemote and createConversation).
- Add t.Parallel() to the 4 functions in agent_endpoint_test.go.
- Add TestResolveRemoteContext_EphemeralMode covering the api-version
default fallback (when URL omits ?api-version=) and explicit override,
plus name/projectEndpoint/agentKey propagation. Pins the existing safe
behavior end-to-end.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Antriksh Jain <antrikshjain@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent 023deb2 commit df29300
4 files changed
Lines changed: 856 additions & 132 deletions
File tree
- cli/azd/extensions/azure.ai.agents/internal/cmd
Lines changed: 186 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
0 commit comments