@openwop/openwop-conformance 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +241 -0
  3. package/api/asyncapi.yaml +481 -0
  4. package/api/openapi.yaml +830 -0
  5. package/api/redocly.yaml +8 -0
  6. package/coverage.md +80 -0
  7. package/dist/cli.js +161 -0
  8. package/fixtures/conformance-a2a-task-roundtrip.json +27 -0
  9. package/fixtures/conformance-agent-identity.json +27 -0
  10. package/fixtures/conformance-agent-low-confidence.json +29 -0
  11. package/fixtures/conformance-agent-memory-cross-tenant.json +28 -0
  12. package/fixtures/conformance-agent-memory-redaction.json +32 -0
  13. package/fixtures/conformance-agent-memory-roundtrip.json +32 -0
  14. package/fixtures/conformance-agent-memory-ttl.json +31 -0
  15. package/fixtures/conformance-agent-pack-export.json +26 -0
  16. package/fixtures/conformance-agent-pack-install.json +26 -0
  17. package/fixtures/conformance-agent-pack-provenance.json +31 -0
  18. package/fixtures/conformance-agent-reasoning.json +29 -0
  19. package/fixtures/conformance-approval.json +27 -0
  20. package/fixtures/conformance-cancellable.json +33 -0
  21. package/fixtures/conformance-cap-breach.json +27 -0
  22. package/fixtures/conformance-capability-missing.json +23 -0
  23. package/fixtures/conformance-channel-ttl.json +60 -0
  24. package/fixtures/conformance-clarification.json +30 -0
  25. package/fixtures/conformance-conversation-capability-negotiation.json +23 -0
  26. package/fixtures/conformance-conversation-lifecycle.json +32 -0
  27. package/fixtures/conformance-conversation-replay.json +33 -0
  28. package/fixtures/conformance-conversation-vs-clarification.json +26 -0
  29. package/fixtures/conformance-delay.json +33 -0
  30. package/fixtures/conformance-dispatch-loop.json +38 -0
  31. package/fixtures/conformance-failure.json +23 -0
  32. package/fixtures/conformance-idempotent.json +30 -0
  33. package/fixtures/conformance-identity.json +32 -0
  34. package/fixtures/conformance-interrupt-auth-required.json +28 -0
  35. package/fixtures/conformance-interrupt-external-event.json +33 -0
  36. package/fixtures/conformance-interrupt-parent-child-cancel-child.json +27 -0
  37. package/fixtures/conformance-interrupt-parent-child-cancel.json +26 -0
  38. package/fixtures/conformance-interrupt-quorum.json +30 -0
  39. package/fixtures/conformance-mcp-tool-roundtrip.json +32 -0
  40. package/fixtures/conformance-message-reducer.json +31 -0
  41. package/fixtures/conformance-multi-node.json +21 -0
  42. package/fixtures/conformance-noop.json +23 -0
  43. package/fixtures/conformance-orchestrator-dispatch.json +47 -0
  44. package/fixtures/conformance-orchestrator-low-confidence.json +41 -0
  45. package/fixtures/conformance-orchestrator-terminate.json +44 -0
  46. package/fixtures/conformance-stream-text.json +26 -0
  47. package/fixtures/conformance-subworkflow-child.json +21 -0
  48. package/fixtures/conformance-subworkflow-parent.json +49 -0
  49. package/fixtures/conformance-version-fold.json +23 -0
  50. package/fixtures/conformance-wasm-pack-roundtrip.json +25 -0
  51. package/fixtures/pack-manifests/pack-private-example.json +26 -0
  52. package/fixtures.md +404 -0
  53. package/package.json +48 -0
  54. package/schemas/README.md +75 -0
  55. package/schemas/agent-manifest.schema.json +107 -0
  56. package/schemas/agent-ref.schema.json +53 -0
  57. package/schemas/capabilities.schema.json +287 -0
  58. package/schemas/channel-written-payload.schema.json +55 -0
  59. package/schemas/conversation-event.schema.json +120 -0
  60. package/schemas/conversation-turn.schema.json +72 -0
  61. package/schemas/debug-bundle.schema.json +196 -0
  62. package/schemas/dispatch-config.schema.json +46 -0
  63. package/schemas/error-envelope.schema.json +25 -0
  64. package/schemas/memory-entry.schema.json +36 -0
  65. package/schemas/memory-list-options.schema.json +21 -0
  66. package/schemas/node-pack-manifest.schema.json +235 -0
  67. package/schemas/orchestrator-decision.schema.json +60 -0
  68. package/schemas/run-event-payloads.schema.json +663 -0
  69. package/schemas/run-event.schema.json +116 -0
  70. package/schemas/run-options.schema.json +81 -0
  71. package/schemas/run-orchestrator-decided-event.schema.json +20 -0
  72. package/schemas/run-snapshot.schema.json +121 -0
  73. package/schemas/suspend-request.schema.json +182 -0
  74. package/schemas/workflow-definition.schema.json +430 -0
  75. package/src/cli.ts +187 -0
  76. package/src/lib/a2a-fake-peer.ts +233 -0
  77. package/src/lib/canaries.ts +186 -0
  78. package/src/lib/driver.ts +96 -0
  79. package/src/lib/env.ts +49 -0
  80. package/src/lib/fixtures.ts +93 -0
  81. package/src/lib/mcp-fake-server.ts +185 -0
  82. package/src/lib/multi-agent-capabilities.ts +155 -0
  83. package/src/lib/multiProcess.ts +141 -0
  84. package/src/lib/otel-collector.ts +312 -0
  85. package/src/lib/paths.ts +198 -0
  86. package/src/lib/polling.ts +81 -0
  87. package/src/lib/profiles.ts +258 -0
  88. package/src/lib/sse.ts +172 -0
  89. package/src/scenarios/a2a-task-roundtrip.test.ts +149 -0
  90. package/src/scenarios/agentConfidenceEscalation.test.ts +61 -0
  91. package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +54 -0
  92. package/src/scenarios/agentMemoryRedactionContract.test.ts +46 -0
  93. package/src/scenarios/agentMemoryRoundTrip.test.ts +52 -0
  94. package/src/scenarios/agentMemoryTtlExpiry.test.ts +47 -0
  95. package/src/scenarios/agentMessageReducer.test.ts +57 -0
  96. package/src/scenarios/agentMetadata.test.ts +56 -0
  97. package/src/scenarios/agentPackExport.test.ts +45 -0
  98. package/src/scenarios/agentPackInstall.test.ts +50 -0
  99. package/src/scenarios/agentPackProvenance.test.ts +53 -0
  100. package/src/scenarios/agentReasoningEvents.test.ts +72 -0
  101. package/src/scenarios/append-ordering.test.ts +91 -0
  102. package/src/scenarios/approval-payload.test.ts +120 -0
  103. package/src/scenarios/audit-log-integrity.test.ts +106 -0
  104. package/src/scenarios/auth.test.ts +55 -0
  105. package/src/scenarios/byok-roundtrip.test.ts +166 -0
  106. package/src/scenarios/cancellation.test.ts +68 -0
  107. package/src/scenarios/cap-breach.test.ts +149 -0
  108. package/src/scenarios/channel-ttl.test.ts +70 -0
  109. package/src/scenarios/configurable-schema.test.ts +76 -0
  110. package/src/scenarios/conversationCapabilityNegotiation.test.ts +39 -0
  111. package/src/scenarios/conversationLifecycle.test.ts +64 -0
  112. package/src/scenarios/conversationReplayDeterminism.test.ts +52 -0
  113. package/src/scenarios/conversationVsLegacySuspend.test.ts +46 -0
  114. package/src/scenarios/cost-attribution.test.ts +207 -0
  115. package/src/scenarios/debugBundle.test.ts +222 -0
  116. package/src/scenarios/discovery.test.ts +147 -0
  117. package/src/scenarios/dispatchLoop.test.ts +52 -0
  118. package/src/scenarios/errors.test.ts +144 -0
  119. package/src/scenarios/eventOrdering.test.ts +144 -0
  120. package/src/scenarios/failure-path.test.ts +46 -0
  121. package/src/scenarios/fixtures-gating.test.ts +137 -0
  122. package/src/scenarios/fixtures-valid.test.ts +140 -0
  123. package/src/scenarios/highConcurrency.test.ts +263 -0
  124. package/src/scenarios/idempotency.test.ts +83 -0
  125. package/src/scenarios/idempotencyRetry.test.ts +130 -0
  126. package/src/scenarios/identity-passthrough.test.ts +54 -0
  127. package/src/scenarios/interrupt-approval.test.ts +97 -0
  128. package/src/scenarios/interrupt-auth-required-resume.test.ts +88 -0
  129. package/src/scenarios/interrupt-clarification.test.ts +45 -0
  130. package/src/scenarios/interrupt-external-event-correlation.test.ts +113 -0
  131. package/src/scenarios/interrupt-parent-child-cascade.test.ts +102 -0
  132. package/src/scenarios/interrupt-quorum-resolution.test.ts +97 -0
  133. package/src/scenarios/interruptRace.test.ts +176 -0
  134. package/src/scenarios/maliciousManifest.test.ts +154 -0
  135. package/src/scenarios/mcp-discoverability.test.ts +129 -0
  136. package/src/scenarios/mcp-tool-roundtrip.test.ts +149 -0
  137. package/src/scenarios/multi-node-ordering.test.ts +60 -0
  138. package/src/scenarios/multi-region-idempotency.test.ts +52 -0
  139. package/src/scenarios/orchestratorConservativePath.test.ts +63 -0
  140. package/src/scenarios/orchestratorDispatch.test.ts +66 -0
  141. package/src/scenarios/orchestratorTermination.test.ts +54 -0
  142. package/src/scenarios/otel-emission.test.ts +113 -0
  143. package/src/scenarios/otel-trace-propagation.test.ts +90 -0
  144. package/src/scenarios/pack-registry-publish.test.ts +93 -0
  145. package/src/scenarios/pack-registry.test.ts +328 -0
  146. package/src/scenarios/pause-resume.test.ts +109 -0
  147. package/src/scenarios/policies.test.ts +162 -0
  148. package/src/scenarios/profileDerivation.test.ts +335 -0
  149. package/src/scenarios/providerPolicyEnforcement.test.ts +132 -0
  150. package/src/scenarios/rate-limit-envelope.test.ts +97 -0
  151. package/src/scenarios/redaction.test.ts +254 -0
  152. package/src/scenarios/redactionAdversarial.test.ts +162 -0
  153. package/src/scenarios/replay-fork-arbitrary.test.ts +347 -0
  154. package/src/scenarios/replay-fork.test.ts +216 -0
  155. package/src/scenarios/replayDeterminism.test.ts +171 -0
  156. package/src/scenarios/route-coverage.test.ts +129 -0
  157. package/src/scenarios/runs-lifecycle.test.ts +65 -0
  158. package/src/scenarios/runtime-capabilities.test.ts +118 -0
  159. package/src/scenarios/spec-corpus-validity.test.ts +1257 -0
  160. package/src/scenarios/staleClaim.test.ts +223 -0
  161. package/src/scenarios/stream-modes-buffer.test.ts +148 -0
  162. package/src/scenarios/stream-modes-mixed.test.ts +149 -0
  163. package/src/scenarios/stream-modes.test.ts +139 -0
  164. package/src/scenarios/streamReconnect.test.ts +162 -0
  165. package/src/scenarios/subworkflow.test.ts +126 -0
  166. package/src/scenarios/version-negotiation.test.ts +157 -0
  167. package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +47 -0
  168. package/src/scenarios/wasm-pack-invoke-completed.test.ts +69 -0
  169. package/src/scenarios/wasm-pack-invoke-suspended.test.ts +74 -0
  170. package/src/scenarios/wasm-pack-load.test.ts +75 -0
  171. package/src/scenarios/wasm-pack-memory-cap.test.ts +43 -0
  172. package/src/scenarios/wasm-pack-replay-determinism.test.ts +61 -0
  173. package/src/scenarios/webhook-sig-algorithm.test.ts +61 -0
  174. package/src/setup.ts +173 -0
  175. package/vitest.config.ts +17 -0
@@ -0,0 +1,663 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/run-event-payloads.schema.json",
4
+ "title": "RunEventPayloads",
5
+ "description": "Per-RunEventType payload schemas. The base RunEventDoc shape (run-event.schema.json) leaves `payload` permissive for forward-compat. This schema defines the canonical payload contract for each known RunEventType. Consumers MAY pin strict payload validation via `$defs.<typeId>` and `ajv.validate(schema.$defs[event.type], event.payload)`. Unknown event types MUST be tolerated (no $defs match → fold best-effort).\n\n47 variants from `run-event.schema.json#$defs.RunEventType` are covered, grouped into ~20 shape families with shared $defs. Naming convention: camelCase keys mirror dotted RunEventType names (e.g., `run.started` → `runStarted`).",
6
+ "type": "object",
7
+ "$defs": {
8
+ "_typeIndex": {
9
+ "description": "Lookup map: RunEventType → $defs key. Informative — consumers can derive this themselves but having it explicit aids tooling.",
10
+ "type": "object",
11
+ "additionalProperties": false,
12
+ "properties": {
13
+ "run.started": { "$ref": "#/$defs/runStarted" },
14
+ "run.completed": { "$ref": "#/$defs/runCompleted" },
15
+ "run.failed": { "$ref": "#/$defs/runFailed" },
16
+ "run.cancelled": { "$ref": "#/$defs/runCancelled" },
17
+ "run.resuming": { "$ref": "#/$defs/runResuming" },
18
+ "run.paused": { "$ref": "#/$defs/runPaused" },
19
+ "run.resumed": { "$ref": "#/$defs/runResumed" },
20
+ "run.restored-from-snapshot":{ "$ref": "#/$defs/runRestoredFromSnapshot" },
21
+ "node.started": { "$ref": "#/$defs/nodeStarted" },
22
+ "node.completed": { "$ref": "#/$defs/nodeCompleted" },
23
+ "node.failed": { "$ref": "#/$defs/nodeFailed" },
24
+ "node.suspended": { "$ref": "#/$defs/nodeSuspended" },
25
+ "node.suspend-failed": { "$ref": "#/$defs/nodeSuspendFailed" },
26
+ "node.resumed": { "$ref": "#/$defs/nodeResumed" },
27
+ "node.retried": { "$ref": "#/$defs/nodeRetried" },
28
+ "node.skipped": { "$ref": "#/$defs/nodeSkipped" },
29
+ "node.cancelled": { "$ref": "#/$defs/nodeCancelled" },
30
+ "approval.requested": { "$ref": "#/$defs/approvalRequested" },
31
+ "approval.received": { "$ref": "#/$defs/approvalReceived" },
32
+ "clarification.requested": { "$ref": "#/$defs/clarificationRequested" },
33
+ "clarification.resolved": { "$ref": "#/$defs/clarificationResolved" },
34
+ "interrupt.requested": { "$ref": "#/$defs/interruptRequested" },
35
+ "interrupt.resolved": { "$ref": "#/$defs/interruptResolved" },
36
+ "channel.written": { "$ref": "#/$defs/channelWritten" },
37
+ "artifact.created": { "$ref": "#/$defs/artifactCreated" },
38
+ "output.chunk": { "$ref": "#/$defs/outputChunk" },
39
+ "variable.changed": { "$ref": "#/$defs/variableChanged" },
40
+ "log.appended": { "$ref": "#/$defs/logAppended" },
41
+ "version.pinned": { "$ref": "#/$defs/versionPinned" },
42
+ "workflow.restored": { "$ref": "#/$defs/workflowRestored" },
43
+ "workflow.loopback-limit": { "$ref": "#/$defs/workflowLoopbackLimit" },
44
+ "workflow.stalled": { "$ref": "#/$defs/workflowStalled" },
45
+ "cap.breached": { "$ref": "#/$defs/capBreached" },
46
+ "lease.acquired": { "$ref": "#/$defs/leaseLifecycle" },
47
+ "lease.renewed": { "$ref": "#/$defs/leaseLifecycle" },
48
+ "lease.lost": { "$ref": "#/$defs/leaseLifecycle" },
49
+ "lease.handed-off": { "$ref": "#/$defs/leaseHandedOff" },
50
+ "replay.diverged": { "$ref": "#/$defs/replayDiverged" },
51
+ "agent.reasoned": { "$ref": "#/$defs/agentReasoned" },
52
+ "agent.toolCalled": { "$ref": "#/$defs/agentToolCalled" },
53
+ "agent.toolReturned": { "$ref": "#/$defs/agentToolReturned" },
54
+ "agent.handoff": { "$ref": "#/$defs/agentHandoff" },
55
+ "agent.decided": { "$ref": "#/$defs/agentDecided" },
56
+ "runOrchestrator.decided": { "$ref": "#/$defs/runOrchestratorDecided" },
57
+ "conversation.opened": { "$ref": "#/$defs/conversationOpened" },
58
+ "conversation.exchanged": { "$ref": "#/$defs/conversationExchanged" },
59
+ "conversation.closed": { "$ref": "#/$defs/conversationClosed" }
60
+ }
61
+ },
62
+
63
+ "_errorObject": {
64
+ "description": "Reusable error shape used by run.failed / node.failed / workflow.stalled / etc.",
65
+ "type": "object",
66
+ "required": ["code", "message"],
67
+ "properties": {
68
+ "code": { "type": "string", "minLength": 1 },
69
+ "message": { "type": "string", "minLength": 1 },
70
+ "details": { "type": "object" },
71
+ "retryable": { "type": "boolean" }
72
+ },
73
+ "additionalProperties": true
74
+ },
75
+
76
+ "_inputsObject": {
77
+ "type": "object",
78
+ "description": "Caller-supplied workflow inputs (from POST /v1/runs body)."
79
+ },
80
+
81
+ "_outputsObject": {
82
+ "type": "object",
83
+ "description": "Node output map. Keys are output port names; values are JSON."
84
+ },
85
+
86
+ "runStarted": {
87
+ "type": "object",
88
+ "description": "Emitted once per run when execution begins.",
89
+ "required": ["workflowId"],
90
+ "properties": {
91
+ "workflowId": { "type": "string", "minLength": 1 },
92
+ "inputs": { "$ref": "#/$defs/_inputsObject" },
93
+ "transport": { "type": "string", "enum": ["rest", "mcp", "a2a", "ui"] },
94
+ "engineVersion": { "type": "string" },
95
+ "tags": { "type": "array", "items": { "type": "string" } },
96
+ "metadata": { "type": "object" }
97
+ },
98
+ "additionalProperties": true
99
+ },
100
+ "runCompleted": {
101
+ "type": "object",
102
+ "description": "Emitted once when the run reaches a successful terminal state. Closes the SSE stream.",
103
+ "properties": {
104
+ "outputs": { "$ref": "#/$defs/_outputsObject" },
105
+ "durationMs": { "type": "integer", "minimum": 0 }
106
+ },
107
+ "additionalProperties": true
108
+ },
109
+ "runFailed": {
110
+ "type": "object",
111
+ "description": "Emitted once when the run reaches a failed terminal state.",
112
+ "required": ["error"],
113
+ "properties": {
114
+ "error": { "$ref": "#/$defs/_errorObject" },
115
+ "failedNodeId": { "type": "string" },
116
+ "durationMs": { "type": "integer", "minimum": 0 }
117
+ },
118
+ "additionalProperties": true
119
+ },
120
+ "runCancelled": {
121
+ "type": "object",
122
+ "description": "Emitted once when the run reaches the cancelled terminal state.",
123
+ "properties": {
124
+ "reason": { "type": "string" },
125
+ "cancelledBy": { "type": "string", "description": "Caller identity (uid / API key fingerprint / `system`)." },
126
+ "durationMs": { "type": "integer", "minimum": 0 }
127
+ },
128
+ "additionalProperties": true
129
+ },
130
+ "runResuming": {
131
+ "type": "object",
132
+ "description": "Emitted when an explicit resume is dispatched (browser bootstrap or `:resume` callable).",
133
+ "properties": {
134
+ "fromStatus": { "type": "string" }
135
+ },
136
+ "additionalProperties": true
137
+ },
138
+ "runPaused": {
139
+ "type": "object",
140
+ "description": "Emitted when the run is paused (manual pause / cap.breached pause-and-checkpoint).",
141
+ "properties": {
142
+ "reason": { "type": "string" }
143
+ },
144
+ "additionalProperties": true
145
+ },
146
+ "runResumed": {
147
+ "type": "object",
148
+ "description": "Emitted when the run resumes from `paused`.",
149
+ "additionalProperties": true
150
+ },
151
+ "runRestoredFromSnapshot": {
152
+ "type": "object",
153
+ "description": "Emitted on cold-start when the engine rebuilds run state from event log.",
154
+ "properties": {
155
+ "snapshotSeq": { "type": "integer", "minimum": 0 },
156
+ "engineVersion": { "type": "string" }
157
+ },
158
+ "additionalProperties": true
159
+ },
160
+
161
+ "nodeStarted": {
162
+ "type": "object",
163
+ "description": "Emitted when a node begins execution.",
164
+ "required": ["nodeId", "typeId"],
165
+ "properties": {
166
+ "nodeId": { "type": "string", "minLength": 1 },
167
+ "typeId": { "type": "string", "minLength": 1 },
168
+ "attempt": { "type": "integer", "minimum": 0, "description": "Zero-based retry counter." }
169
+ },
170
+ "additionalProperties": true
171
+ },
172
+ "nodeCompleted": {
173
+ "type": "object",
174
+ "description": "Emitted when a node successfully completes.",
175
+ "required": ["nodeId"],
176
+ "properties": {
177
+ "nodeId": { "type": "string", "minLength": 1 },
178
+ "outputs": { "$ref": "#/$defs/_outputsObject" },
179
+ "durationMs": { "type": "integer", "minimum": 0 }
180
+ },
181
+ "additionalProperties": true
182
+ },
183
+ "nodeFailed": {
184
+ "type": "object",
185
+ "description": "Emitted on terminal node failure (after retries exhausted).",
186
+ "required": ["nodeId", "error"],
187
+ "properties": {
188
+ "nodeId": { "type": "string", "minLength": 1 },
189
+ "error": { "$ref": "#/$defs/_errorObject" },
190
+ "attempts": { "type": "integer", "minimum": 1 }
191
+ },
192
+ "additionalProperties": true
193
+ },
194
+ "nodeSuspended": {
195
+ "type": "object",
196
+ "description": "Emitted when a node calls `ctx.interrupt(...)`. Carries the InterruptPayload (suspend-request.schema.json).",
197
+ "required": ["nodeId", "interruptId"],
198
+ "properties": {
199
+ "nodeId": { "type": "string", "minLength": 1 },
200
+ "interruptId": { "type": "string", "minLength": 1 },
201
+ "kind": { "type": "string", "enum": ["approval", "clarification", "external-event", "custom"] },
202
+ "key": { "type": "string", "minLength": 1 }
203
+ },
204
+ "additionalProperties": true
205
+ },
206
+ "nodeSuspendFailed": {
207
+ "type": "object",
208
+ "description": "Emitted when a suspension attempt fails (storage write error, signed-token issue).",
209
+ "required": ["nodeId", "error"],
210
+ "properties": {
211
+ "nodeId": { "type": "string", "minLength": 1 },
212
+ "error": { "$ref": "#/$defs/_errorObject" }
213
+ },
214
+ "additionalProperties": true
215
+ },
216
+ "nodeResumed": {
217
+ "type": "object",
218
+ "description": "Emitted when a suspended node receives its resume value and continues.",
219
+ "required": ["nodeId"],
220
+ "properties": {
221
+ "nodeId": { "type": "string", "minLength": 1 },
222
+ "interruptId": { "type": "string" },
223
+ "resumeValue": {}
224
+ },
225
+ "additionalProperties": true
226
+ },
227
+ "nodeRetried": {
228
+ "type": "object",
229
+ "description": "Emitted on each retry attempt within a node's retry budget.",
230
+ "required": ["nodeId", "attempt"],
231
+ "properties": {
232
+ "nodeId": { "type": "string", "minLength": 1 },
233
+ "attempt": { "type": "integer", "minimum": 1 },
234
+ "delayMs": { "type": "integer", "minimum": 0 },
235
+ "lastError":{ "$ref": "#/$defs/_errorObject" }
236
+ },
237
+ "additionalProperties": true
238
+ },
239
+ "nodeSkipped": {
240
+ "type": "object",
241
+ "description": "Emitted when a node is skipped (edge condition failed, gate veto, conditional branch).",
242
+ "required": ["nodeId"],
243
+ "properties": {
244
+ "nodeId": { "type": "string", "minLength": 1 },
245
+ "reason": { "type": "string" }
246
+ },
247
+ "additionalProperties": true
248
+ },
249
+ "nodeCancelled": {
250
+ "type": "object",
251
+ "description": "Emitted when an in-flight node is cancelled (run-level cancel or upstream failure cascade).",
252
+ "required": ["nodeId"],
253
+ "properties": {
254
+ "nodeId": { "type": "string", "minLength": 1 },
255
+ "reason": { "type": "string" }
256
+ },
257
+ "additionalProperties": true
258
+ },
259
+
260
+ "approvalRequested": {
261
+ "type": "object",
262
+ "description": "Legacy kind-specific event (back-compat with pre-interrupt-primitive consumers). Modern servers SHOULD emit `interrupt.requested` with `kind: 'approval'` and MAY also emit this for back-compat per interrupt.md §migration.",
263
+ "required": ["nodeId", "artifactId", "artifactType", "actions"],
264
+ "properties": {
265
+ "nodeId": { "type": "string" },
266
+ "interruptId": { "type": "string" },
267
+ "artifactId": { "type": "string" },
268
+ "artifactType": { "type": "string" },
269
+ "title": { "type": "string" },
270
+ "actions": {
271
+ "type": "array",
272
+ "items": { "type": "string", "enum": ["accept", "reject", "refine", "edit", "ask"] },
273
+ "minItems": 1
274
+ },
275
+ "approversList": { "type": "array", "items": { "type": "string" } },
276
+ "requiredApprovals": { "type": "integer", "minimum": 1 }
277
+ },
278
+ "additionalProperties": true
279
+ },
280
+ "approvalReceived": {
281
+ "type": "object",
282
+ "description": "Emitted when an approval action is recorded (accept/reject/refine/edit-accept/timeout). The `ask` action does NOT emit this — Q&A exchanges have their own log. See `interrupt.md` §`ApprovalResume` for the action vocabulary + the host-side enforcement boundary.",
283
+ "required": ["nodeId", "action"],
284
+ "properties": {
285
+ "nodeId": { "type": "string" },
286
+ "action": { "type": "string", "enum": ["accept", "reject", "refine", "edit-accept", "timeout"] },
287
+ "decidedBy": { "type": "string", "description": "Host-defined opaque principal identifier. Hosts MUST populate this for non-timeout actions per `interrupt.md` §`Host-side enforcement boundary`." },
288
+ "decidedAt": { "type": "string", "description": "ISO 8601 timestamp at decision time." },
289
+ "comment": { "type": "string" },
290
+ "feedback": { "type": "string", "description": "Legacy free-text feedback. New hosts SHOULD use `refineFeedback` for structured refine-action feedback." },
291
+ "refineFeedback": {
292
+ "type": "object",
293
+ "description": "Structured feedback object for `action: 'refine'`. See `interrupt.md` §`RefineFeedback`.",
294
+ "properties": {
295
+ "scope": { "type": "string", "enum": ["whole", "section", "items"] },
296
+ "sectionPath": { "type": "string" },
297
+ "itemIds": { "type": "array", "items": { "type": "string" } },
298
+ "tags": { "type": "array", "items": { "type": "string" } },
299
+ "text": { "type": "string" }
300
+ },
301
+ "required": ["scope"],
302
+ "additionalProperties": true
303
+ },
304
+ "editedArtifactData": { "description": "User-edited artifact bytes when `action: 'edit-accept'`." }
305
+ },
306
+ "additionalProperties": true
307
+ },
308
+ "clarificationRequested": {
309
+ "type": "object",
310
+ "description": "Legacy kind-specific event for HITL clarification requests.",
311
+ "required": ["nodeId", "questions"],
312
+ "properties": {
313
+ "nodeId": { "type": "string" },
314
+ "interruptId": { "type": "string" },
315
+ "questions": {
316
+ "type": "array",
317
+ "items": {
318
+ "type": "object",
319
+ "required": ["id", "question"],
320
+ "properties": {
321
+ "id": { "type": "string" },
322
+ "question": { "type": "string" },
323
+ "schema": { "type": "object" }
324
+ },
325
+ "additionalProperties": true
326
+ }
327
+ }
328
+ },
329
+ "additionalProperties": true
330
+ },
331
+ "clarificationResolved": {
332
+ "type": "object",
333
+ "description": "Emitted when clarification answers are received.",
334
+ "required": ["nodeId", "answers"],
335
+ "properties": {
336
+ "nodeId": { "type": "string" },
337
+ "answers": { "type": "object" },
338
+ "answeredBy": { "type": "string" }
339
+ },
340
+ "additionalProperties": true
341
+ },
342
+ "interruptRequested": {
343
+ "description": "Canonical HITL primitive — discriminated union over kind. Payload mirrors `suspend-request.schema.json` (InterruptPayload) plus engine-assigned identity fields.",
344
+ "$ref": "https://openwop.dev/spec/v1/suspend-request.schema.json"
345
+ },
346
+ "interruptResolved": {
347
+ "type": "object",
348
+ "description": "Emitted when an interrupt is resolved (any kind).",
349
+ "required": ["nodeId", "interruptId"],
350
+ "properties": {
351
+ "nodeId": { "type": "string" },
352
+ "interruptId": { "type": "string" },
353
+ "kind": { "type": "string", "enum": ["approval", "clarification", "external-event", "custom"] },
354
+ "resumeValue": {}
355
+ },
356
+ "additionalProperties": true
357
+ },
358
+
359
+ "channelWritten": {
360
+ "$ref": "https://openwop.dev/spec/v1/channel-written-payload.schema.json"
361
+ },
362
+
363
+ "artifactCreated": {
364
+ "type": "object",
365
+ "description": "Emitted when a node produces a typed artifact (PRD, theme, plan, etc.).",
366
+ "required": ["artifactId", "artifactType"],
367
+ "properties": {
368
+ "artifactId": { "type": "string", "minLength": 1 },
369
+ "artifactType": { "type": "string", "minLength": 1 },
370
+ "nodeId": { "type": "string" },
371
+ "version": { "type": "string" },
372
+ "summary": { "type": "string" }
373
+ },
374
+ "additionalProperties": true
375
+ },
376
+
377
+ "outputChunk": {
378
+ "type": "object",
379
+ "description": "Emitted for streaming output (e.g., LLM token chunks). Stream-mode `messages` consumers see these. Tiered metadata per stream-modes.md §messages (S2 closure): bare {nodeId, chunk} is the minimum compliant payload; `meta` adds Tier 1 typed slots and a Tier 2 provider-pass-through escape hatch.",
380
+ "required": ["nodeId", "chunk"],
381
+ "properties": {
382
+ "nodeId": { "type": "string", "minLength": 1 },
383
+ "chunk": { "type": "string" },
384
+ "isLast": { "type": "boolean" },
385
+ "channel": { "type": "string", "description": "Optional sub-stream identifier when a node emits multiple parallel streams." },
386
+ "meta": { "$ref": "#/$defs/_chunkMeta" }
387
+ },
388
+ "additionalProperties": true
389
+ },
390
+
391
+ "_chunkMeta": {
392
+ "type": "object",
393
+ "description": "Tiered metadata for ai.message.chunk / outputChunk payloads. See stream-modes.md §messages.",
394
+ "properties": {
395
+ "finishReason": {
396
+ "type": "string",
397
+ "enum": ["stop", "length", "tool_calls", "content_filter"],
398
+ "description": "Tier 1: normalized termination reason. Set on the final chunk of a generation. Forward-compat: future values MAY be added; consumers MUST tolerate unknown values."
399
+ },
400
+ "logprobs": {
401
+ "type": "array",
402
+ "description": "Tier 1: per-token log-probability info. Shape mirrors OpenAI / Anthropic / Gemini conventions; servers SHOULD normalize to `[{token: string, logprob: number, topLogprobs?: [{token, logprob}]}]`."
403
+ },
404
+ "toolCalls": {
405
+ "type": "array",
406
+ "description": "Tier 1: structured tool / function calls emitted in this chunk. Each item: `{id, name, arguments}` — `arguments` is a string (JSON-encoded args, may be partial when streaming)."
407
+ },
408
+ "model": {
409
+ "type": "string",
410
+ "description": "Tier 1: model identifier the chunk was produced by (e.g., `claude-opus-4-7`, `gpt-5`). Useful when a workflow node fans out across multiple models."
411
+ },
412
+ "usage": {
413
+ "type": "object",
414
+ "description": "Tier 1: token-billing metadata. Pairs with O4 cost-attribution attributes (`openwop.cost.tokens.*`).",
415
+ "properties": {
416
+ "promptTokens": { "type": "integer", "minimum": 0 },
417
+ "completionTokens": { "type": "integer", "minimum": 0 },
418
+ "totalTokens": { "type": "integer", "minimum": 0 }
419
+ },
420
+ "additionalProperties": true
421
+ },
422
+ "provider": {
423
+ "type": "string",
424
+ "description": "Tier 2: provider name when `providerExtensions` is present. Examples: `openai`, `anthropic`, `google`, `minimax`. Consumers MUST NOT branch on this for behavior — use Tier 1 fields. Provided for observability + debugging only."
425
+ },
426
+ "providerExtensions": {
427
+ "type": "object",
428
+ "description": "Tier 2: provider pass-through for fields the spec hasn't typed yet. Consumers using this opt into per-provider knowledge; spec evolution may move fields up into Tier 1 over time (additive — typed slot wins)."
429
+ }
430
+ },
431
+ "additionalProperties": false
432
+ },
433
+
434
+ "variableChanged": {
435
+ "type": "object",
436
+ "description": "Emitted on workflow-variable mutation (debug/values stream modes). NOT emitted for typed channels (those use channel.written).",
437
+ "required": ["name"],
438
+ "properties": {
439
+ "name": { "type": "string", "minLength": 1 },
440
+ "previous": {},
441
+ "next": {},
442
+ "nodeId": { "type": "string" }
443
+ },
444
+ "additionalProperties": true
445
+ },
446
+
447
+ "logAppended": {
448
+ "type": "object",
449
+ "description": "Emitted on `ctx.log.*` calls. Debug-stream-mode only.",
450
+ "required": ["level", "message"],
451
+ "properties": {
452
+ "level": { "type": "string", "enum": ["debug", "info", "warn", "error"] },
453
+ "message": { "type": "string" },
454
+ "nodeId": { "type": "string" },
455
+ "fields": { "type": "object" }
456
+ },
457
+ "additionalProperties": true
458
+ },
459
+
460
+ "versionPinned": {
461
+ "type": "object",
462
+ "description": "Emitted via `ctx.getVersion(changeId)` per Temporal-style versioning. See version-negotiation.md.",
463
+ "required": ["changeId", "version"],
464
+ "properties": {
465
+ "changeId": { "type": "string", "minLength": 1 },
466
+ "version": { "type": "integer", "minimum": 0 },
467
+ "nodeId": { "type": "string" }
468
+ },
469
+ "additionalProperties": true
470
+ },
471
+
472
+ "workflowRestored": {
473
+ "type": "object",
474
+ "description": "Emitted when an in-flight run is recovered from the event log on a fresh engine boot.",
475
+ "properties": {
476
+ "fromSnapshotSeq": { "type": "integer", "minimum": 0 },
477
+ "engineVersion": { "type": "string" }
478
+ },
479
+ "additionalProperties": true
480
+ },
481
+ "workflowLoopbackLimit": {
482
+ "type": "object",
483
+ "description": "Emitted when a request-changes loop hits its `maxLoopbackIterations` cap.",
484
+ "required": ["nodeId", "iterations"],
485
+ "properties": {
486
+ "nodeId": { "type": "string" },
487
+ "iterations": { "type": "integer", "minimum": 1 },
488
+ "limit": { "type": "integer", "minimum": 1 }
489
+ },
490
+ "additionalProperties": true
491
+ },
492
+ "workflowStalled": {
493
+ "type": "object",
494
+ "description": "Emitted when the engine detects a stalled run (no progress for N seconds).",
495
+ "properties": {
496
+ "stalledForMs": { "type": "integer", "minimum": 0 },
497
+ "lastNodeId": { "type": "string" }
498
+ },
499
+ "additionalProperties": true
500
+ },
501
+
502
+ "capBreached": {
503
+ "type": "object",
504
+ "description": "Emitted when a CapabilityLimit is exceeded (clarificationRounds / schemaRounds / envelopesPerTurn / maxNodeExecutions).",
505
+ "required": ["kind", "limit", "observed"],
506
+ "properties": {
507
+ "kind": { "type": "string", "enum": ["clarification", "schema", "envelopes", "node-executions"] },
508
+ "limit": { "type": "integer", "minimum": 0 },
509
+ "observed": { "type": "integer", "minimum": 0 },
510
+ "nodeId": { "type": "string" }
511
+ },
512
+ "additionalProperties": true
513
+ },
514
+
515
+ "leaseLifecycle": {
516
+ "type": "object",
517
+ "description": "Shared payload for lease.acquired / lease.renewed / lease.lost.",
518
+ "required": ["leaseId", "host"],
519
+ "properties": {
520
+ "leaseId": { "type": "string", "minLength": 1 },
521
+ "host": { "type": "string", "enum": ["browser", "cloud"] },
522
+ "instanceId": { "type": "string" },
523
+ "expiresAt": { "type": "string", "format": "date-time" },
524
+ "previousHost": { "type": "string", "enum": ["browser", "cloud"] }
525
+ },
526
+ "additionalProperties": true
527
+ },
528
+ "leaseHandedOff": {
529
+ "type": "object",
530
+ "description": "Emitted when a run's executing lease is transferred from one host to another (e.g., browser → cloud failover).",
531
+ "required": ["leaseId", "fromHost", "toHost"],
532
+ "properties": {
533
+ "leaseId": { "type": "string", "minLength": 1 },
534
+ "fromHost": { "type": "string", "enum": ["browser", "cloud"] },
535
+ "toHost": { "type": "string", "enum": ["browser", "cloud"] },
536
+ "reason": { "type": "string" }
537
+ },
538
+ "additionalProperties": true
539
+ },
540
+
541
+ "replayDiverged": {
542
+ "type": "object",
543
+ "description": "Emitted by `:fork` when a replay re-execution produces a different output than the original at a given sequence. See replay.md §divergence detection.",
544
+ "required": ["sourceRunId", "atSequence"],
545
+ "properties": {
546
+ "sourceRunId": { "type": "string", "minLength": 1 },
547
+ "atSequence": { "type": "integer", "minimum": 0 },
548
+ "originalEventId": { "type": "string" },
549
+ "divergenceKind": { "type": "string", "enum": ["output", "missing", "extra", "type-mismatch"] }
550
+ },
551
+ "additionalProperties": true
552
+ },
553
+
554
+ "agentReasoned": {
555
+ "type": "object",
556
+ "description": "Multi-Agent Shift Phase 1. Emitted when an agent produces a reasoning trace (typically pre-tool-call, pre-decision, or pre-handoff). The reasoning text MAY be a summary or full trace depending on the run's resolved `RunOptions.configurable.reasoningVerbosity`. Replay-deterministic over the event log.",
557
+ "required": ["agentId", "reasoning"],
558
+ "properties": {
559
+ "agentId": { "type": "string", "minLength": 3, "maxLength": 256, "description": "AgentRef.agentId of the reasoning agent." },
560
+ "reasoning": { "type": "string", "description": "Reasoning text. Bounded by `capabilities.agents.reasoning.tokenLimit` when verbosity is `summary` (default 512 tokens)." },
561
+ "verbosity": { "type": "string", "enum": ["summary", "full", "off"], "description": "Verbosity mode this trace was produced under. Hosts MAY emit `'off'` to record the suppression decision without payload content." }
562
+ },
563
+ "additionalProperties": true
564
+ },
565
+
566
+ "agentToolCalled": {
567
+ "type": "object",
568
+ "description": "Multi-Agent Shift Phase 1. Emitted when an agent invokes a tool. Pairs with `agent.toolReturned` via shared `callId`.",
569
+ "required": ["agentId", "toolName", "callId"],
570
+ "properties": {
571
+ "agentId": { "type": "string", "minLength": 3, "maxLength": 256 },
572
+ "toolName": { "type": "string", "minLength": 1, "description": "Tool identifier (typically a node-pack typeId or function name)." },
573
+ "callId": { "type": "string", "minLength": 1, "description": "Unique correlation id linking this call to the subsequent `agent.toolReturned`. Hosts SHOULD use UUIDs or content-addressable hashes." },
574
+ "inputs": { "description": "Tool inputs. Shape is tool-specific; consumers MUST NOT assume an object." }
575
+ },
576
+ "additionalProperties": true
577
+ },
578
+
579
+ "agentToolReturned": {
580
+ "type": "object",
581
+ "description": "Multi-Agent Shift Phase 1. Emitted when a tool invocation completes (success or failure). Pairs with `agent.toolCalled` via `callId`.",
582
+ "required": ["agentId", "toolName", "callId"],
583
+ "properties": {
584
+ "agentId": { "type": "string", "minLength": 3, "maxLength": 256 },
585
+ "toolName": { "type": "string", "minLength": 1 },
586
+ "callId": { "type": "string", "minLength": 1 },
587
+ "outcome": { "description": "Tool result. Discriminated by host on success vs error; payload shape is tool-specific." },
588
+ "error": { "$ref": "#/$defs/_errorObject", "description": "Set on tool-execution failure. Mutually exclusive with `outcome`." }
589
+ },
590
+ "additionalProperties": true
591
+ },
592
+
593
+ "agentHandoff": {
594
+ "type": "object",
595
+ "description": "Multi-Agent Shift Phase 1. Emitted when control transfers from one agent to another mid-run (e.g., orchestrator → worker, worker → worker via `core.dispatch`). Pairs with the receiving agent's subsequent `agent.decided` / `agent.toolCalled` / `node.started`.",
596
+ "required": ["fromAgentId", "toAgentId"],
597
+ "properties": {
598
+ "fromAgentId": { "type": "string", "minLength": 3, "maxLength": 256, "description": "Agent surrendering control." },
599
+ "toAgentId": { "type": "string", "minLength": 3, "maxLength": 256, "description": "Agent receiving control." },
600
+ "reason": { "type": "string", "description": "Optional handoff rationale (e.g., 'specialist routing', 'escalation', 'dispatch-loop-iteration')." }
601
+ },
602
+ "additionalProperties": true
603
+ },
604
+
605
+ "agentDecided": {
606
+ "type": "object",
607
+ "description": "Multi-Agent Shift Phase 1. Emitted when an agent produces a typed decision (routing, classification, judgement). Carries optional `confidence` in `[0, 1]`; when below the run's resolved `escalationThreshold` (default 0.7), hosts MUST suspend with `node.suspended { reason: 'low-confidence' }` per the confidence-escalation contract.",
608
+ "required": ["agentId", "decision"],
609
+ "properties": {
610
+ "agentId": { "type": "string", "minLength": 3, "maxLength": 256 },
611
+ "decision": { "description": "Typed decision payload. Shape is decision-specific; consumers MUST NOT assume an object." },
612
+ "confidence": { "type": "number", "minimum": 0, "maximum": 1, "description": "Optional confidence in `[0, 1]`. Below the threshold → escalate via `node.suspended { reason: 'low-confidence' }`. Absent values are treated as 'no signal' — no escalation." }
613
+ },
614
+ "additionalProperties": true
615
+ },
616
+
617
+ "runOrchestratorDecided": {
618
+ "type": "object",
619
+ "description": "Multi-Agent Shift Phase 5. Emitted exactly once per orchestrator decision by a `core.orchestrator.supervisor` node (or host-extension equivalent). Carries the deciding agent's identity and the typed `OrchestratorDecision` (see `orchestrator-decision.schema.json`). The envelope's top-level `nodeId` carries the supervisor node-id; the payload does NOT duplicate it.",
620
+ "required": ["agentId", "decision"],
621
+ "properties": {
622
+ "agentId": { "type": "string", "minLength": 3, "maxLength": 256, "description": "AgentRef.agentId of the orchestrator. MUST equal `RunSnapshot.runOrchestrator.agentId` for the run's lifetime — orchestrator identity is set at first decision and does not change." },
623
+ "decision": { "$ref": "orchestrator-decision.schema.json" }
624
+ },
625
+ "additionalProperties": false
626
+ },
627
+
628
+ "conversationOpened": {
629
+ "type": "object",
630
+ "description": "Multi-Agent Shift Phase 4. Emitted when a `core.conversationGate` (or host-extension equivalent) opens a new multi-turn conversation context. Pairs with the closing `conversation.closed` via shared `conversationId`.",
631
+ "required": ["conversationId"],
632
+ "properties": {
633
+ "conversationId": { "type": "string", "minLength": 1, "maxLength": 256, "description": "Opaque conversation identifier. Tenant-unique invariant: hosts MUST ensure no two distinct conversations in the same tenant share an id. Cross-host portability not normative." },
634
+ "openedBy": { "type": "string", "description": "Optional `AgentRef.agentId` of the agent that opened the conversation." }
635
+ },
636
+ "additionalProperties": true
637
+ },
638
+
639
+ "conversationExchanged": {
640
+ "type": "object",
641
+ "description": "Multi-Agent Shift Phase 4. Emitted when a single turn completes within an open conversation (`core.conversationGate.exchange`). Carries the validated outcome of the suspend/resume cycle.",
642
+ "required": ["conversationId", "turnIndex"],
643
+ "properties": {
644
+ "conversationId": { "type": "string", "minLength": 1, "maxLength": 256 },
645
+ "turnIndex": { "type": "integer", "minimum": 0, "description": "0-indexed turn within this conversation. Strictly monotonic." },
646
+ "outcome": { "description": "Validated turn outcome per the per-turn schema declared in `core.conversationGate` config." }
647
+ },
648
+ "additionalProperties": true
649
+ },
650
+
651
+ "conversationClosed": {
652
+ "type": "object",
653
+ "description": "Multi-Agent Shift Phase 4. Emitted when a conversation context terminates (`core.conversationGate.close`). Final event in the conversation's lifecycle; no further `conversation.exchanged` events MAY follow for the same `conversationId` in this run.",
654
+ "required": ["conversationId"],
655
+ "properties": {
656
+ "conversationId": { "type": "string", "minLength": 1, "maxLength": 256 },
657
+ "reason": { "type": "string", "description": "Optional close reason (e.g., 'goal-reached', 'max-turns', 'user-cancelled')." },
658
+ "turnCount": { "type": "integer", "minimum": 0, "description": "Total turns completed in this conversation (number of `conversation.exchanged` events emitted)." }
659
+ },
660
+ "additionalProperties": true
661
+ }
662
+ }
663
+ }