automations: feat: add shared automations protocol - #393
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 935e37c8-2b68-40b1-9273-f195cb3af9a0
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 935e37c8-2b68-40b1-9273-f195cb3af9a0
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 935e37c8-2b68-40b1-9273-f195cb3af9a0
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1131c2f6-bcd0-4012-9265-fdb2685179e7
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1131c2f6-bcd0-4012-9265-fdb2685179e7
isClientDispatchable did not accept automation or automation-run actions, so servers could not use it to reject server-only actions arriving on the new automation channels. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1131c2f6-bcd0-4012-9265-fdb2685179e7
Collapse recurring schedules to the specified five-field AHP cron format, document the automation and run protocol surfaces, expand the guide, and regenerate every client and schema. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1131c2f6-bcd0-4012-9265-fdb2685179e7
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1131c2f6-bcd0-4012-9265-fdb2685179e7
Require imported definitions to remain disabled through cutover and let clients supply the source scheduler's next unevaluated occurrence. Document durable occurrence claiming so catch-up runs cannot be duplicated after restart. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1131c2f6-bcd0-4012-9265-fdb2685179e7
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1131c2f6-bcd0-4012-9265-fdb2685179e7 # Conflicts: # clients/go/ahptypes/state.generated.go # clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/State.generated.kt # clients/rust/crates/ahp-types/src/actions.rs # clients/rust/crates/ahp-types/src/state.rs # clients/rust/crates/ahp/src/reducers.rs # clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/State.generated.swift # scripts/generate-rust.ts
| * Values described by {@link AutomationTriggerDefinition.configSchema}. | ||
| * Clients MUST preserve unknown entries when editing other fields. | ||
| */ | ||
| config?: Record<string, unknown>; |
There was a problem hiding this comment.
Would we want additional information from the AutomationTriggerDefinition/AutomationTriggerEventDefinition to aid in display here? Since looking them up again required a ListAutomationTriggerDefinitionsParams method call and potentially some enumeration under the hood...
| workingDirectories?: URI[]; | ||
| /** Opaque host-defined runtime metadata. */ | ||
| _meta?: Record<string, unknown>; | ||
| } |
There was a problem hiding this comment.
I'm unclear on how this is used. Automations already have their own session.workingDirectories, how is this different?
| /** Monotonic definition revision used for optimistic concurrency. */ | ||
| revision: number; |
There was a problem hiding this comment.
this does not appear to actually be used
| export interface PreviewAutomationScheduleParams extends BaseParams { | ||
| /** Schedule preview is requested from the root channel. */ | ||
| channel: 'ahp-root://'; | ||
| /** Portable AHP cron schedule to evaluate. */ | ||
| schedule: AutomationSchedule; | ||
| /** Requested maximum number of future occurrences; the host MAY cap it. */ | ||
| count?: number; | ||
| } |
There was a problem hiding this comment.
I'm unconvinced we need a whole protocol method for this. I would not be opposed to including a test case for cron scheduling in client libraries in this repo (like action tests, we should have a JSON assertion file and then have each language test against that fixture for consistency)
| * | ||
| * @category Automation Run State | ||
| */ | ||
| export type AutomationRunCause = |
There was a problem hiding this comment.
We have the notion of origin's already for message and chat provenance, I suggest we use the same terminology here
| export type AutomationRunCause = | |
| export type AutomationRunOrigin = |
| * | ||
| * @category Automation Run State | ||
| */ | ||
| export const enum AutomationRunOperation { |
There was a problem hiding this comment.
Same Q on operations here -- is this a capability or open ended operation
| * @category Automation Run State | ||
| */ | ||
| export interface AutomationRunArtifact extends ContentRef { | ||
| /** Stable artifact id within this run, used by artifact actions. */ |
There was a problem hiding this comment.
- I don't see any notion of artifact actions in this PR
- These should be unique already by
ContentRef.uri, no need for another ID
| export type AutomationRunLifecycle = | ||
| | AutomationPendingRunLifecycle | ||
| | AutomationRunningRunLifecycle | ||
| | AutomationBlockedRunLifecycle | ||
| | AutomationCompletedRunLifecycle | ||
| | AutomationFailedRunLifecycle | ||
| | AutomationCancelledRunLifecycle; |
There was a problem hiding this comment.
It seems in these we kind of reinvent/mirror the SessionInputRequest / SessionStatus but use slightly different terminology/interfaces. Can we consilidate these?
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 41e3da72-3ce4-4b56-ab25-b321f53ecad9
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 41e3da72-3ce4-4b56-ab25-b321f53ecad9
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 41e3da72-3ce4-4b56-ab25-b321f53ecad9
Remove per-automation revision fields and define deterministic host-ordered patch processing for catalogue updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 41e3da72-3ce4-4b56-ab25-b321f53ecad9
Keep requested working directories in the automation session template and use each linked session as the authority for its resolved directories. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 41e3da72-3ce4-4b56-ab25-b321f53ecad9
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 41e3da72-3ce4-4b56-ab25-b321f53ecad9
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 41e3da72-3ce4-4b56-ab25-b321f53ecad9
Store host-normalized trigger and selected event descriptors inline so saved automations remain directly renderable without contextual rediscovery. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ca7db31a-310e-4636-99ab-ec6c9501c032
Remove the unused run-level artifact collection and actions so automation runs focus on lifecycle, linked sessions, and cancellation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ca7db31a-310e-4636-99ab-ec6c9501c032
Remove the mirrored run-level blocker state so linked sessions remain authoritative for attention and response routing while runs retain task-level lifecycle outcomes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ca7db31a-310e-4636-99ab-ec6c9501c032
Remove per-run operation state and derive cancellation availability from host capability and lifecycle. Regenerate and update all five client reducers, event routing, state mirrors, and tests for the accumulated automation protocol changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use non-trapping last-wins indexing for duplicate automation resources and cover both snapshot and action rebuild paths. Correct the stale automation command documentation and Kotlin snapshot discriminator comment. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
|
||
| let initialValue = await multi.host("local") | ||
| let initial = try XCTUnwrap(initialValue) | ||
| XCTAssertEqual(initial.automations?.execution.lifetime, .managed) |
There was a problem hiding this comment.
AI Review: AutomationCapabilities has no execution or lifetime member, so this assertion and the equivalent reconnect assertion on line 167 fail to compile. Remove them or assert capabilities that are actually configured, such as create and runHistoryLimit.
| .sessionIsReadChanged, | ||
| .sessionIsArchivedChanged: | ||
| .sessionIsArchivedChanged, | ||
| .automationRunCancelRequested: |
There was a problem hiding this comment.
AI Review: The protocol marks automation/createRequested, automation/updateRequested, and automation/removed as client-dispatchable, but Swift recognizes only automationRun/cancelRequested. Integrations using these exported validation helpers will reject valid catalogue mutations. Add all three strings and corresponding StateAction switch cases, with tests.
| return; | ||
| } | ||
| if (channel === AUTOMATIONS_URI) { | ||
| this.setAutomationCatalog(automationReducer(this.automationCatalogState, action as AutomationAction)); |
There was a problem hiding this comment.
AI Review: This reduces every server-pushed automation envelope without checking rejectionReason. A rejected automation/removed therefore deletes the catalogue entry even though authoritative state remains unchanged, violating the documented reconciliation semantics. Skip rejected envelopes before reduction, audit equivalent mirror paths, and add a rejected-removal test.
| /** Target {@link AutomationState.resource}. */ | ||
| automation: URI; | ||
| /** | ||
| * Durable client-generated idempotency key. Retrying with the same key and |
There was a problem hiding this comment.
AI Review: The durable key is defined only as (automation, requestId), while requestId is an arbitrary client-generated string. Two clients can therefore reuse a value such as 1, causing one manual run to resolve to the other client's run. Scope the durable key by reconnect-stable clientId, or require globally unique request IDs.
| * {@link AutomationSchedule.expression}. Omission means no restriction beyond | ||
| * the cron format's one-minute resolution. | ||
| */ | ||
| minIntervalMinutes?: number; |
There was a problem hiding this comment.
AI Review: minIntervalMinutes is emitted as JSON Schema number, so values such as 1.5 validate in TypeScript while generated Go, Rust, Kotlin, and Swift bindings require integers. The same mismatch affects runHistoryLimit and AutomationRunSummary.sessionCount. Generate integer schemas with minimum 1 for the interval and 0 for the counts, and add cross-language validation tests.
Integrate the AHP 0.8 release, 1.0 development version, and atomic working-directory replacement while preserving the shared-automations protocol and regenerated clients. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ca7db31a-310e-4636-99ab-ec6c9501c032
Rename AutomationOperation.Dispose and its wire value to Remove and remove so the permission terminology matches automation/removed across the protocol, docs, fixtures, schemas, and generated clients. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ca7db31a-310e-4636-99ab-ec6c9501c032
Add MessageKind.Automation and require automation definitions to use it for their initial run message, so clients can distinguish generated turns from direct user input while session origin retains the linked automation and run resources. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ca7db31a-310e-4636-99ab-ec6c9501c032
Stop the reconnect test from referencing the removed automation execution capability while retaining coverage for the remaining persisted automation capabilities. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ca7db31a-310e-4636-99ab-ec6c9501c032
e511d70
into
microsoft:main
Summary
Add shared, host-owned Automations to Agent Host Protocol.
The protocol represents:
preview, and retained history;
Multiple clients connected to one authority can share one catalogue, scheduler,
run claim, and history instead of independently scheduling duplicate sessions.
Resource model
ahp-automation:Owns the reusable definition:
ahp-automation-run:Owns one invocation:
Ordinary session/chat channels remain authoritative for transcripts, tools,
confirmations, and changes.
Triggers and execution
time zone.
Unix day-of-month/day-of-week matching.
configuration discovered through
listAutomationTriggerDefinitions.execution.lifetimedistinguishes interactivehostLifetimescheduling fromindependently managed authorities.
runAutomation.requestIdis a durable idempotency key.Cutover-safe imports
createAutomation.importcarries a stable source, batch, and item identity.It may also carry each schedule trigger's next unevaluated source occurrence.
host definition, overdue work follows the trigger's misfire policy.
execution, preventing restart from dispatching the same catch-up twice.
This supports a migration with no interval where both old and new schedulers can
run the same definition.
Commands and synchronization
Adds:
listAutomations;listAutomationTriggerDefinitions;createAutomation;updateAutomation;disposeAutomation;runAutomation;fetchAutomationRuns;previewAutomationSchedule;Presence capabilities use optional objects rather than booleans so they can gain
feature-specific details without changing capability detection.
Generated surfaces
The contract is propagated through JSON Schema, reference documentation,
TypeScript, Rust, Go, Swift, Kotlin, reducer fixtures, and round-trip fixtures.
The guide documents authority, cron semantics, imports, revisions, lifecycle,
cancellation, retention, reconciliation, and security.
Deliberate boundaries
provenance must not be cleared to change client presentation.
Main merge
Merged current AHP
mainthroughd6bf47d2, preserving its scopedcustomization-enablement model. Additive generated-client conflicts were
resolved by updating the Rust generator and regenerating every client/schema.
Validation
npm run generatenpm run testcargo test --workspaceunderclients/rustnpm run docs:buildCompanion implementation
Companion VS Code draft PR:
microsoft/vscode#330463