Carry over hosted-agent session across azd deploy#8942
Conversation
Automatically stop the current hosted-agent session during `azd deploy` and re-point the newly deployed version's session pointer at it, so the next `azd ai agent invoke` resumes the same session on the new code with its /home/session volume intact instead of minting a fresh session. - session_carryover.go: capture (predeploy) + stop-and-carry-forward (postdeploy) logic, opt-out via AZD_AGENT_SKIP_SESSION_CARRYOVER. - listen.go: wire capture into predeployHandler and carry-over into postdeployHandler (best-effort, never blocks deploy). - session_carryover_test.go: env-flag and guard no-op tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds best-effort “session carry-over” behavior for azure.ai.agents hosted agents during azd deploy, so the next azd ai agent invoke can resume the same session (and its /home/session volume) after a new agent version is deployed.
Changes:
- Introduces predeploy capture and postdeploy stop+repoint logic for carried sessions, with opt-out via
AZD_AGENT_SKIP_SESSION_CARRYOVER. - Wires session capture into
predeployHandlerand session carry-over intopostdeployHandlerfor hosted agents. - Adds unit tests for env-flag parsing and guard/no-op behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| cli/azd/extensions/azure.ai.agents/internal/cmd/session_carryover.go | Implements in-process stash, capture, stop, and session-pointer repointing across deploys. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/session_carryover_test.go | Adds tests for opt-out behavior and early-return guards. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go | Hooks session capture into predeploy and carry-over into postdeploy for hosted agents. |
| if !sessionCarryoverEnabled() || svc == nil || agentClient == nil { | ||
| return | ||
| } |
jongio
left a comment
There was a problem hiding this comment.
The session carry-over design is solid: pre-deploy capture, post-deploy stop+repoint, with best-effort guarantees that never block deploy. The code uses modern Go patterns correctly (�rrors.AsType, mutex-guarded stash) and the error handling is thorough with informative log messages at every failure point.
One suggestion on test coverage: the current tests verify guard/no-op behavior (disabled flag, nil params), but the interesting logic -- the StopSession error classification (409 conflict vs 404 vs unexpected), the endpoint re-read after deploy, and the config-store write -- is untested. A table-driven test with a fake AgentClient (or httptest server) covering the session_already_stopped proceeds path, the 404 bails-out path, and the successful end-to-end repoint would strengthen confidence in the branching logic and prevent regressions.
| if got, ok := stashedSession(svc.Name); !ok || got != "sess-abc" { | ||
| t.Fatalf("expected stash untouched when agentClient is nil, got %q (present=%v)", got, ok) | ||
| } | ||
| } |
There was a problem hiding this comment.
Consider adding a test that exercises the StopSession branching -- at minimum the session_already_stopped conflict case (should proceed to repoint) and the 404 case (should bail out). These are the most nuanced code paths and a table-driven test with a fake round-tripper or httptest server would catch regressions in the error-code matching logic.
Replace the AZD_AGENT_SKIP_SESSION_CARRYOVER opt-out env var with an opt-in per-service azure.yaml field, resumeSessionOnDeploy (default false). This is more discoverable, versioned with the project, and scoped per agent service. - config.go: add ResumeSessionOnDeploy to ServiceTargetAgentConfig. - session_carryover.go: gate capture/carry-over on the per-service field via LoadServiceTargetAgentConfig instead of the env var. - schemas: document resumeSessionOnDeploy in Agent.json and azure.ai.agent.json. - tests: cover the opt-in/opt-out/absent/nil cases. Validated live on a Foundry hosted agent: with resumeSessionOnDeploy: true and no env var, a redeploy + plain invoke resumed the same session on the new code (BUILD v9 -> v10, /home/session 2 -> 3, volume preserved). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
The switch from a global env-var opt-out to per-service resumeSessionOnDeploy config is the right design: more granular, opt-in by default, and source-controlled. Implementation is clean with proper nil/error guards.
My earlier suggestion about testing the deeper carry-over logic (StopSession 409/404 classification, endpoint re-read after deploy, config-store write) still applies as follow-up work for confidence in the branching paths, but is not blocking for this PR.
- Serialize the carry-over read-modify-write of the shared UserConfig 'sessions' map with a package mutex, since concurrent postdeploy handlers could otherwise clobber each other's carried session (last-writer-wins). - Extract stop-error classification into classifyStopSessionErr and add a table-driven test covering nil/409 already-stopped/404/other-409/500/ non-response cases. - Document that carry-over consumes the stashed session single-shot and is intentionally non-retriable (best-effort contract). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ssion-carryover-on-deploy
| // The behavior is OPT-IN per agent service via `resumeSessionOnDeploy: true` in | ||
| // azure.yaml. It is always best-effort and never fails a deploy. |
| oldKey := buildRemoteAgentKeyFromEndpoint(endpointResp.Value) | ||
| sessionID, err := getAgentSpecificContextValue(ctx, azdClient, "sessions", oldKey) | ||
| if err != nil || sessionID == "" { | ||
| return | ||
| } | ||
|
|
||
| pendingSessionCarryover.Lock() | ||
| pendingSessionCarryover.byService[svc.Name] = sessionID | ||
| pendingSessionCarryover.Unlock() |
…carryover Main refactored postdeployHandler and removed the local agentClient and the agent_api import; the session carry-over call referenced the now-undefined agentClient (typecheck failure). Rebuild agentClient from the in-scope endpoint and credential and re-add the agent_api import. Also apply gofmt to session_carryover.go (stray trailing blank line) so the gofmt lint passes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Incremental check: the two new commits (merge main + rebuild agentClient in postdeploy) are mechanical CI fixes. The agent_api.NewAgentClient(endpointResp.Value, cred) construction in postdeployHandler correctly targets the post-deploy endpoint for the StopSession call. No design changes, no new risk.
📋 Prioritization NoteThanks for the contribution! The linked issue isn't in the current milestone yet. |
…zure.yaml field Revert the opt-in control from a per-service azure.yaml field back to an environment variable, named resumeSessionOnDeploy. Carry-over is opt-in: off unless resumeSessionOnDeploy is truthy (1/true/yes/on). Removes the ServiceTargetAgentConfig.ResumeSessionOnDeploy field and its schema entries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Closes #8957
Automatically stop the current hosted-agent session during
azd deployand re-point the newly deployed version's session pointer at it, so the nextazd ai agent invokeresumes the same session on the new code with its /home/session volume intact instead of minting a fresh session. - session_carryover.go: capture (predeploy) + stop-and-carry-forward (postdeploy) logic, opt-out via AZD_AGENT_SKIP_SESSION_CARRYOVER. - listen.go: wire capture into predeployHandler and carry-over into postdeployHandler (best-effort, never blocks deploy). - session_carryover_test.go: env-flag and guard no-op tests.