automations: feat: migrate execution to Agent Host Protocol - #331796
automations: feat: migrate execution to Agent Host Protocol#331796Ulugbek Abdullaev (ulugbekna) wants to merge 31 commits into
Conversation
Move Automation definitions, scheduling, run lifecycle, and persistence into the Agent Host while safely migrating legacy VS Code data and retaining older-host fallback behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 142bb750-abf2-4b29-91b8-1e9ab2444635
📬 CODENOTIFYThe following users are being notified based on files changed in this PR: Ladislau Szomoru (@lszomoru)Matched files:
Anthony Kim (@anthonykim1)Matched files:
|
There was a problem hiding this comment.
Pull request overview
Migrates Automations from browser-owned execution to durable Agent Host Protocol state, while retaining legacy migration and older-host fallback.
Changes:
- Synchronizes AHP Automation protocol types, actions, reducers, and capabilities.
- Adds host-owned scheduling, execution, persistence, recovery, and cancellation.
- Projects host state into Sessions with migration, compatibility, UI, telemetry, and tests.
Show a summary per file
| File | Description |
|---|---|
scripts/sync-agent-host-protocol.ts |
Updates AHP synchronization. |
src/vs/platform/agentHost/browser/nullAgentHostService.ts |
Extends the null host API. |
src/vs/platform/agentHost/browser/remoteAgentHostProtocolClient.ts |
Adds Automation protocol methods. |
src/vs/platform/agentHost/common/agentService.ts |
Expands Agent Host contracts. |
src/vs/platform/agentHost/common/automationMigration.ts |
Defines migration handshake data. |
src/vs/platform/agentHost/common/meta/automationMeta.ts |
Adds Automation metadata helpers. |
src/vs/platform/agentHost/common/state/agentSubscription.ts |
Adds Automation subscriptions. |
src/vs/platform/agentHost/common/state/protocol/.ahp-version |
Records synchronized AHP revision. |
src/vs/platform/agentHost/common/state/protocol/action-origin.generated.ts |
Updates generated action provenance. |
src/vs/platform/agentHost/common/state/protocol/actions.ts |
Exports synchronized actions. |
src/vs/platform/agentHost/common/state/protocol/channels-automation-run/actions.ts |
Defines run actions. |
src/vs/platform/agentHost/common/state/protocol/channels-automation-run/reducer.ts |
Reduces run lifecycle state. |
src/vs/platform/agentHost/common/state/protocol/channels-automation-run/state.ts |
Defines run state. |
src/vs/platform/agentHost/common/state/protocol/channels-automation/actions.ts |
Defines catalogue actions. |
src/vs/platform/agentHost/common/state/protocol/channels-automation/commands.ts |
Defines Automation commands. |
src/vs/platform/agentHost/common/state/protocol/channels-automation/reducer.ts |
Reduces catalogue state. |
src/vs/platform/agentHost/common/state/protocol/channels-automation/state.ts |
Defines catalogue state. |
src/vs/platform/agentHost/common/state/protocol/channels-chat/state.ts |
Updates synchronized chat state. |
src/vs/platform/agentHost/common/state/protocol/channels-root/state.ts |
Adds Automation capabilities. |
src/vs/platform/agentHost/common/state/protocol/channels-session/actions.ts |
Updates synchronized session actions. |
src/vs/platform/agentHost/common/state/protocol/channels-session/commands.ts |
Updates session commands. |
src/vs/platform/agentHost/common/state/protocol/channels-session/reducer.ts |
Updates session reduction. |
src/vs/platform/agentHost/common/state/protocol/channels-session/state.ts |
Updates session state. |
src/vs/platform/agentHost/common/state/protocol/commands.ts |
Exports synchronized commands. |
src/vs/platform/agentHost/common/state/protocol/common/actions.ts |
Registers synchronized action types. |
src/vs/platform/agentHost/common/state/protocol/common/commands.ts |
Adds Automation capabilities. |
src/vs/platform/agentHost/common/state/protocol/common/messages.ts |
Updates protocol messages. |
src/vs/platform/agentHost/common/state/protocol/common/reducer-helpers.ts |
Adds reducer helpers. |
src/vs/platform/agentHost/common/state/protocol/common/state.ts |
Updates shared state types. |
src/vs/platform/agentHost/common/state/protocol/reducers.ts |
Exports Automation reducers. |
src/vs/platform/agentHost/common/state/protocol/state.ts |
Exports Automation state. |
src/vs/platform/agentHost/common/state/protocol/version/registry.ts |
Versions new protocol features. |
src/vs/platform/agentHost/common/state/sessionActions.ts |
Extends client action unions. |
src/vs/platform/agentHost/common/state/sessionReducers.ts |
Routes Automation reducers. |
src/vs/platform/agentHost/common/state/sessionState.ts |
Adds Automation components. |
src/vs/platform/agentHost/electron-browser/localAgentHostService.ts |
Exposes local Automation APIs. |
src/vs/platform/agentHost/node/agentHostAutomationService.ts |
Implements host Automation authority. |
src/vs/platform/agentHost/node/agentHostInputRequestTracker.ts |
Tracks Automation input requests. |
src/vs/platform/agentHost/node/agentHostServerMain.ts |
Flushes persistence on shutdown. |
src/vs/platform/agentHost/node/agentHostShutdown.ts |
Adds bounded shutdown flushing. |
src/vs/platform/agentHost/node/agentHostStateManager.ts |
Stores and publishes Automation state. |
src/vs/platform/agentHost/node/agentHostStorageService.ts |
Hardens durable storage writes. |
src/vs/platform/agentHost/node/agentService.ts |
Wires Automation commands and execution. |
src/vs/platform/agentHost/node/automationCron.ts |
Implements cron evaluation. |
src/vs/platform/agentHost/node/claude/claudeCanUseTool.ts |
Marks Automation input handling. |
src/vs/platform/agentHost/node/claude/claudeElicitation.ts |
Maps Automation elicitation. |
src/vs/platform/agentHost/node/codex/codexElicitationMapper.ts |
Maps Codex elicitation. |
src/vs/platform/agentHost/node/codex/codexUserInputMapper.ts |
Maps Codex user input. |
src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts |
Handles Automation-originated prompts. |
src/vs/platform/agentHost/node/protocolServerHandler.ts |
Routes Automation protocol requests. |
src/vs/platform/agentHost/test/common/agentSubscription.test.ts |
Tests Automation subscriptions. |
src/vs/platform/agentHost/test/node/agentHostAutomationService.test.ts |
Tests host Automation behavior. |
src/vs/platform/agentHost/test/node/agentHostInputRequestTracker.test.ts |
Tests input tracking. |
src/vs/platform/agentHost/test/node/agentHostShutdown.test.ts |
Tests bounded shutdown. |
src/vs/platform/agentHost/test/node/agentHostStateManager.test.ts |
Tests Automation state management. |
src/vs/platform/agentHost/test/node/agentHostStorageService.test.ts |
Tests storage failure handling. |
src/vs/platform/agentHost/test/node/agentSideEffects.test.ts |
Tests Automation side effects. |
src/vs/platform/agentHost/test/node/automationCron.test.ts |
Tests cron semantics. |
src/vs/platform/agentHost/test/node/claudeAgent.test.ts |
Tests Claude Automation prompts. |
src/vs/platform/agentHost/test/node/claudeElicitation.test.ts |
Tests Claude elicitation. |
src/vs/platform/agentHost/test/node/codex/codexElicitationMapper.test.ts |
Tests Codex elicitation mapping. |
src/vs/platform/agentHost/test/node/codex/codexUserInputMapper.test.ts |
Tests Codex input mapping. |
src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts |
Tests Copilot Automation sessions. |
src/vs/platform/agentHost/test/node/protocolServerHandler.test.ts |
Tests Automation request routing. |
src/vs/platform/agentHost/test/node/reducers.test.ts |
Tests synchronized reducers. |
src/vs/sessions/contrib/automations/browser/automationRunner.ts |
Supports host-dispatched runs. |
src/vs/sessions/contrib/automations/browser/automationScheduler.ts |
Negotiates scheduling ownership. |
src/vs/sessions/contrib/automations/browser/automationService.ts |
Routes Automation capabilities. |
src/vs/sessions/contrib/automations/browser/automations.contribution.ts |
Registers the host provider. |
src/vs/sessions/contrib/automations/browser/providerAutomationService.ts |
Coordinates provider migration. |
src/vs/sessions/contrib/automations/test/browser/automationRunner.test.ts |
Tests host-run delegation. |
src/vs/sessions/contrib/automations/test/browser/automationScheduler.test.ts |
Tests scheduling ownership. |
src/vs/sessions/contrib/automations/test/browser/automationService.test.ts |
Tests service routing. |
src/vs/sessions/contrib/automations/test/browser/providerAutomationService.test.ts |
Tests provider migration. |
src/vs/sessions/contrib/providers/agentHost/AGENT_HOST_SESSIONS_PROVIDER.md |
Documents provider behavior. |
src/vs/sessions/contrib/providers/agentHost/browser/agentHostAutomationStore.ts |
Projects and migrates host Automations. |
src/vs/sessions/contrib/providers/agentHost/browser/localAgentHostSessionsProvider.ts |
Registers the local store. |
src/vs/sessions/contrib/providers/agentHost/browser/reconnectableAgentHostAutomationStore.ts |
Handles reconnect and fallback. |
src/vs/sessions/contrib/providers/agentHost/test/browser/agentHostAutomationStore.test.ts |
Tests projection and migration. |
src/vs/sessions/contrib/providers/agentHost/test/browser/localAgentHostSessionsProvider.test.ts |
Tests local provider wiring. |
src/vs/sessions/contrib/providers/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts |
Registers remote Automations. |
src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts |
Tests remote provider wiring. |
src/vs/sessions/contrib/sessions/browser/views/automationsView.ts |
Reflects host operation availability. |
src/vs/sessions/services/sessions/common/sessionsProvider.ts |
Extends provider migration contracts. |
src/vs/workbench/contrib/chat/common/automations/automationService.ts |
Extends shared Automation contracts. |
src/vs/workbench/contrib/chat/common/automations/automationTelemetry.ts |
Adds migration telemetry. |
src/vs/workbench/contrib/chat/common/automations/automationsEnabled.ts |
Updates timeout documentation. |
src/vs/workbench/contrib/terminal/test/browser/agentHostPty.test.ts |
Updates Agent Host test mock. |
src/vs/workbench/services/agentHost/browser/editorRemoteAgentHostServiceClient.ts |
Exposes remote Automation APIs. |
Review details
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Suppressed comments (2)
src/vs/platform/agentHost/node/agentHostAutomationService.ts:520
- The active-run check occurs only once before iterating all triggers. If two schedule triggers are due at the same tick, each branch appends a pending run, so this claim returns multiple non-terminal runs for one Automation and violates the single-active-run invariant. Stop claiming further triggers after the first run is added (while preserving the cursor for that claimed trigger), or re-check the pending map inside the loop.
src/vs/platform/agentHost/node/agentHostAutomationService.ts:923 - This validator accepts any non-null object as a definition, so malformed persisted data such as
{ definition: {} }passesisStoredAutomationsand then crashes innormalizeLegacyVsCodeAutomationModelwhen it readsdefinition.session.provider. The run validator similarly accepts arbitraryoriginandlifecycleobjects. Validate the complete protocol shapes (including triggers, session, operations, run origin/lifecycle, and timestamps) so corrupt state fails closed as intended.
- Files reviewed: 89/89 changed files
- Comments generated: 4
- Review effort level: Balanced
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the canRun composite used by handleConfigurationChanged so a create that arrives while chat.automations.enabled is false does not advertise Run.
Bring Replaced to parity with Set/Removed at the working-directory gate: extend the action union, canonicalize both URIs in the resolver, enforce editor-only client and provider capability at _dispatchActionNow, and include Replaced in the customization-enablement listener. Also honors the Removed contract for primaryReplacement: rejects index-0 removal when the agent advertises primaryReplacement.
There was a problem hiding this comment.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/vs/platform/agentHost/node/agentHostAutomationService.ts:920
- This validator accepts any non-null object as an
AutomationDefinition(and the run validator below similarly accepts arbitraryorigin/lifecycleobjects)._load()immediately callsnormalizeLegacyVsCodeAutomationModel, which dereferencesdefinition.session.provider, so persisted data such asdefinition: {}passes validation and then throws during service construction instead of disabling Automation capability without rewriting the corrupt state. Please validate the nested canonical definition/run discriminants and required fields before treating storage as typed.
- Files reviewed: 92/92 changed files
- Comments generated: 2
- Review effort level: Balanced
Break out of the trigger loop once a schedule trigger has been claimed for an Automation, after advancing that trigger's cursor. Prevents two simultaneously-due schedule triggers on one Automation from both starting sessions and violating the one-non-terminal-run-per-Automation invariant. Deferred triggers keep their cursors untouched so their firings are re-evaluated on the next tick rather than dropped.
…ne run Two schedule triggers on one Automation whose past-due cursors land in the same claim tick now coalesce into a single run. Catch-up is idempotent: one run at now, regardless of how many missed firings a sibling trigger also carries. The claim block skips when another trigger has already claimed for this Automation this tick, but the deferred cursor still rolls forward to its next cron occurrence so it does not re-fire on the next tick. Replaces the earlier break-after-claim approach from e2c2657, which serialized the deferred firing back-to-back.
…durably removed The migration path published imported snapshots with Run granted before the legacy source row was CAS-removed, creating a double-authority window where both schedulers could dispatch the same occurrence. If the removal failed, the window became permanent. Stage imports with a pending meta flag, centralize the Run/Remove permission check in the host, restore Remove when the flag clears, gate scheduling ownership on the flag, and add an acknowledge hook so cross-provider retargets clear the pending state after the source is durably gone. Recovery drains stranded pending rows on reconnect.
|
Copilot resolve the merge conflicts in this pull request |
Co-authored-by: benvillalobos <4691428+benvillalobos@users.noreply.github.com>
|
Copilot fix CI |
Co-authored-by: benvillalobos <4691428+benvillalobos@users.noreply.github.com>
Resolved the conflicts with current |
Co-authored-by: benvillalobos <4691428+benvillalobos@users.noreply.github.com>
Fixed in |
The merge collapsed main's two-block structure for working-directory actions back into one, dropping the explicit reject for `session/workingDirectoryReplaced`. No provider advertises `primaryReplacement` and the host has no backend side effect for the action, so the reducer would apply an unvalidated mutation. Restore the standalone reject before the EditorWindow-gated block for Set / Removed.
Recreate the pre-existing dispatch-guard convention rather than switching this hot path to isClientDispatchable. The generic check pulled in synced protocol code and widened the scope of this change. Automation and automation-run actions now flow through family guards, consistent with how session, chat, terminal, changeset, and annotations actions are already handled. The family-vs-permission gap this restores is pre-existing and tracked for maintainer follow-up.
The dispatch guard no longer uses isClientDispatchable, so nothing imports the synced reducer-helpers.ts. Its generated-source compatibility patch only existed to widen that helper's signature for the guard, so remove it and let the file sync verbatim. The state.ts dead-import patch stays until the synced AHP revision picks up the upstream fix.
Keep URI-based subscription APIs narrow while preserving exact AHP catalogue channels. Mark failed reconnect restorations by channel and cover the exact-channel path with regressions.
Resolve conflicts from main's chat-contributions and sealed-graph composition refactors. Port automation wiring onto the extracted agentServiceFoundation callback adapter, and drop the branch's inline persisted-turn-usage path in favor of main's chat contribution.
5070c87 to
d670771
Compare
Connor Peet (connor4312)
left a comment
There was a problem hiding this comment.
some AH-side comments
Catch the automations AHP migration up to main (293593c, 46 commits). Resolved 4 conflicts, all additive: - agentService.ts: union import of automation + main's session creation-reference symbols; kept our start-message extraction alongside main's new delegation param - agentServiceComposition.ts: main's createInstance options arg plus our automation service wiring - localAgentHostSessionsProvider.test.ts: import union - sessionState.ts: dropped dead SessionInput* aliases (matches #332219) Dropped the old session-orchestration symbols that main renamed to creation-provenance in #332558.
The catalogue channel constant was `ahp-automations://`, which is not a
round-trippable URI. `URI.parse('ahp-automations://').toString()` drops the
empty authority and yields `ahp-automations:`, so a channel serialized on the
client no longer matched the catalogue check on the host.
Append a `catalog` authority so the URI survives a parse/toString round-trip.
Comparing catalogue channels as URIs everywhere (ResourceMap/isEqual) remains
the intended followup.
Now that the catalogue channel URI round-trips through parse/toString, its subscription key no longer needs to preserve the raw channel string. Drop the `_subscriptionChannel` helper and its automation-catalogue special case, and key every channel through `_subscriptionResource` by its parsed URI. Comparing channels as URIs everywhere (ResourceMap/isEqual) remains the intended followup.
Use dedicated automation channels for subscription relevance and reuse the canonical action-family guards in state management. This avoids silent drift when the protocol adds actions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8c6d543f-7777-4e8b-9371-3e39a0842293
AgentService received the automation service through a post-construction setAutomationService setter, leaving a definite-assignment field and a one-off wiring step. It depends on AgentService only through the lazy callback adapter, so it can be built first and passed in the collaborators bag like every other dependency. Its constructor installs durable state without firing emitters, so the earlier ordering is safe.
The subscription map had been changed from a ResourceMap to a Map<string> keyed by a synthetic getComparisonKey string, purely to hold the lossy catalogue channel under a raw key. With the catalogue channel now round-trippable and the special-case keying gone, every entry keys by a real URI again. Restores the ResourceMap that main uses and drops the synthetic key from the entry type, the resource helper, and all fifteen call sites.
The subscription manager threaded raw channel strings through _subscribe/_unsubscribe to dodge a lossy round-trip on the catalogue channel. Now that the catalogue URI round-trips, revert those callbacks to (resource: URI) to match main. The wire boundary keeps its .toString() serialization in the protocol client.
Resolves import unions across agentSubscription, agentService, and three test files where main's durable-error-parts and legacy-protocol-compat work sat alongside the automation-catalog symbols. Updates the automation run lifecycle to read ChatError from action.part.error after main reshaped ChatErrorAction.
Translate legacy automations at the client boundary instead of persisting editor projection metadata. Derive the compatibility view from host state and canonicalize supported round trips.
Adopt the extracted Agent Host provider service (#332481): route the automation availability check through resolveProvider and fire handleAgentsChanged from onDidRegisterProvider. Thread the subscribe cancellation guard through _subscribeStateChannel so the automation catalog path keeps the post-await re-check.
Serialize folderUri as explicit URI components instead of URI.toJSON(). toJSON() only emits the lazily cached fsPath and formatted fields once they have been accessed, so two URIs for the same folder could serialize differently. That made the snapshot equality check during Agent Host migration fail with "kept changing while migrating" for every folder-target automation, blocking migration indefinitely. Reads already go through URI.revive, so existing ledger data stays compatible in both directions.
_handleEnvelope finalized an automation run on ChatTurnComplete, ChatTurnCancelled, or ChatError but did not check rejectionReason. A rejected action never reached authoritative host state, so applying it marked a still-live run terminal and orphaned its session. Guard against rejected envelopes before finalizing, matching the sessions provider's action handler.
Summary
This PR migrates VS Code Automations from its browser-owned implementation to the shared Automations design in Agent Host Protocol (AHP).
Automation definitions and runs are now durable Agent Host state synchronized through the singleton
ahp-automations://catalogue. The Agent Host owns scheduling, run claims, session creation, cancellation, lifecycle transitions, and restart recovery.The existing VS Code Automation store remains only as:
The canonical protocol surface comes from merged
microsoft/agent-host-protocolmain ate511d70f(the shared Automations work from microsoft/agent-host-protocol#393).Why move authority into the Agent Host?
Previously, Automations depended on a VS Code-specific JSON ledger, renderer-window leader election, a browser scheduler, and a browser runner that created sessions and inferred lifecycle.
That model did not provide one shared authority across editor windows, remote clients, and other AHP consumers. It also duplicated protocol concepts that now have canonical representations.
The AHP model establishes:
AutomationStateentries inahp-automations://;automation/set;expectedRevision;update,remove, andrunoperations;pending,running,completed,failed, andcancelledrun lifecycle;Architecture
Canonical protocol state
The sync script now imports the Automation catalogue/run actions, reducers, state, commands, capabilities, action provenance, and version metadata from AHP.
It records the source commit in
.ahp-version, preserves the exact rawahp-automations://channel string, and fails loudly if either generated-source compatibility transform stops matching upstream output.Agent Host authority
AgentHostAutomationServicepersists:Every mutation follows persist-before-publish ordering:
The host also owns cron evaluation, due-run claims, session creation, run timeouts, cancellation, lifecycle derivation, and restart recovery.
Sessions projection
AgentHostAutomationStoremaps canonical AHP state onto the existingISessionsProviderAutomationscontract. It translates schedule shape, target/session configuration, model identifiers, run summaries, and local/remote session URIs.ReconnectableAgentHostAutomationStorehandles capability negotiation, reconnect, feature enablement, migration readiness, and older-host fallback.Safe legacy migration
Migration deliberately uses canonical AHP actions rather than introducing migration commands into the protocol.
For each legacy Automation:
automation/createRequested;automation/set;After all items transfer, the client sends an internal completion handshake with every expected resource. The host verifies them, durably writes completion, grants
run, and publishes the complete entries.Until that succeeds:
runand rejects execution;Corrupt, future, or partially unreadable legacy data fails closed rather than being dropped.
Scheduling and runs
The Agent Host evaluates canonical five-field cron schedules in IANA time zones, including ranges, lists, steps, names, Sunday normalization, and Unix DOM/DOW semantics.
Cursor advancement and run creation are committed together. Misfire behavior supports
runOnceandskip.Execution ordering is:
pendingrun and request ID;running;MessageKind.Automationprompt;Only one non-terminal run may exist per Automation.
Long-running host-dispatched runs no longer produce a false 30-second client timeout. Mutation and session-assignment waits remain bounded; only the authoritative terminal-lifecycle wait is allowed to outlive 30 seconds.
Compatibility details
agent-host-copilotcli:autoare converted to provider-native AHP IDs such asauto.copilotcli:/...are projected to local, remote, or aliased Sessions resource schemes so run history resolves correctly.Storage and shutdown hardening
Agent Host storage now surfaces load/write errors, supports flushed mutations with rollback, and refuses to overwrite corrupt or future Automation state.
Standalone shutdown uses a bounded flush helper so a failed or stalled write is reported without skipping disposal, logger cleanup, or process exit.
Observability
Migration logs and
automation.migrationtelemetry record:They never include names, prompts, model IDs, or folder paths. Expected reconnect/disposal cancellation is excluded from failed telemetry.
Intentional boundaries
chat.automations.enabledandchat.automations.runTimeoutMinutessettings are reused and mirrored to host configuration.Validation
origin/main.e511d70f.npm run typecheck-client.npm run valid-layers-check.git diff --check.History -> Today.The implementation also received repeated independent long-context reviews. High-confidence findings were treated as blocking, fixed, and re-reviewed until no blockers remained.
Suggested review order
scripts/sync-agent-host-protocol.tsand generated Automation protocol modules.agentHostAutomationService.tsand its storage/state-manager integration.agentHostAutomationStore.tsandreconnectableAgentHostAutomationStore.ts.providerAutomationService.ts,automationScheduler.ts, andautomationRunner.ts.Residual risks