@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,196 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/debug-bundle.schema.json",
4
+ "title": "RunDebugBundle",
5
+ "description": "Portable JSON export of a single run's diagnostic state. See spec/v1/debug-bundle.md. Additive over v1 per COMPATIBILITY.md §2.1: optional endpoint; hosts MAY omit.",
6
+ "type": "object",
7
+ "required": [
8
+ "bundleVersion",
9
+ "generatedAt",
10
+ "host",
11
+ "run",
12
+ "events",
13
+ "redactionApplied",
14
+ "redactionMode"
15
+ ],
16
+ "properties": {
17
+ "bundleVersion": {
18
+ "type": "string",
19
+ "description": "Schema version of the bundle. v1.x bundles are '1'.",
20
+ "pattern": "^[0-9]+\\.[0-9]+$"
21
+ },
22
+ "generatedAt": {
23
+ "type": "string",
24
+ "format": "date-time",
25
+ "description": "ISO 8601 timestamp when the host generated this response."
26
+ },
27
+ "host": {
28
+ "type": "object",
29
+ "description": "Identifies the host that produced the bundle.",
30
+ "required": ["name", "version"],
31
+ "properties": {
32
+ "name": { "type": "string", "minLength": 1 },
33
+ "version": { "type": "string", "minLength": 1 },
34
+ "vendor": { "type": "string" }
35
+ },
36
+ "additionalProperties": false
37
+ },
38
+ "run": {
39
+ "type": "object",
40
+ "description": "Run snapshot — same shape as GET /v1/runs/{runId}. See run-snapshot.schema.json.",
41
+ "required": ["runId", "workflowId", "status"],
42
+ "properties": {
43
+ "runId": { "type": "string", "minLength": 1 },
44
+ "workflowId": { "type": "string", "minLength": 1 },
45
+ "status": {
46
+ "type": "string",
47
+ "enum": [
48
+ "pending",
49
+ "running",
50
+ "completed",
51
+ "failed",
52
+ "cancelled",
53
+ "waiting-approval",
54
+ "waiting-clarification"
55
+ ]
56
+ },
57
+ "startedAt": { "type": "string", "format": "date-time" },
58
+ "endedAt": {
59
+ "oneOf": [
60
+ { "type": "string", "format": "date-time" },
61
+ { "type": "null" }
62
+ ]
63
+ },
64
+ "error": {
65
+ "oneOf": [
66
+ { "type": "null" },
67
+ {
68
+ "type": "object",
69
+ "required": ["code"],
70
+ "properties": {
71
+ "code": { "type": "string", "minLength": 1 },
72
+ "message": { "type": "string" }
73
+ },
74
+ "additionalProperties": true
75
+ }
76
+ ]
77
+ },
78
+ "inputs": { "type": "object" },
79
+ "variables": { "type": "object" },
80
+ "currentNodeId": {
81
+ "oneOf": [{ "type": "string" }, { "type": "null" }]
82
+ },
83
+ "nodeStates": { "type": "object" },
84
+ "metrics": { "type": "object" }
85
+ },
86
+ "additionalProperties": true
87
+ },
88
+ "events": {
89
+ "type": "array",
90
+ "description": "Full event log for this run, in order. Each entry follows run-event.schema.json.",
91
+ "items": {
92
+ "type": "object",
93
+ "properties": {
94
+ "sequence": {
95
+ "oneOf": [
96
+ { "type": "integer", "minimum": 0 },
97
+ { "type": "null" }
98
+ ]
99
+ },
100
+ "seq": {
101
+ "oneOf": [
102
+ { "type": "integer", "minimum": 0 },
103
+ { "type": "null" }
104
+ ],
105
+ "description": "Legacy alias for `sequence`. Hosts that pre-date the rename emit `seq`. Clients accept either."
106
+ },
107
+ "type": { "type": "string", "minLength": 1 },
108
+ "timestamp": { "type": "string", "format": "date-time" },
109
+ "nodeId": {
110
+ "oneOf": [{ "type": "string" }, { "type": "null" }]
111
+ },
112
+ "data": {}
113
+ },
114
+ "additionalProperties": true
115
+ }
116
+ },
117
+ "spans": {
118
+ "type": "array",
119
+ "description": "OTel-format spans per spec/v1/observability.md. Empty array if host doesn't emit spans.",
120
+ "items": {
121
+ "type": "object",
122
+ "required": ["name"],
123
+ "properties": {
124
+ "name": { "type": "string", "minLength": 1 },
125
+ "spanId": { "type": "string" },
126
+ "parentSpanId": {
127
+ "oneOf": [{ "type": "string" }, { "type": "null" }]
128
+ },
129
+ "startedAt": { "type": "string", "format": "date-time" },
130
+ "endedAt": {
131
+ "oneOf": [
132
+ { "type": "string", "format": "date-time" },
133
+ { "type": "null" }
134
+ ]
135
+ },
136
+ "attributes": { "type": "object" }
137
+ },
138
+ "additionalProperties": true
139
+ }
140
+ },
141
+ "metrics": {
142
+ "type": "object",
143
+ "description": "Aggregate metrics for the run.",
144
+ "properties": {
145
+ "openwopCost": {
146
+ "oneOf": [
147
+ { "type": "null" },
148
+ {
149
+ "type": "object",
150
+ "properties": {
151
+ "usd": { "type": "number", "minimum": 0 },
152
+ "tokens": {
153
+ "type": "object",
154
+ "properties": {
155
+ "input": { "type": "integer", "minimum": 0 },
156
+ "output": { "type": "integer", "minimum": 0 }
157
+ },
158
+ "additionalProperties": false
159
+ },
160
+ "model": { "type": "string" },
161
+ "provider": { "type": "string" },
162
+ "duration_ms": { "type": "number", "minimum": 0 }
163
+ },
164
+ "additionalProperties": true
165
+ }
166
+ ]
167
+ },
168
+ "nodeCount": { "type": "integer", "minimum": 0 },
169
+ "eventCount": {
170
+ "type": "integer",
171
+ "minimum": 0,
172
+ "description": "MUST equal events.length per spec/v1/debug-bundle.md."
173
+ }
174
+ },
175
+ "additionalProperties": true
176
+ },
177
+ "redactionApplied": {
178
+ "type": "boolean",
179
+ "description": "True if any field in this bundle was masked/omitted/hashed by the host's redaction harness."
180
+ },
181
+ "redactionMode": {
182
+ "type": "string",
183
+ "enum": ["mask", "omit", "hash", "passthrough"],
184
+ "description": "Masking mode in effect per capabilities.compliance.defaultMode."
185
+ },
186
+ "truncated": {
187
+ "type": "boolean",
188
+ "description": "True when the bundle was truncated to fit the host's size cap."
189
+ },
190
+ "truncatedReason": {
191
+ "type": "string",
192
+ "description": "Why truncation occurred (e.g., 'events_truncated_to_size_cap')."
193
+ }
194
+ },
195
+ "additionalProperties": true
196
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/dispatch-config.schema.json",
4
+ "title": "DispatchConfig",
5
+ "description": "Configuration for the `core.dispatch` node typeId (RFC 0007). Pins per-`kind` routing strategies for the dispatch loop. The dispatch node consumes the latest `OrchestratorDecision` (RFC 0006 §C) from the run's projection and acts on it per the per-`kind` semantics in RFC 0007 §D.",
6
+ "type": "object",
7
+ "required": [],
8
+ "properties": {
9
+ "askUserRouting": {
10
+ "type": "string",
11
+ "enum": ["conversation", "clarification", "auto"],
12
+ "default": "auto",
13
+ "description": "Routing surface for `kind: 'ask-user'` decisions. `'conversation'` requires the host to advertise `capabilities.conversationPrimitive: true` (RFC 0005) and uses `conversation.exchange`; `'clarification'` uses the existing `clarification.requested` interrupt (pre-RFC-0010 path); `'auto'` selects `conversation` when the host advertises the conversation-primitive capability and `clarification` otherwise. Hosts that select `'conversation'` without advertising the capability MUST surface a `validation_error` at workflow registration time."
14
+ },
15
+ "workerDispatchModel": {
16
+ "type": "string",
17
+ "enum": ["child-run"],
18
+ "default": "child-run",
19
+ "description": "How `kind: 'next-worker'` is dispatched. v1.x normates only `'child-run'` — each `nextWorkerIds[i]` resolves to a workflow id and is dispatched as a child run via the existing `core.subWorkflow` machinery. Same-run DAG navigation is OUT OF SCOPE for v1.x (deferred to RFC 0013 / v1.3+). Hosts MAY add vendor-extension dispatch models under `vendor.<host>.<model>`; conformance scenarios validate `'child-run'` only."
20
+ },
21
+ "fanOutPolicy": {
22
+ "type": "string",
23
+ "enum": ["sequential", "reject"],
24
+ "default": "sequential",
25
+ "description": "Behavior when `decision.kind === 'next-worker'` and `nextWorkerIds.length > 1`. `'sequential'` dispatches each in array order, blocking on each child terminal before starting the next; the dispatch node's output reports the LAST child's `childRunId`/`childStatus` (intermediate child-run identifiers surface in the run's event log via standard `node.*` events). `'reject'` fails the dispatch with a structured `'fan_out_unsupported'` error envelope per `error-envelope.md`. Parallel fan-out is OUT OF SCOPE for v1.x — see RFC 0007 §K3 (unresolved question)."
26
+ },
27
+ "iterationCap": {
28
+ "type": "integer",
29
+ "minimum": 1,
30
+ "description": "Optional per-run hard cap on dispatch-node executions (across all dispatch nodes in the same run). Independent of `RunOptions.configurable.recursionLimit` / `Capabilities.limits.maxNodeExecutions` — when set, the engine MUST surface a `cap.breached` event with `kind: 'dispatch-iterations'` once exceeded and transition the run to `'failed'`. When absent, the run-level `recursionLimit` cap applies normally (each dispatch counts as one node execution against the run total)."
31
+ }
32
+ },
33
+ "additionalProperties": false,
34
+ "examples": [
35
+ {
36
+ "askUserRouting": "auto",
37
+ "workerDispatchModel": "child-run",
38
+ "fanOutPolicy": "sequential"
39
+ },
40
+ {
41
+ "askUserRouting": "clarification",
42
+ "fanOutPolicy": "reject",
43
+ "iterationCap": 25
44
+ }
45
+ ]
46
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/error-envelope.schema.json",
4
+ "title": "ErrorEnvelope",
5
+ "description": "Canonical error response shape returned by every OpenWOP-compliant server on a non-2xx response. See rest-endpoints.md §error-envelope and auth.md §3.",
6
+ "type": "object",
7
+ "required": ["error", "message"],
8
+ "properties": {
9
+ "error": {
10
+ "type": "string",
11
+ "description": "Machine-readable error code (e.g., `unauthenticated`, `validation_error`, `not_found`, `run_already_active`, `unsupported_stream_mode`). Servers SHOULD use snake_case; spec-canonical codes are listed in `rest-endpoints.md`.",
12
+ "minLength": 1
13
+ },
14
+ "message": {
15
+ "type": "string",
16
+ "description": "Human-readable error description. Localizable per `Accept-Language` request header (servers MAY ignore).",
17
+ "minLength": 1
18
+ },
19
+ "details": {
20
+ "type": "object",
21
+ "description": "Error-specific contextual data. Schema varies by `error` code — readers MUST tolerate missing/unknown fields (forward-compat). Examples: `{retryAfter: 30}` for rate-limit errors, `{activeRunId, activeHost}` for run_already_active conflicts."
22
+ }
23
+ },
24
+ "additionalProperties": false
25
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/memory-entry.schema.json",
4
+ "title": "MemoryEntry",
5
+ "description": "A single memory entry returned by `MemoryAdapter.list` or `MemoryAdapter.get` (RFC 0004). Hosts that ship cross-run memory expose entries in this shape regardless of their internal storage format. Mid-run mutations to the underlying store are NOT visible to the same run — `list` returns entries that existed at run start (preserves replay determinism per RFC 0004 §A).",
6
+ "type": "object",
7
+ "required": ["id", "content", "tags", "createdAt"],
8
+ "properties": {
9
+ "id": {
10
+ "type": "string",
11
+ "description": "Memory entry id. Stable for the lifetime of the entry. Hosts MAY tenant-prefix (`mem_<wsId>_<id>`) for defense in depth, but the protocol is silent on the id shape — see RFC 0004 §Unresolved questions #1. Length cap matches Firestore document-id limit (1500 octets) so adapters that pass-through native Firestore IDs validate cleanly.",
12
+ "minLength": 1,
13
+ "maxLength": 1500
14
+ },
15
+ "content": {
16
+ "type": "string",
17
+ "description": "Memory body. UTF-8 plaintext. Hosts MUST honor the redaction invariant SR-1 (RFC 0004 §D) at write time — content reaching this projection MUST NOT contain plaintext secret values that flowed through the run's BYOK harness."
18
+ },
19
+ "tags": {
20
+ "type": "array",
21
+ "items": { "type": "string", "maxLength": 256 },
22
+ "description": "Free-form tags for filtering. Hosts MAY enforce caps. Empty strings are tolerated by the schema (matches reference-impl behavior) but hosts SHOULD filter them at write time. The `MemoryListOptions.tag` filter is a set-membership check against this array. Hosts that want to surface their internal `MemorySource` (or similar host-defined classification) MAY do so via tags (e.g., `tags: ['source:session']`) per RFC 0004 §G."
23
+ },
24
+ "createdAt": {
25
+ "type": "string",
26
+ "format": "date-time",
27
+ "description": "ISO 8601 timestamp of entry creation. Never mutated post-create; subsequent edits to the entry are out-of-spec at the read-path layer."
28
+ },
29
+ "expiresAt": {
30
+ "type": "string",
31
+ "format": "date-time",
32
+ "description": "Optional TTL. When the host's clock passes this value, the entry MUST NOT be returned by `list` / `get`. Hosts MAY also delete expired entries from storage as a side effect."
33
+ }
34
+ },
35
+ "additionalProperties": false
36
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/memory-list-options.schema.json",
4
+ "title": "MemoryListOptions",
5
+ "description": "Options accepted by `MemoryAdapter.list` (RFC 0004). All fields optional; default is \"all entries, newest first.\" Hosts MAY further bound results per their scale-profile.",
6
+ "type": "object",
7
+ "properties": {
8
+ "limit": {
9
+ "type": "integer",
10
+ "minimum": 0,
11
+ "description": "Cap on entries returned. `0` means \"no limit\" (treated identically to omission); positive integers cap the result. Hosts MAY further bound the effective cap per their scale-profile. When omitted, the host applies its own default cap (typical: 100 entries)."
12
+ },
13
+ "tag": {
14
+ "type": "string",
15
+ "minLength": 1,
16
+ "maxLength": 256,
17
+ "description": "Filter to entries whose `tags` array contains this string (set-membership check). Hosts MAY support more advanced filtering as a host extension under a `vendor.<host>.*` field."
18
+ }
19
+ },
20
+ "additionalProperties": false
21
+ }
@@ -0,0 +1,235 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/node-pack-manifest.schema.json",
4
+ "title": "NodePackManifest",
5
+ "description": "Manifest for a published OpenWOP node pack — `pack.json` at the pack root. Language-neutral. See node-packs.md for the canonical contract.",
6
+ "type": "object",
7
+ "required": ["name", "version", "engines", "runtime"],
8
+ "anyOf": [
9
+ { "properties": { "nodes": { "type": "array", "minItems": 1 } }, "required": ["nodes"] },
10
+ { "properties": { "agents": { "type": "array", "minItems": 1 } }, "required": ["agents"] }
11
+ ],
12
+ "properties": {
13
+ "name": {
14
+ "type": "string",
15
+ "description": "Reverse-DNS pack name. Reserved scopes: `core.*` (spec-canonical), `vendor.<org>.*` (vendor-published), `community.<author>.*` (individual), `private.<host>.*` (host-internal — MUST NOT appear in `packs.openwop.dev`). `local.*` is for in-repo unpublished packs and MUST NOT appear in any registry. See node-packs.md §Naming for the full reservation table.",
16
+ "pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$",
17
+ "minLength": 1,
18
+ "maxLength": 256
19
+ },
20
+ "version": {
21
+ "type": "string",
22
+ "description": "Pack version per Semantic Versioning 2.0.0.",
23
+ "pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$"
24
+ },
25
+ "description": { "type": "string", "maxLength": 1024 },
26
+ "author": { "type": "string" },
27
+ "license": { "type": "string", "description": "SPDX license identifier (e.g., `Apache-2.0`, `MIT`)." },
28
+ "homepage": { "type": "string", "format": "uri" },
29
+ "repository": { "type": "string", "format": "uri" },
30
+ "keywords": {
31
+ "type": "array",
32
+ "items": { "type": "string", "maxLength": 64 },
33
+ "maxItems": 50
34
+ },
35
+ "engines": {
36
+ "type": "object",
37
+ "required": ["openwop"],
38
+ "properties": {
39
+ "openwop": {
40
+ "type": "string",
41
+ "description": "Semver range — which openwop protocol versions this pack works against. Example: `>=1.0 <2.0.0`."
42
+ }
43
+ },
44
+ "additionalProperties": true
45
+ },
46
+ "dependencies": {
47
+ "type": "object",
48
+ "additionalProperties": { "type": "string" },
49
+ "description": "Other node packs this pack depends on. Map of pack name → semver range. Engine resolves transitively at workflow-register time."
50
+ },
51
+ "peerDependencies": {
52
+ "type": "object",
53
+ "additionalProperties": { "type": "string" },
54
+ "description": "Engine-supplied capabilities the pack consumes (e.g., a particular AI provider extension). Resolved against `Capabilities` at register time."
55
+ },
56
+ "nodes": {
57
+ "type": "array",
58
+ "items": { "$ref": "#/$defs/PackNode" },
59
+ "description": "Node typeIds the pack contributes. Each MUST have a unique `typeId` within the pack. A pack MAY omit `nodes` entirely if it ships only `agents[]` — the top-level `anyOf` constraint requires at least one of `nodes` or `agents` to be non-empty."
60
+ },
61
+ "agents": {
62
+ "type": "array",
63
+ "items": { "$ref": "agent-manifest.schema.json" },
64
+ "description": "Optional agent manifests shipped alongside this pack. Each entry is an AgentManifest (see agent-manifest.schema.json + RFC 0003 §`agents[]` extension). Pure-agent packs MUST set `runtime.language: 'remote'` — agents are interpreted by the host, not bundled as executable artifacts. Mixed packs (nodes + agents) declare the runtime that loads the node implementations; agents remain host-interpreted."
65
+ },
66
+ "runtime": { "$ref": "#/$defs/Runtime" },
67
+ "signing": { "$ref": "#/$defs/Signing" }
68
+ },
69
+ "additionalProperties": false,
70
+ "$defs": {
71
+ "PackNode": {
72
+ "type": "object",
73
+ "required": ["typeId", "version", "category", "role"],
74
+ "properties": {
75
+ "typeId": {
76
+ "type": "string",
77
+ "description": "Canonical node type ID. MUST match the same pattern as `WorkflowNode.typeId` in workflow-definition.schema.json. The pack's `name` prefix is recommended (e.g., a pack `vendor.acme.salesforce-tools` exposing `vendor.acme.salesforce.upsert`).",
78
+ "pattern": "^[a-z][a-zA-Z0-9._-]*$",
79
+ "minLength": 1,
80
+ "maxLength": 256
81
+ },
82
+ "version": {
83
+ "type": "string",
84
+ "description": "Per-node semver. MAY differ from the pack's overall version — useful when a single pack ships multiple nodes that evolve independently."
85
+ },
86
+ "label": { "type": "string", "minLength": 1 },
87
+ "description": { "type": "string" },
88
+ "category": {
89
+ "type": "string",
90
+ "enum": ["chat", "control", "data", "canvas", "coordination", "integration"],
91
+ "description": "Engine-side classification used for scheduling and UI grouping."
92
+ },
93
+ "role": {
94
+ "type": "string",
95
+ "description": "Semantic role from the `NodeRole` taxonomy. Drives engine scheduling. Common values: `pure`, `side-effect`, `human-input`, `streaming-output`, `gate`."
96
+ },
97
+ "capabilities": {
98
+ "type": "array",
99
+ "items": {
100
+ "type": "string",
101
+ "enum": ["streamable", "cacheable", "side-effectful", "mcp-exportable"]
102
+ },
103
+ "uniqueItems": true,
104
+ "description": "Orthogonal capability traits. The engine uses these to decide caching, MCP exposure, idempotency enforcement."
105
+ },
106
+ "configSchemaRef": {
107
+ "type": "string",
108
+ "description": "Path inside the pack tarball to the JSON Schema for the node's config. Server fetches the file when resolving the pack."
109
+ },
110
+ "inputSchemaRef": {
111
+ "type": "string",
112
+ "description": "Path to the JSON Schema for the node's input ports."
113
+ },
114
+ "outputSchemaRef": {
115
+ "type": "string",
116
+ "description": "Path to the JSON Schema for the node's output ports."
117
+ },
118
+ "outputs": {
119
+ "type": "object",
120
+ "additionalProperties": {
121
+ "type": "object",
122
+ "properties": {
123
+ "sensitive": {
124
+ "type": "boolean",
125
+ "default": false,
126
+ "description": "When true, the engine masks this output port's value in `node.completed` event payloads. Use when a NodeModule ALWAYS handles sensitive data (e.g., a Salesforce upsert always touches PII). Workflows MAY override per-instance via `WorkflowNode.outputSensitivity`. See observability.md §Privacy classification (closes O5)."
127
+ }
128
+ },
129
+ "additionalProperties": true
130
+ },
131
+ "description": "Per-output-port metadata. Keys are output port names; values are per-port markers. Currently the only marker is `sensitive`; future additions live alongside (additionalProperties: true on each port)."
132
+ },
133
+ "envelopeContractRef": {
134
+ "type": "string",
135
+ "description": "Optional path to a JSON document declaring the LLM envelope contract for AI-backed nodes."
136
+ },
137
+ "artifact": {
138
+ "type": "object",
139
+ "properties": {
140
+ "typeId": { "type": "string" },
141
+ "syncOn": { "type": "string", "enum": ["completion", "approval", "manual"] },
142
+ "supportsCheckpoint": { "type": "boolean" }
143
+ },
144
+ "additionalProperties": false
145
+ },
146
+ "mcp": {
147
+ "type": "object",
148
+ "properties": {
149
+ "exposeAsTool": { "type": "boolean" },
150
+ "toolName": { "type": "string" }
151
+ },
152
+ "additionalProperties": false
153
+ },
154
+ "requiresSecrets": {
155
+ "type": "array",
156
+ "items": { "$ref": "#/$defs/SecretRequirement" },
157
+ "description": "Secrets the node needs to execute. Resolved by the host's secret-resolution adapter at dispatch time. Hosts that don't advertise `Capabilities.secrets.supported` MUST refuse to dispatch a node with non-empty `requiresSecrets` and return `credential_unavailable`."
158
+ }
159
+ },
160
+ "additionalProperties": false
161
+ },
162
+ "SecretRequirement": {
163
+ "type": "object",
164
+ "required": ["id", "kind"],
165
+ "description": "Declared secret requirement. The engine surfaces this to the host's `SecretResolver` which returns an opaque `ResolvedSecret` reference; the executor passes the reference to provider adapters that dereference internally. Raw key material NEVER reaches the protocol surface (events, logs, traces, etc.).",
166
+ "properties": {
167
+ "id": {
168
+ "type": "string",
169
+ "minLength": 1,
170
+ "description": "Stable identifier the node executor uses to look up the resolved secret (e.g., `'primary-anthropic-key'`)."
171
+ },
172
+ "kind": {
173
+ "type": "string",
174
+ "enum": ["ai-provider", "api-key", "oauth-token", "custom"],
175
+ "description": "Coarse classification driving host-side resolution policy. `ai-provider` triggers the BYOK / aiProviders flow; `api-key` is generic; `oauth-token` integrates with the host's OAuth refresh flow; `custom` is host-extensible."
176
+ },
177
+ "provider": {
178
+ "type": "string",
179
+ "description": "For `kind: 'ai-provider'`, the provider id (`anthropic`, `openai`, `gemini`, etc.). MUST be in `Capabilities.aiProviders.supported`. Optional for other kinds."
180
+ },
181
+ "scope": {
182
+ "type": "string",
183
+ "enum": ["tenant", "user", "run"],
184
+ "description": "Where the host should look up the secret. MUST match a scope in `Capabilities.secrets.scopes`. Defaults to `tenant` when omitted."
185
+ }
186
+ },
187
+ "additionalProperties": false
188
+ },
189
+ "Runtime": {
190
+ "type": "object",
191
+ "required": ["language", "entry"],
192
+ "properties": {
193
+ "language": {
194
+ "type": "string",
195
+ "enum": ["javascript", "python", "go", "wasm", "remote"],
196
+ "description": "How the engine loads the pack. See node-packs.md §runtime formats."
197
+ },
198
+ "entry": {
199
+ "type": "string",
200
+ "description": "Path inside the tarball to the runtime artifact, OR a URL when `language: remote`."
201
+ },
202
+ "format": {
203
+ "type": "string",
204
+ "enum": ["esm", "cjs", "wheel", "binary", "shared-library", "wasm"],
205
+ "description": "Runtime-specific format hint. `esm`/`cjs` for JS, `wheel` for Python, `binary`/`shared-library` for Go, `wasm` for wasm."
206
+ },
207
+ "minRuntimeVersion": {
208
+ "type": "string",
209
+ "description": "Minimum host runtime version (e.g., `node>=20`, `python>=3.10`, `go>=1.22`)."
210
+ }
211
+ },
212
+ "additionalProperties": false
213
+ },
214
+ "Signing": {
215
+ "type": "object",
216
+ "description": "Optional signing metadata. See node-packs.md §signing.",
217
+ "properties": {
218
+ "publicKeyRef": {
219
+ "type": "string",
220
+ "description": "Path inside the tarball to the Ed25519 public key (PEM-encoded)."
221
+ },
222
+ "signatureRef": {
223
+ "type": "string",
224
+ "description": "Path to the detached signature over `pack.json`."
225
+ },
226
+ "method": {
227
+ "type": "string",
228
+ "enum": ["manual", "sigstore"],
229
+ "description": "Signing method. `manual` uses publicKeyRef + signatureRef; `sigstore` uses a Sigstore bundle at `pack.json.sigstore`."
230
+ }
231
+ },
232
+ "additionalProperties": false
233
+ }
234
+ }
235
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/orchestrator-decision.schema.json",
4
+ "title": "OrchestratorDecision",
5
+ "description": "Routing decision emitted by an orchestrator-supervisor node. Carried in the `runOrchestrator.decided` event payload's `decision` field (run-orchestrator-decided-event.schema.json). Three canonical kinds form a closed canonical enum at the protocol layer: `'next-worker'` (continue dispatch), `'ask-user'` (route a question to the human party), `'terminate'` (clean run completion). Vendor-extension decision kinds MAY ship under `vendor.<host>.<kind>` per host-extensions.md but conformance MUST validate against the closed canonical set. See RFC 0006 §C.",
6
+ "type": "object",
7
+ "required": ["kind"],
8
+ "oneOf": [
9
+ {
10
+ "title": "NextWorkerDecision",
11
+ "description": "Dispatch one or more next workers. Phase-5 hosts MAY treat the `nextWorkerIds` array as length-1 only and ignore the tail; Phase-6 hosts (RFC 0007 / `core.dispatch`) SHOULD honor full fan-out semantics. The interpretation of array entries (node-id vs agent-id) is left to host capability advertisement; see RFC 0006 §C unresolved-question 1.",
12
+ "type": "object",
13
+ "required": ["kind", "nextWorkerIds"],
14
+ "properties": {
15
+ "kind": { "const": "next-worker" },
16
+ "nextWorkerIds": {
17
+ "type": "array",
18
+ "minItems": 1,
19
+ "items": {
20
+ "type": "string",
21
+ "minLength": 1,
22
+ "maxLength": 256
23
+ },
24
+ "description": "Ordered list of worker node-ids OR agent-ids to dispatch. Hosts that interpret entries as node-ids dispatch in DAG order; hosts that interpret as agent-ids resolve to nodes via the run's static DAG."
25
+ }
26
+ },
27
+ "additionalProperties": false
28
+ },
29
+ {
30
+ "title": "AskUserDecision",
31
+ "description": "Route a human-targeted question. Hosts that adopted RFC 0005 (Phase 4) SHOULD route through `conversation.exchange`; pre-RFC-0005 hosts MAY surface as a `'clarification'` interrupt. The transport is host-policy; the protocol-layer decision shape is the same.",
32
+ "type": "object",
33
+ "required": ["kind", "prompt"],
34
+ "properties": {
35
+ "kind": { "const": "ask-user" },
36
+ "prompt": {
37
+ "type": "string",
38
+ "minLength": 1,
39
+ "description": "Human-targeted question. Free-form text; the protocol does NOT structure the prompt further."
40
+ }
41
+ },
42
+ "additionalProperties": false
43
+ },
44
+ {
45
+ "title": "TerminateDecision",
46
+ "description": "Clean run termination driven by orchestrator judgement. Distinct from `run.failed` (uncaught executor error) and `run.cancelled` (operator cancellation) — see RFC 0006 §H. The terminate `reason?` is informational; the protocol does NOT close the value enum.",
47
+ "type": "object",
48
+ "required": ["kind"],
49
+ "properties": {
50
+ "kind": { "const": "terminate" },
51
+ "reason": {
52
+ "type": "string",
53
+ "description": "Optional free-form rationale captured for audit/debug. Common values: `'goal-reached'`, `'max-iterations'`, `'unrecoverable-error'`. Spec does NOT close this enum.",
54
+ "minLength": 1
55
+ }
56
+ },
57
+ "additionalProperties": false
58
+ }
59
+ ]
60
+ }