Event API
AsyncAPI — streamed event surface
OpenWOP's event surface is documented as an AsyncAPI 3 document. Hosts
stream run.* events over SSE
and fan out to subscribers over signed
webhooks. The wire shape is normative; the AsyncAPI source below is
the same contract in machine-readable form.
asyncapi: 3.1.0
info:
title: Workflow Orchestration Protocol (openwop) SSE Event Stream
version: "1.1.0"
externalDocs:
description: openwop spec v1 corpus
url: https://openwop.dev/spec/v1/
description: |
Canonical AsyncAPI 3.1.0 specification for the openwop server's
Server-Sent Events surface. Formalizes `stream-modes.md`
and references the run-event JSON Schema via `$ref` so external SDK
authors can codegen typed consumers without re-reading the prose.
Four canonical stream modes are exposed via the `streamMode` query
parameter on a single endpoint (`GET /v1/runs/{runId}/events`):
- `updates` — minimal state-change deltas (default; lowest bandwidth)
- `values` — full `state.snapshot` after every transition
- `messages` — LLM token chunks for chat-style UIs
- `debug` — full event firehose including internal events
Each mode is modeled as a separate AsyncAPI channel because the
payload union differs per mode. The underlying transport (HTTPS SSE)
is shared; only the filter + synthesis layer differs.
See `stream-modes.md` for the complete event-to-mode mapping table.
contact:
name: openwop spec working group
url: https://openwop.dev/spec/v1/
license:
name: Apache-2.0
defaultContentType: text/event-stream
# ─────────────────────────────────────────────────────────────────────────────
# SERVERS
# ─────────────────────────────────────────────────────────────────────────────
servers:
production:
host: '{host}'
pathname: /v1
protocol: https
description: openwop-compliant server
variables:
host:
default: api.example.com
description: Replace with your server's hostname.
security:
- $ref: '#/components/securitySchemes/ApiKeyAuth'
# ─────────────────────────────────────────────────────────────────────────────
# CHANNELS — one per streamMode (filter contract differs)
# ─────────────────────────────────────────────────────────────────────────────
channels:
heartbeatEvents:
# Logical channel — `address: null` per AsyncAPI 3.x ("address not
# applicable / host-defined"). RFC 0094 §I: host-capabilities.md
# §host.heartbeat defines the two heartbeat events but documents NO
# HTTP delivery path for them (they are heartbeat-scoped, NOT
# run-event-log entries, so they do not ride /runs/{runId}/events
# either). The previous `/heartbeats/{heartbeatId}/events` address
# implied an undocumented REST surface; the delivery transport is a
# host concern until an RFC specifies one.
address: null
title: Heartbeat evaluation events (RFC 0060)
summary: Per-tick heartbeat evaluation + state-change notifications (logical channel).
description: |
RFC 0060 `host.heartbeat`. Heartbeat-scoped (NOT a run-event
stream): a host advertising `capabilities.heartbeat.supported: true`
emits `heartbeat.evaluated` every tick and `heartbeat.stateChanged`
only on a predicate-state transition. Both are observability-only;
consumers MAY ignore them.
LOGICAL channel: `host-capabilities.md` §host.heartbeat documents
the event shapes but no HTTP address; how a host delivers them
(webhook, host-internal bus, vendor stream) is host-defined.
messages:
heartbeatEvaluated: { $ref: '#/components/messages/HeartbeatEvaluated' }
heartbeatStateChanged: { $ref: '#/components/messages/HeartbeatStateChanged' }
runEventsUpdates:
address: /runs/{runId}/events
title: SSE — updates mode (default)
summary: Minimal state-change deltas for UI/CLI consumers.
description: |
Default consumption mode. Emits an SSE event for each terminal
node transition, suspension transition, run transition, and
artifact production. Payloads are deltas (the change since the
last event), NOT full snapshots.
Termination: server closes the connection on a terminal run
event (`run.completed`, `run.failed`, `run.cancelled`).
Selected via `?streamMode=updates` (or by omitting the query
parameter — `updates` is the default per `stream-modes.md`).
parameters:
runId:
$ref: '#/components/parameters/RunId'
messages:
runStarted: { $ref: '#/components/messages/RunStarted' }
runCompleted: { $ref: '#/components/messages/RunCompleted' }
runFailed: { $ref: '#/components/messages/RunFailed' }
runCancelled: { $ref: '#/components/messages/RunCancelled' }
runPaused: { $ref: '#/components/messages/RunPaused' }
runResumed: { $ref: '#/components/messages/RunResumed' }
runAnnotated: { $ref: '#/components/messages/RunAnnotated' }
workspaceUpdated: { $ref: '#/components/messages/WorkspaceUpdated' }
nodeCompleted: { $ref: '#/components/messages/NodeCompleted' }
nodeFailed: { $ref: '#/components/messages/NodeFailed' }
nodeSkipped: { $ref: '#/components/messages/NodeSkipped' }
nodeSuspended: { $ref: '#/components/messages/NodeSuspended' }
nodeDispatched: { $ref: '#/components/messages/NodeDispatched' }
approvalRequested: { $ref: '#/components/messages/ApprovalRequested' }
approvalReceived: { $ref: '#/components/messages/ApprovalReceived' }
clarificationRequested: { $ref: '#/components/messages/ClarificationRequested' }
clarificationResolved: { $ref: '#/components/messages/ClarificationResolved' }
interruptRequested: { $ref: '#/components/messages/InterruptRequested' }
interruptResolved: { $ref: '#/components/messages/InterruptResolved' }
artifactCreated: { $ref: '#/components/messages/ArtifactCreated' }
evalStarted: { $ref: '#/components/messages/EvalStarted' }
evalScored: { $ref: '#/components/messages/EvalScored' }
evalCompleted: { $ref: '#/components/messages/EvalCompleted' }
deploymentPromoted: { $ref: '#/components/messages/DeploymentPromoted' }
deploymentRolledBack: { $ref: '#/components/messages/DeploymentRolledBack' }
deploymentCanaryAdjusted: { $ref: '#/components/messages/DeploymentCanaryAdjusted' }
deploymentStateChanged: { $ref: '#/components/messages/DeploymentStateChanged' }
proposalCreated: { $ref: '#/components/messages/ProposalCreated' }
proposalActivated: { $ref: '#/components/messages/ProposalActivated' }
goalEvaluated: { $ref: '#/components/messages/GoalEvaluated' }
goalClosed: { $ref: '#/components/messages/GoalClosed' }
importApplied: { $ref: '#/components/messages/ImportApplied' }
runEventsValues:
address: /runs/{runId}/events
title: SSE — values mode
summary: Full state snapshots after every transition.
description: |
Higher-bandwidth mode for consumers that don't maintain their
own state machine. Emits a synthesized `state.snapshot` event
after each `updates`-tier transition. Payload is the complete
`ProjectedRunState` (status, nodeStates, variables,
currentNodeId, channels).
On resumption (`Last-Event-ID` header), the server MUST emit a
fresh `state.snapshot` first so the resuming client gets a
baseline before continuing with subsequent snapshots.
Selected via `?streamMode=values`.
parameters:
runId:
$ref: '#/components/parameters/RunId'
messages:
stateSnapshot: { $ref: '#/components/messages/StateSnapshot' }
runEventsMessages:
address: /runs/{runId}/events
title: SSE — messages mode
summary: LLM token chunks for chat-style UIs.
description: |
Per-token chunks from any AI node currently streaming
(`core.ai.callPrompt`, `core.ai.generateFromPrompt`, etc).
Other event types are filtered out — consumers wanting state
transitions should pair this with a separate `updates` stream.
If no AI nodes execute during the run, the stream is empty
until termination.
Selected via `?streamMode=messages`.
parameters:
runId:
$ref: '#/components/parameters/RunId'
messages:
aiMessageChunk: { $ref: '#/components/messages/AiMessageChunk' }
runEventsDebug:
address: /runs/{runId}/events
title: SSE — debug mode
summary: Full event firehose including internal events.
description: |
Every `RunEventDoc` from the durable event log, including
events filtered out of `updates`: `log.appended`,
`variable.changed`, `version.pinned`, `lease.*`,
`node.retried`, internal projection writes, and any
vendor-extension events.
Highest bandwidth. Used by replay tools, debuggers, and
conformance tests.
Selected via `?streamMode=debug`.
parameters:
runId:
$ref: '#/components/parameters/RunId'
messages:
anyRunEvent: { $ref: '#/components/messages/AnyRunEvent' }
runAnnotated: { $ref: '#/components/messages/RunAnnotated' }
workspaceUpdated: { $ref: '#/components/messages/WorkspaceUpdated' }
# ─────────────────────────────────────────────────────────────────────────────
# OPERATIONS — consumer-side (receive)
# ─────────────────────────────────────────────────────────────────────────────
operations:
subscribeUpdates:
action: receive
channel:
$ref: '#/channels/runEventsUpdates'
title: Subscribe to updates stream
summary: Receive minimal state-change events for a run.
description: |
Long-lived SSE subscription. Connection auto-closes on
terminal run event. Honor the `Last-Event-ID` request header
for resumption — server begins streaming from the sequence
AFTER the supplied ID and MUST NOT re-emit the resumption
point itself.
Mixed mode (RFC 0094 §I note): the binding's single-value
`streamMode` enum below describes THIS mode's pure subscription;
`streamMode` additionally accepts comma-separated combinations
(e.g. `updates,messages`) per `stream-modes.md` §"Mixed mode" —
union-of-filters semantics, per-event `event:` labels.
`values` MUST NOT combine with other modes.
bindings:
http:
method: GET
query:
type: object
properties:
streamMode:
type: string
enum: [updates]
default: updates
subscribeValues:
action: receive
channel:
$ref: '#/channels/runEventsValues'
title: Subscribe to values stream
summary: Receive full state snapshots after every transition.
description: |
Mixed mode (RFC 0094 §I note): `values` is EXCLUSIVE — it MUST NOT
be combined in a comma-separated `streamMode` list
(`stream-modes.md` §"Mixed mode": state.snapshot semantics need
exclusive ownership). The binding's single-value enum is exact here.
bindings:
http:
method: GET
query:
type: object
required: [streamMode]
properties:
streamMode:
type: string
enum: [values]
subscribeMessages:
action: receive
channel:
$ref: '#/channels/runEventsMessages'
title: Subscribe to messages stream
summary: Receive per-token AI chunks.
description: |
Mixed mode (RFC 0094 §I note): the binding's single-value
`streamMode` enum below describes the pure `messages` subscription;
`streamMode` additionally accepts comma-separated combinations
(e.g. `updates,messages`) per `stream-modes.md` §"Mixed mode".
`values` MUST NOT combine with other modes.
bindings:
http:
method: GET
query:
type: object
required: [streamMode]
properties:
streamMode:
type: string
enum: [messages]
subscribeDebug:
action: receive
channel:
$ref: '#/channels/runEventsDebug'
title: Subscribe to debug stream
summary: Receive every engine event including internal/log/lease.
description: |
Mixed mode (RFC 0094 §I note): the binding's single-value
`streamMode` enum below describes the pure `debug` subscription;
`streamMode` additionally accepts comma-separated combinations
(e.g. `updates,debug`) per `stream-modes.md` §"Mixed mode".
`values` MUST NOT combine with other modes.
bindings:
http:
method: GET
query:
type: object
required: [streamMode]
properties:
streamMode:
type: string
enum: [debug]
# ─────────────────────────────────────────────────────────────────────────────
# COMPONENTS
# ─────────────────────────────────────────────────────────────────────────────
components:
securitySchemes:
ApiKeyAuth:
type: httpApiKey
in: header
name: Authorization
description: |
Bearer-style API key. Format implementation-defined; reference
impl uses `hk_`/`hk_test_` prefixes. Required scopes:
`runs:read` to subscribe. See `auth.md`.
parameters:
RunId:
description: The run to subscribe to. Format opaque; clients MUST treat as a string.
# ── Messages ─────────────────────────────────────────────────────────────
# All `updates`/`debug`-mode messages share the canonical RunEventDoc shape
# (run-event.schema.json). Each named message below pins the `type` field
# to a specific RunEventType discriminator so codegens can emit narrowed
# consumer handlers.
messages:
# ── Agent evaluation (RFC 0081) — content-free recorded-fact events ───
EvalStarted:
name: eval.started
title: Eval run started (RFC 0081)
summary: An eval run began. Content-free — suite provenance + counts only. Gated on capabilities.agents.evalSuite.supported.
contentType: application/json
payload:
$ref: '#/components/schemas/EvalStartedPayload'
EvalScored:
name: eval.scored
title: Eval task scored (RFC 0081)
summary: One eval task was scored (emitted per task, after its terminal agent.decided). Content-free — taskId + score + scalars only, never task output (eval-summary-no-content-leak).
contentType: application/json
payload:
$ref: '#/components/schemas/EvalScoredPayload'
EvalCompleted:
name: eval.completed
title: Eval run completed (RFC 0081)
summary: An eval run finished. Content-free aggregate scalars; the full scorecard is the run output, read via GET /v1/runs/{runId}/eval-summary.
contentType: application/json
payload:
$ref: '#/components/schemas/EvalCompletedPayload'
# ── Agent deployment lifecycle (RFC 0082) — content-free audit events ─
DeploymentPromoted:
name: deployment.promoted
title: Deployment promoted (RFC 0082)
summary: A version was promoted into a new lifecycle state (gated by RFC 0049 deploy:* + RFC 0051 approvalGate + RFC 0081 requiredEval). Content-free (deployment-event-no-content-leak). Principal-stamped + audit-logged.
contentType: application/json
payload:
$ref: '#/components/schemas/DeploymentPromotedPayload'
DeploymentRolledBack:
name: deployment.rolled-back
title: Deployment rolled back (RFC 0082)
summary: An active version was rolled back and a prior version restored. Content-free; recorded-fact; audit-logged.
contentType: application/json
payload:
$ref: '#/components/schemas/DeploymentRolledBackPayload'
DeploymentCanaryAdjusted:
name: deployment.canary.adjusted
title: Deployment canary adjusted (RFC 0082)
summary: An active version's canary traffic share changed. Content-free; recorded-fact; audit-logged.
contentType: application/json
payload:
$ref: '#/components/schemas/DeploymentCanaryAdjustedPayload'
DeploymentStateChanged:
name: deployment.state.changed
title: Deployment state changed (RFC 0082)
summary: A non-promotion lifecycle transition (pause / resume / deprecate). Content-free; recorded-fact; audit-logged.
contentType: application/json
payload:
$ref: '#/components/schemas/DeploymentStateChangedPayload'
# ── Reviewable learning (RFC 0096) — content-free proposal lifecycle ──
ProposalCreated:
name: proposal.created
title: Proposal created (RFC 0096)
summary: The host synthesized a reviewable-learning draft. Content-free — ids/kind/refs only, never the artifact body or rationale (proposal-inert-until-applied). Emitted only when capabilities.agents.proposals is advertised.
contentType: application/json
payload:
$ref: '#/components/schemas/ProposalCreatedPayload'
ProposalActivated:
name: proposal.activated
title: Proposal activated (RFC 0096)
summary: A proposal was applied (RFC 0051/0049-gated). Content-free; the installed artifact byte-matches the last-persisted draft (proposal-no-resynthesis).
contentType: application/json
payload:
$ref: '#/components/schemas/ProposalActivatedPayload'
# ── Standing goals (RFC 0097) — content-free judge/continuation events ─
GoalEvaluated:
name: goal.evaluated
title: Goal evaluated (RFC 0097)
summary: A judge check ran against a standing goal. Content-free — no objective text; the verdict is recorded (not recomputed on replay).
contentType: application/json
payload:
$ref: '#/components/schemas/GoalEvaluatedPayload'
GoalClosed:
name: goal.closed
title: Goal closed (RFC 0097)
summary: A standing goal stopped continuation (satisfied / escalated / abandoned / bound-exceeded). Content-free.
contentType: application/json
payload:
$ref: '#/components/schemas/GoalClosedPayload'
# ── Portability (RFC 0098) — content-free import event ───────────────
ImportApplied:
name: import.applied
title: Import applied (RFC 0098)
summary: An estate import was applied. Content-free — counts + refs only, never item payloads or secret values (export-bundle-no-credential-material).
contentType: application/json
payload:
$ref: '#/components/schemas/ImportAppliedPayload'
# ── Run-lifecycle ────────────────────────────────────────────────────
RunStarted:
name: run.started
title: Run started
summary: A new run was registered and execution began.
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
RunCompleted:
name: run.completed
title: Run completed (terminal)
summary: Run reached terminal success state. SSE connection closes after this event.
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
RunFailed:
name: run.failed
title: Run failed (terminal)
summary: Run reached terminal failure state. SSE connection closes after this event.
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
RunCancelled:
name: run.cancelled
title: Run cancelled (terminal)
summary: Run was cancelled by user or admin. SSE connection closes after this event.
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
RunPaused:
name: run.paused
title: Run paused
summary: Run paused (e.g., capability limit reached, manual pause).
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
RunResumed:
name: run.resumed
title: Run resumed
summary: Run resumed from pause/suspend.
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
RunAnnotated:
name: run.annotated
title: Run annotated (RFC 0056)
summary: A non-blocking quality annotation was recorded for the run. Live notification ONLY — NOT a replayable run-event-log entry; its payload is an Annotation (not a RunEventDoc), so it is excluded from fork/replay (RFC 0056 §B/§D).
contentType: application/json
payload:
$ref: '#/components/schemas/Annotation'
WorkspaceUpdated:
name: workspace.updated
title: Workspace file updated (RFC 0059)
summary: A workspace file was created, replaced, or deleted via the host.workspace store. Content-free — carries the file path + resulting version only (the body is served by the read-side, SR-1-redacted). A replayable run-event-log entry (re-read from the log on replay, never regenerated); gated on capabilities.workspace.supported.
contentType: application/json
payload:
$ref: '#/components/schemas/WorkspaceUpdatedPayload'
# RFC 0060. Heartbeat-scoped observability events — NOT RunEventDocs,
# NOT replayable run-event-log entries. Emitted on the heartbeat channel.
HeartbeatEvaluated:
name: heartbeat.evaluated
title: Heartbeat evaluated (RFC 0060)
summary: A heartbeat predicate was evaluated this tick (status + changed flag). Heartbeat-scoped observability.
contentType: application/json
payload:
$ref: '#/components/schemas/HeartbeatEvaluated'
HeartbeatStateChanged:
name: heartbeat.stateChanged
title: Heartbeat state changed (RFC 0060)
summary: A heartbeat predicate's state transitioned; emitted ONLY on change (never on an unchanged tick). Heartbeat-scoped.
contentType: application/json
payload:
$ref: '#/components/schemas/HeartbeatStateChanged'
# ── Node-lifecycle ───────────────────────────────────────────────────
NodeCompleted:
name: node.completed
title: Node completed successfully
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
NodeFailed:
name: node.failed
title: Node failed
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
NodeSkipped:
name: node.skipped
title: Node skipped due to edge condition
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
NodeSuspended:
name: node.suspended
title: Node suspended (HITL or external-event wait)
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
NodeDispatched:
name: node.dispatched
title: core.dispatch spawned a child workflow (RFC 0007 §D + RFC 0022 §A)
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
# ── HITL ─────────────────────────────────────────────────────────────
ApprovalRequested:
name: approval.requested
title: Approval requested
summary: Engine emitted an approval interrupt awaiting user resolution.
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
ApprovalReceived:
name: approval.received
title: Approval received
summary: User resolved an approval interrupt (accept/reject/refine/edit).
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
ClarificationRequested:
name: clarification.requested
title: Clarification requested
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
ClarificationResolved:
name: clarification.resolved
title: Clarification resolved
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
InterruptRequested:
name: interrupt.requested
title: Interrupt requested (canonical HITL primitive)
summary: |
The discriminated-union form of the full `interrupt.md` kind union
(RFC 0094 §E): approval / clarification / external-event / custom /
conversation.start / conversation.exchange / conversation.close /
low-confidence.
Servers emitting `interrupt.requested` SHOULD also emit the legacy
kind-specific event (`approval.requested` etc) for backward compat
until consumers migrate.
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
InterruptResolved:
name: interrupt.resolved
title: Interrupt resolved
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
# ── Artifacts ────────────────────────────────────────────────────────
ArtifactCreated:
name: artifact.created
title: Artifact produced by a node
summary: A typed artifact (PRD, plan, theme, etc) was created and registered.
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
# ── Synthesized for `values` mode ────────────────────────────────────
StateSnapshot:
name: state.snapshot
title: Full projected run state
summary: |
Synthesized event emitted by the server in `values` mode after
each `updates`-tier transition. NOT a member of the canonical
`RunEventType` enum — this is a per-mode synthetic.
contentType: application/json
payload:
$ref: '#/components/schemas/StateSnapshotPayload'
# ── Synthesized for `messages` mode ──────────────────────────────────
AiMessageChunk:
name: ai.message.chunk
title: AI token chunk
summary: Per-token streaming chunk from a `core.ai.*` node.
contentType: application/json
payload:
$ref: '#/components/schemas/AiMessageChunkPayload'
# ── Catch-all for `debug` mode ───────────────────────────────────────
AnyRunEvent:
name: any
title: Any RunEventDoc
summary: |
Type-erased handler for `debug` mode — discriminate on the
`type` field per the `RunEventType` enum in the run-event
JSON Schema (the authoritative, exhaustive event list; the
named messages above are a curated `updates`-tier subset).
Includes events filtered out of `updates`: `log.appended`,
`variable.changed`, `version.pinned`, `lease.*`, `node.retried`,
`replay.diverged`, `connector.authorized`,
`connector.auth_expired` (RFC 0047), `authorization.decided`
(RFC 0049), `approval.granted` / `approval.rejected` /
`approval.overridden` (RFC 0051), etc.
contentType: application/json
payload:
$ref: '#/components/schemas/RunEventDoc'
# ── Schemas ────────────────────────────────────────────────────────────
schemas:
# The canonical persisted-event shape. Defined externally so the same
# contract is shared with REST event-poll responses (rest-endpoints.md
# `GET /v1/runs/{runId}/events/poll`) and offline replay tools.
RunEventDoc:
$ref: '../schemas/run-event.schema.json'
# RFC 0081 — eval event payloads.
EvalStartedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/evalStarted' }
EvalScoredPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/evalScored' }
EvalCompletedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/evalCompleted' }
# RFC 0082 — deployment event payloads.
DeploymentPromotedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/deploymentPromoted' }
DeploymentRolledBackPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/deploymentRolledBack' }
DeploymentCanaryAdjustedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/deploymentCanaryAdjusted' }
DeploymentStateChangedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/deploymentStateChanged' }
# RFC 0096 — reviewable-learning proposal event payloads.
ProposalCreatedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/proposalCreated' }
ProposalActivatedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/proposalActivated' }
# RFC 0097 — standing-goal event payloads.
GoalEvaluatedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/goalEvaluated' }
GoalClosedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/goalClosed' }
# RFC 0098 — portability import event payload.
ImportAppliedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/importApplied' }
# RFC 0056. The run.annotated notification carries an Annotation —
# NOT a RunEventDoc — because annotations are a side-resource, not
# replayable run-event-log entries (RFC 0056 §B/§D).
Annotation:
$ref: '../schemas/annotation.schema.json'
# RFC 0059. The workspace.updated event payload — content-free
# {path, version}. Definition lives at
# run-event-payloads.schema.json#$defs.workspaceUpdated so the SSE
# consumer + run-event log share one shape contract.
WorkspaceUpdatedPayload:
$ref: '../schemas/run-event-payloads.schema.json#/$defs/workspaceUpdated'
# RFC 0060 heartbeat events (heartbeat-scoped; see host-capabilities.md §host.heartbeat).
HeartbeatEvaluated:
$ref: '../schemas/heartbeat-evaluated.schema.json'
HeartbeatStateChanged:
$ref: '../schemas/heartbeat-state-changed.schema.json'
StateSnapshotPayload:
# S1 closure (2026-04-27): reuse the canonical RunSnapshot
# projection shape verbatim. Same type returned by
# `GET /v1/runs/{runId}` — consumers can swap polling for
# values-mode SSE without re-modeling state.
$ref: '../schemas/run-snapshot.schema.json'
AiMessageChunkPayload:
# S2 closure (2026-04-27) + RFC 0094 §D single-sourcing: the payload
# is the canonical `outputChunk` definition in
# run-event-payloads.schema.json — referenced (not hand-copied, the
# prior inline copy was one of the three drifting definitions) so the
# SSE consumer + run-event log share exactly one shape contract.
# Minimum compliant payload: {nodeId, runId, chunk, isLast} per
# stream-modes.md §messages; `meta` adds Tier 1 typed slots
# (finishReason / logprobs / toolCalls / model / usage) and a Tier 2
# provider-pass-through escape hatch (see #$defs/_chunkMeta).
$ref: '../schemas/run-event-payloads.schema.json#/$defs/outputChunk'