Skip to content

automations: feat: add shared automations protocol - #393

Merged
Ulugbek Abdullaev (ulugbekna) merged 28 commits into
microsoft:mainfrom
ulugbekna:ulugbekna/shared-automations
Aug 19, 2026
Merged

automations: feat: add shared automations protocol#393
Ulugbek Abdullaev (ulugbekna) merged 28 commits into
microsoft:mainfrom
ulugbekna:ulugbekna/shared-automations

Conversation

@ulugbekna

@ulugbekna Ulugbek Abdullaev (ulugbekna) commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Add shared, host-owned Automations to Agent Host Protocol.

The protocol represents:

  • durable automation definitions and optimistic revisions;
  • manual, schedule, and host-defined event triggers;
  • task-level runs with lifecycle, blockers, artifacts, and cancellation;
  • one or more linked ordinary AHP sessions;
  • durable automation/run provenance on created sessions;
  • host capabilities for execution lifetime, creation, schedules, cancellation,
    preview, and retained history;
  • idempotent, cutover-safe imports from legacy schedulers.

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:

  • title and initial user message;
  • provider, model, custom agent, working directories, and session config;
  • enabled state and trigger collection;
  • revision and next occurrence;
  • retained run summaries and permitted operations.

ahp-automation-run:

Owns one invocation:

  • manual or trigger provenance;
  • pending, running, blocked, completed, failed, or cancelled lifecycle;
  • blocker summary;
  • linked sessions and primary session;
  • run-scoped artifacts and permitted operations.

Ordinary session/chat channels remain authoritative for transcripts, tools,
confirmations, and changes.

Triggers and execution

  • Schedule triggers use one portable five-field cron expression plus an IANA
    time zone.
  • The grammar covers numeric/named fields, wildcards, ranges, lists, steps, and
    Unix day-of-month/day-of-week matching.
  • Event triggers use authority-defined types, actions, and schema-defined
    configuration discovered through listAutomationTriggerDefinitions.
  • execution.lifetime distinguishes interactive hostLifetime scheduling from
    independently managed authorities.
  • runAutomation.requestId is a durable idempotency key.

Cutover-safe imports

createAutomation.import carries a stable source, batch, and item identity.
It may also carry each schedule trigger's next unevaluated source occurrence.

  • Imported definitions must be created disabled.
  • The host persists supplied occurrences while the definition is disabled.
  • After the client removes the legacy scheduler-visible copy and enables the
    host definition, overdue work follows the trigger's misfire policy.
  • A host must durably claim a due occurrence with its run record before external
    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;
  • root catalogue notifications;
  • definition/run-summary reducer actions;
  • run lifecycle/session/artifact reducer actions;
  • client-dispatchable run cancellation.

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

  • Sharing is within one authority; AHP does not replicate independent hosts.
  • Event types remain authority-defined.
  • Run-history deletion is not part of this revision.
  • Local and managed/cloud execution can be represented by separate authorities.
  • Session-list promotion is not modeled here yet; immutable automation
    provenance must not be cleared to change client presentation.

Main merge

Merged current AHP main through d6bf47d2, preserving its scoped
customization-enablement model. Additive generated-client conflicts were
resolved by updating the Rust generator and regenerating every client/schema.

Validation

  • npm run generate
  • npm run test
  • cargo test --workspace under clients/rust
  • npm run docs:build
  • 100% reducer branch coverage
  • change-fragment and release-metadata verification

Companion implementation

Companion VS Code draft PR:
microsoft/vscode#330463

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
Comment thread types/channels-automation/state.ts Outdated
Comment thread types/channels-automation/state.ts Outdated
Comment thread docs/guide/automations.md Outdated
Comment thread types/channels-automation/state.ts
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
@ulugbekna
Ulugbek Abdullaev (ulugbekna) marked this pull request as ready for review August 12, 2026 19:50
Comment thread types/channels-automation/state.ts
Comment thread types/common/messages.ts Outdated
Comment thread types/channels-automation/reducer.ts Outdated
Comment thread clients/typescript/src/client/hosts/runtime.ts
Comment thread clients/typescript/test/client.test.ts Outdated
Comment thread scripts/generate-json-schema.test.ts
Comment thread types/channels-automation-run/state.ts
Comment thread docs/guide/automations.md
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
Comment thread types/channels-automation/state.ts
Comment thread types/channels-automation/state.ts Outdated
* Values described by {@link AutomationTriggerDefinition.configSchema}.
* Clients MUST preserve unknown entries when editing other fields.
*/
config?: Record<string, unknown>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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...

Comment thread types/channels-automation/state.ts Outdated
workingDirectories?: URI[];
/** Opaque host-defined runtime metadata. */
_meta?: Record<string, unknown>;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unclear on how this is used. Automations already have their own session.workingDirectories, how is this different?

Comment thread types/channels-automation/state.ts Outdated
Comment on lines +324 to +325
/** Monotonic definition revision used for optimistic concurrency. */
revision: number;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does not appear to actually be used

Comment thread types/channels-automation/commands.ts Outdated
Comment on lines +276 to +283
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;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Comment thread types/channels-automation-run/state.ts Outdated
*
* @category Automation Run State
*/
export type AutomationRunCause =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the notion of origin's already for message and chat provenance, I suggest we use the same terminology here

Suggested change
export type AutomationRunCause =
export type AutomationRunOrigin =

Comment thread types/channels-automation-run/state.ts Outdated
*
* @category Automation Run State
*/
export const enum AutomationRunOperation {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same Q on operations here -- is this a capability or open ended operation

Comment thread types/channels-automation-run/state.ts Outdated
* @category Automation Run State
*/
export interface AutomationRunArtifact extends ContentRef {
/** Stable artifact id within this run, used by artifact actions. */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I don't see any notion of artifact actions in this PR
  2. These should be unique already by ContentRef.uri, no need for another ID

Comment on lines +217 to +223
export type AutomationRunLifecycle =
| AutomationPendingRunLifecycle
| AutomationRunningRunLifecycle
| AutomationBlockedRunLifecycle
| AutomationCompletedRunLifecycle
| AutomationFailedRunLifecycle
| AutomationCancelledRunLifecycle;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread types/common/commands.ts
* {@link AutomationSchedule.expression}. Omission means no restriction beyond
* the cron format's one-minute resolution.
*/
minIntervalMinutes?: number;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread types/channels-automation/state.ts Outdated
Comment thread types/channels-automation/state.ts Outdated
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
@ulugbekna
Ulugbek Abdullaev (ulugbekna) merged commit e511d70 into microsoft:main Aug 19, 2026
8 checks passed
@ulugbekna
Ulugbek Abdullaev (ulugbekna) deleted the ulugbekna/shared-automations branch August 19, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants