@openwop/spec-artifacts 2.0.0-rc.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 (229) hide show
  1. package/CORPUS-STAMP.json +234 -0
  2. package/LICENSE +201 -0
  3. package/README.md +7 -0
  4. package/api/.redocly.lint-ignore.yaml +22 -0
  5. package/api/asyncapi.yaml +834 -0
  6. package/api/grpc/openwop.proto +251 -0
  7. package/api/openapi.yaml +2693 -0
  8. package/api/redocly.yaml +23 -0
  9. package/api/seams-v2.yaml +680 -0
  10. package/api/v2/asyncapi.yaml +528 -0
  11. package/api/v2/openapi.yaml +3139 -0
  12. package/api/v2/redocly.yaml +16 -0
  13. package/package.json +10 -0
  14. package/schemas/README.md +144 -0
  15. package/schemas/a2a-task-state.schema.json +78 -0
  16. package/schemas/a2ui-surface-delta-frame.schema.json +48 -0
  17. package/schemas/agent-deployment-transition.schema.json +49 -0
  18. package/schemas/agent-deployment.schema.json +54 -0
  19. package/schemas/agent-eval-suite.schema.json +140 -0
  20. package/schemas/agent-inventory-response.schema.json +115 -0
  21. package/schemas/agent-manifest.schema.json +156 -0
  22. package/schemas/agent-org-chart.schema.json +82 -0
  23. package/schemas/agent-ref.schema.json +63 -0
  24. package/schemas/agent-roster-entry.schema.json +81 -0
  25. package/schemas/agent-roster-response.schema.json +21 -0
  26. package/schemas/ai-envelope.schema.json +134 -0
  27. package/schemas/annotation-create.schema.json +37 -0
  28. package/schemas/annotation.schema.json +56 -0
  29. package/schemas/artifact-type-pack-manifest.schema.json +244 -0
  30. package/schemas/audit-verify-result.schema.json +90 -0
  31. package/schemas/budget-policy.schema.json +18 -0
  32. package/schemas/capabilities.schema.json +4160 -0
  33. package/schemas/certification-bundle-v2.schema.json +118 -0
  34. package/schemas/channel-presence-payload.schema.json +41 -0
  35. package/schemas/channel-written-payload.schema.json +55 -0
  36. package/schemas/chat-card-pack-manifest.schema.json +253 -0
  37. package/schemas/compact-tool-descriptor.schema.json +51 -0
  38. package/schemas/compensation-policy.schema.json +64 -0
  39. package/schemas/conformance-certification-bundle.schema.json +86 -0
  40. package/schemas/connection-pack-manifest.schema.json +353 -0
  41. package/schemas/conversation-event.schema.json +168 -0
  42. package/schemas/conversation-turn.schema.json +117 -0
  43. package/schemas/core-conformance-mock-agent-config.schema.json +152 -0
  44. package/schemas/credential-provenance.schema.json +18 -0
  45. package/schemas/credential-reference.schema.json +21 -0
  46. package/schemas/debug-bundle.schema.json +196 -0
  47. package/schemas/dispatch-config.schema.json +112 -0
  48. package/schemas/envelopes/clarification.request.schema.json +52 -0
  49. package/schemas/envelopes/error.schema.json +30 -0
  50. package/schemas/envelopes/media.audio.schema.json +38 -0
  51. package/schemas/envelopes/media.file.schema.json +37 -0
  52. package/schemas/envelopes/media.image.schema.json +33 -0
  53. package/schemas/envelopes/schema.request.schema.json +26 -0
  54. package/schemas/envelopes/schema.response.schema.json +22 -0
  55. package/schemas/envelopes/ui.a2ui-surface.schema.json +154 -0
  56. package/schemas/error-envelope.schema.json +25 -0
  57. package/schemas/eval-summary.schema.json +92 -0
  58. package/schemas/export-bundle.schema.json +66 -0
  59. package/schemas/form-content-pack-manifest.schema.json +243 -0
  60. package/schemas/frontend-plugin-manifest.schema.json +109 -0
  61. package/schemas/goal.schema.json +104 -0
  62. package/schemas/heartbeat-evaluated.schema.json +14 -0
  63. package/schemas/heartbeat-state-changed.schema.json +14 -0
  64. package/schemas/localized-content-language-settings.schema.json +26 -0
  65. package/schemas/localized-content-page-response.schema.json +60 -0
  66. package/schemas/localized-content-page.schema.json +62 -0
  67. package/schemas/localized-content-section.schema.json +51 -0
  68. package/schemas/memory-entry.schema.json +36 -0
  69. package/schemas/memory-list-options.schema.json +37 -0
  70. package/schemas/node-pack-manifest.schema.json +654 -0
  71. package/schemas/orchestrator-decision.schema.json +90 -0
  72. package/schemas/org-chart-responsibility-view.schema.json +26 -0
  73. package/schemas/pack-lockfile.schema.json +108 -0
  74. package/schemas/prompt-kind.schema.json +8 -0
  75. package/schemas/prompt-pack-manifest.schema.json +117 -0
  76. package/schemas/prompt-ref.schema.json +40 -0
  77. package/schemas/prompt-template.schema.json +149 -0
  78. package/schemas/proposal.schema.json +84 -0
  79. package/schemas/registry-version-manifest.schema.json +208 -0
  80. package/schemas/residency.schema.json +16 -0
  81. package/schemas/run-ancestry-response.schema.json +54 -0
  82. package/schemas/run-diff-response.schema.json +64 -0
  83. package/schemas/run-event-payloads.schema.json +4452 -0
  84. package/schemas/run-event.schema.json +211 -0
  85. package/schemas/run-options.schema.json +80 -0
  86. package/schemas/run-orchestrator-decided-event.schema.json +20 -0
  87. package/schemas/run-snapshot.schema.json +148 -0
  88. package/schemas/security-advisory.schema.json +109 -0
  89. package/schemas/self-hosted-runner-dispatch-frame.schema.json +79 -0
  90. package/schemas/self-hosted-runner-registration.schema.json +53 -0
  91. package/schemas/self-hosted-runner-result-frame.schema.json +38 -0
  92. package/schemas/subject.schema.json +56 -0
  93. package/schemas/suspend-request.schema.json +207 -0
  94. package/schemas/tool-descriptor.schema.json +63 -0
  95. package/schemas/trigger-event.schema.json +222 -0
  96. package/schemas/trigger-subscription-registration.schema.json +67 -0
  97. package/schemas/trigger-subscription.schema.json +26 -0
  98. package/schemas/ui-plugin-message.schema.json +90 -0
  99. package/schemas/v2/a2a-task-state.schema.json +102 -0
  100. package/schemas/v2/a2ui-surface-delta-frame.schema.json +62 -0
  101. package/schemas/v2/agent-deployment-transition.schema.json +67 -0
  102. package/schemas/v2/agent-deployment.schema.json +68 -0
  103. package/schemas/v2/agent-eval-suite.schema.json +214 -0
  104. package/schemas/v2/agent-inventory-response.schema.json +172 -0
  105. package/schemas/v2/agent-manifest.schema.json +218 -0
  106. package/schemas/v2/agent-org-chart.schema.json +149 -0
  107. package/schemas/v2/agent-ref.schema.json +67 -0
  108. package/schemas/v2/agent-roster-entry.schema.json +104 -0
  109. package/schemas/v2/agent-roster-response.schema.json +27 -0
  110. package/schemas/v2/ai-envelope.schema.json +167 -0
  111. package/schemas/v2/annotation-create.schema.json +109 -0
  112. package/schemas/v2/annotation.schema.json +142 -0
  113. package/schemas/v2/artifact-type-pack-manifest.schema.json +245 -0
  114. package/schemas/v2/audit-verify-result.schema.json +110 -0
  115. package/schemas/v2/budget-policy.schema.json +61 -0
  116. package/schemas/v2/capabilities.schema.json +7644 -0
  117. package/schemas/v2/certification-bundle.schema.json +401 -0
  118. package/schemas/v2/channel-presence-payload.schema.json +60 -0
  119. package/schemas/v2/channel-written-payload.schema.json +64 -0
  120. package/schemas/v2/chat-card-pack-manifest.schema.json +254 -0
  121. package/schemas/v2/compact-tool-descriptor.schema.json +108 -0
  122. package/schemas/v2/compensation-policy.schema.json +91 -0
  123. package/schemas/v2/compensation-projection.schema.json +95 -0
  124. package/schemas/v2/configurable.schema.json +110 -0
  125. package/schemas/v2/connection-pack-manifest.schema.json +354 -0
  126. package/schemas/v2/conversation-event.schema.json +281 -0
  127. package/schemas/v2/conversation-turn.schema.json +165 -0
  128. package/schemas/v2/credential-provenance.schema.json +62 -0
  129. package/schemas/v2/credential-reference.schema.json +28 -0
  130. package/schemas/v2/debug-bundle.schema.json +312 -0
  131. package/schemas/v2/dispatch-config.schema.json +147 -0
  132. package/schemas/v2/effect-ledger-projection.schema.json +71 -0
  133. package/schemas/v2/effect-seam-manifest.schema.json +98 -0
  134. package/schemas/v2/envelopes/clarification.request.schema.json +59 -0
  135. package/schemas/v2/envelopes/error.schema.json +35 -0
  136. package/schemas/v2/envelopes/media.audio.schema.json +41 -0
  137. package/schemas/v2/envelopes/media.file.schema.json +40 -0
  138. package/schemas/v2/envelopes/media.image.schema.json +36 -0
  139. package/schemas/v2/envelopes/schema.request.schema.json +29 -0
  140. package/schemas/v2/envelopes/schema.response.schema.json +26 -0
  141. package/schemas/v2/envelopes/ui.a2ui-surface.schema.json +292 -0
  142. package/schemas/v2/error-envelope.schema.json +229 -0
  143. package/schemas/v2/eval-summary.schema.json +166 -0
  144. package/schemas/v2/export-bundle.schema.json +94 -0
  145. package/schemas/v2/form-content-pack-manifest.schema.json +241 -0
  146. package/schemas/v2/frontend-plugin-manifest.schema.json +152 -0
  147. package/schemas/v2/goal.schema.json +188 -0
  148. package/schemas/v2/heartbeat-evaluated.schema.json +34 -0
  149. package/schemas/v2/heartbeat-state-changed.schema.json +31 -0
  150. package/schemas/v2/ids.schema.json +31 -0
  151. package/schemas/v2/localized-content-language-settings.schema.json +34 -0
  152. package/schemas/v2/localized-content-page-response.schema.json +94 -0
  153. package/schemas/v2/localized-content-page.schema.json +87 -0
  154. package/schemas/v2/localized-content-section.schema.json +63 -0
  155. package/schemas/v2/memory-entry.schema.json +45 -0
  156. package/schemas/v2/memory-list-options.schema.json +41 -0
  157. package/schemas/v2/node-pack-manifest.schema.json +657 -0
  158. package/schemas/v2/orchestrator-decision.schema.json +120 -0
  159. package/schemas/v2/org-chart-responsibility-view.schema.json +36 -0
  160. package/schemas/v2/pack-lockfile.schema.json +139 -0
  161. package/schemas/v2/prompt-kind.schema.json +14 -0
  162. package/schemas/v2/prompt-pack-manifest.schema.json +118 -0
  163. package/schemas/v2/prompt-ref.schema.json +42 -0
  164. package/schemas/v2/prompt-template.schema.json +209 -0
  165. package/schemas/v2/proposal.schema.json +145 -0
  166. package/schemas/v2/registry-version-manifest.schema.json +292 -0
  167. package/schemas/v2/residency.schema.json +19 -0
  168. package/schemas/v2/run-ancestry-response.schema.json +68 -0
  169. package/schemas/v2/run-diff-response.schema.json +80 -0
  170. package/schemas/v2/run-event-payloads.schema.json +4588 -0
  171. package/schemas/v2/run-event.schema.json +324 -0
  172. package/schemas/v2/run-options.schema.json +90 -0
  173. package/schemas/v2/run-orchestrator-decided-event.schema.json +22 -0
  174. package/schemas/v2/run-snapshot.schema.json +207 -0
  175. package/schemas/v2/security-advisory.schema.json +138 -0
  176. package/schemas/v2/self-hosted-runner-dispatch-frame.schema.json +119 -0
  177. package/schemas/v2/self-hosted-runner-registration.schema.json +74 -0
  178. package/schemas/v2/self-hosted-runner-result-frame.schema.json +47 -0
  179. package/schemas/v2/subject-link.schema.json +69 -0
  180. package/schemas/v2/subject.schema.json +597 -0
  181. package/schemas/v2/suspend-request.schema.json +370 -0
  182. package/schemas/v2/tool-descriptor.schema.json +150 -0
  183. package/schemas/v2/trigger-event.schema.json +508 -0
  184. package/schemas/v2/trigger-subscription-registration.schema.json +99 -0
  185. package/schemas/v2/trigger-subscription.schema.json +79 -0
  186. package/schemas/v2/ui-plugin-message.schema.json +149 -0
  187. package/schemas/v2/workflow-chain-pack-manifest.schema.json +610 -0
  188. package/schemas/v2/workflow-definition.schema.json +864 -0
  189. package/schemas/v2/workload-identity.schema.json +115 -0
  190. package/schemas/v2/workspace-file-create.schema.json +23 -0
  191. package/schemas/v2/workspace-file.schema.json +45 -0
  192. package/schemas/workflow-chain-pack-manifest.schema.json +615 -0
  193. package/schemas/workflow-definition.schema.json +850 -0
  194. package/schemas/workload-identity.schema.json +79 -0
  195. package/schemas/workspace-file-create.schema.json +20 -0
  196. package/schemas/workspace-file.schema.json +39 -0
  197. package/spec/v1/alias-detectors.json +283 -0
  198. package/spec/v1/capability-declaration-classes.json +176 -0
  199. package/spec/v1/core-standard-manifest.json +423 -0
  200. package/spec/v1/deprecations.json +854 -0
  201. package/spec/v1/deprecations.schema.json +187 -0
  202. package/spec/v1/event-codemap.json +1089 -0
  203. package/spec/v1/event-codemap.schema.json +41 -0
  204. package/spec/v1/extensions.json +1273 -0
  205. package/spec/v1/gaps.json +14581 -0
  206. package/spec/v1/gaps.schema.json +55 -0
  207. package/spec/v1/migrations.json +1822 -0
  208. package/spec/v1/migrations.schema.json +44 -0
  209. package/spec/v1/operation-path-manifest.json +462 -0
  210. package/spec/v1/spec-gaps.json +4823 -0
  211. package/spec/v2/declaration.json +1862 -0
  212. package/spec/v2/declaration.schema.json +305 -0
  213. package/spec/v2/errors.json +837 -0
  214. package/spec/v2/event-codemap.json +1099 -0
  215. package/spec/v2/event-codemap.schema.json +114 -0
  216. package/spec/v2/ext/provider-idempotency/registry.json +6 -0
  217. package/spec/v2/facets/a2a.schema.json +51 -0
  218. package/spec/v2/facets/aiProviders.schema.json +56 -0
  219. package/spec/v2/facets/auth.schema.json +91 -0
  220. package/spec/v2/facets/heartbeat.schema.json +20 -0
  221. package/spec/v2/facets/interrupt.schema.json +29 -0
  222. package/spec/v2/facets/mcp.schema.json +81 -0
  223. package/spec/v2/facets/replay.schema.json +34 -0
  224. package/spec/v2/facets/sandbox.schema.json +31 -0
  225. package/spec/v2/facets/webhooks.schema.json +18 -0
  226. package/spec/v2/path-manifest.json +279 -0
  227. package/spec/v2/peer-dependency-aliases.json +165 -0
  228. package/spec/v2/profiles.json +45 -0
  229. package/spec/v2/release.json +6 -0
@@ -0,0 +1,4160 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/capabilities.schema.json",
4
+ "title": "Capabilities",
5
+ "description": "openwop capability declaration returned from `GET /.well-known/openwop`. Required v1 fields identify the protocol version, envelope catalog, schema versions, and base limits. Optional v1 fields have stable shapes but MAY be omitted when unsupported.",
6
+ "type": "object",
7
+ "required": [
8
+ "protocolVersion",
9
+ "supportedEnvelopes",
10
+ "schemaVersions",
11
+ "limits"
12
+ ],
13
+ "properties": {
14
+ "protocolVersion": {
15
+ "type": "string",
16
+ "description": "RFC 0149 §C — openwop protocol version the server speaks, as ASCII `<major>.<minor>` with no leading zero except zero itself. `1.0` and `1.12` are valid; `1`, `1.0.0`, `v1.0`, and `01.0` are not. The integer major is the hard compatibility boundary and the integer minor is the additive contract level; PATCH belongs to suite and SDK versions, not the spec version. Previously constrained only by `minLength: 1`, which admitted `v1.0`, `1.0.0`, and `banana` — a negotiation the wire could not decide, since neither integer can be extracted from an unconstrained string. Independent of `engineVersion` (which gates persisted-doc compatibility per version-negotiation.md).",
17
+ "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
18
+ },
19
+ "protocolVersions": {
20
+ "type": "array",
21
+ "minItems": 1,
22
+ "uniqueItems": true,
23
+ "items": {
24
+ "type": "string",
25
+ "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
26
+ },
27
+ "description": "RFC 0165 §A. Every protocol `<major>.<minor>` this host speaks, newest first by convention. OPTIONAL in v1.x; when present it MUST contain the value of `protocolVersion` and MUST NOT name a major the host does not serve. Same grammar as `protocolVersion` (RFC 0149 §C), not the looser A2A item pattern. Consumers that do not understand it ignore it; profile derivation reads `protocolVersion` only. Reserved for v2 major negotiation."
28
+ },
29
+ "preferredVersion": {
30
+ "type": "string",
31
+ "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$",
32
+ "description": "RFC 0179 §A.1 (v1.x additive half of RFC 0172 §A.1). The `<major>.<minor>` this host serves to a header-less request. OPTIONAL in v1.x; when present MUST be a member of `protocolVersions[]` (when present) and, on a host serving a single major, MUST equal `protocolVersion`. Required at v2 with `OpenWOP-Version` negotiation."
33
+ },
34
+ "contractProvenance": {
35
+ "type": "object",
36
+ "description": "RFC 0146 (`Active`). Which corpus revision this host's contract handling corresponds to — the copy it validates and serves against. OPTIONAL; absent ⇒ UNSPECIFIED provenance, neither current nor stale, and a consumer MUST NOT infer a version from its absence. ADVISORY: a consumer MUST NOT reject a request, refuse interop, or fail a run solely because this differs from its own — v1.x corpus revisions are additive, so a host on an older revision is conformant and this field detects drift rather than making drift an error. Nothing else on the wire answers this: `protocolVersion` is `1.0` across the entire v1 line (the drift happens inside that value) and `schemaVersions` is per-envelope-type. NOT an integrity check — `corpusCommit` says WHICH contract, never whether the copy was modified.",
37
+ "properties": {
38
+ "suiteVersion": {
39
+ "type": "string",
40
+ "description": "A published `@openwop/openwop-conformance` version, e.g. `1.73.0`. Mirrors `schemas/CORPUS-STAMP.json` so a host depending on the package can echo its own stamp rather than hand-maintaining a constant.",
41
+ "minLength": 1
42
+ },
43
+ "corpusCommit": {
44
+ "type": "string",
45
+ "description": "Full 40-character commit SHA of the openwop repository. A vendor build identifier does NOT belong here — `implementation` already exists for that.",
46
+ "pattern": "^[0-9a-f]{40}$"
47
+ }
48
+ },
49
+ "additionalProperties": false
50
+ },
51
+ "supportedEnvelopes": {
52
+ "type": "array",
53
+ "items": {
54
+ "type": "string",
55
+ "minLength": 1
56
+ },
57
+ "description": "Envelope `type` strings the engine recognizes (e.g., `prd.create`, `theme.create`, `tasks.create`, `clarification.request`)."
58
+ },
59
+ "schemaVersions": {
60
+ "type": "object",
61
+ "additionalProperties": {
62
+ "type": "integer",
63
+ "minimum": 0
64
+ },
65
+ "description": "Active schema version per envelope type (e.g., `{ \"prd.create\": 2 }`)."
66
+ },
67
+ "limits": {
68
+ "type": "object",
69
+ "required": [
70
+ "clarificationRounds",
71
+ "schemaRounds",
72
+ "envelopesPerTurn"
73
+ ],
74
+ "properties": {
75
+ "clarificationRounds": {
76
+ "type": "integer",
77
+ "minimum": 0,
78
+ "description": "Maximum total clarification envelopes per task before the engine forces the LLM to proceed."
79
+ },
80
+ "schemaRounds": {
81
+ "type": "integer",
82
+ "minimum": 0,
83
+ "description": "Maximum total schema-validation rounds per envelope before the node fails."
84
+ },
85
+ "envelopesPerTurn": {
86
+ "type": "integer",
87
+ "minimum": 0,
88
+ "description": "Maximum total envelopes the LLM may emit in a single chat turn."
89
+ },
90
+ "maxNodeExecutions": {
91
+ "type": "integer",
92
+ "minimum": 1,
93
+ "description": "Engine-side ceiling on total node executions per run. Acts as the upper bound for `RunOptions.configurable.recursionLimit`. Optional in v1; hosts that advertise it MUST enforce it."
94
+ },
95
+ "maxRunDurationMs": {
96
+ "type": "integer",
97
+ "minimum": 1000,
98
+ "description": "RFC 0058. Engine-side wall-clock ceiling per run (milliseconds). Upper bound for `RunOptions.configurable.runTimeoutMs`. Breach emits `cap.breached { kind: 'run-duration' }` + error `run_timeout`. Optional; hosts that advertise it MUST enforce it."
99
+ },
100
+ "maxLoopIterations": {
101
+ "type": "integer",
102
+ "minimum": 1,
103
+ "description": "RFC 0058. Authoritative engine-side ceiling on agent-loop iterations. Upper bound for `RunOptions.configurable.maxLoopIterations`. Breach emits `cap.breached { kind: 'loop-iterations' }` + error `loop_limit_exceeded`. Optional; hosts that advertise it MUST enforce it."
104
+ },
105
+ "maxBudgetTokens": {
106
+ "type": "integer",
107
+ "minimum": 1,
108
+ "description": "RFC 0084. Engine-side ceiling clamping `RunOptions.configurable.budget.maxTokens` (`min(requested, ceiling)`). Optional; only meaningful with `capabilities.budget`."
109
+ },
110
+ "maxBudgetCostUsd": {
111
+ "type": "number",
112
+ "minimum": 0,
113
+ "description": "RFC 0084. Engine-side cost ceiling clamping `RunOptions.configurable.budget.maxCostUsd`. Optional; only meaningful with `capabilities.budget`."
114
+ },
115
+ "maxRequestBodyBytes": {
116
+ "type": "integer",
117
+ "minimum": 1,
118
+ "description": "RFC 0094 §H. Maximum REST request body size (bytes) the host accepts. Optional v1 field per `capabilities.md` §3 (previously documented as reserved — the closed `limits` object made advertising it a schema-validation failure). Hosts that advertise it MUST enforce it."
119
+ }
120
+ },
121
+ "additionalProperties": false,
122
+ "description": "Hard limits enforced by the engine. See capabilities.md §3."
123
+ },
124
+ "envelopeStrictness": {
125
+ "type": "string",
126
+ "enum": [
127
+ "warn",
128
+ "strict"
129
+ ],
130
+ "description": "AI Envelope schema-version drift handling per `spec/v1/ai-envelope.md` §\"Capability handshake integration\" (DRAFT v1.x). Optional in v1.x; default when absent is `warn`. Under `warn`, the engine MUST attempt validation against the advertised version of a kind and log `envelope_schema_version_drift` when the emitted `schemaVersion` is lower than advertised. Under `strict`, the same condition MUST cause refusal with `unknown_schema_version`. Emitted `schemaVersion` higher than advertised MUST refuse regardless of strictness."
131
+ },
132
+ "envelopeContracts": {
133
+ "type": "object",
134
+ "description": "AI Envelope contract-gating advertisement per `spec/v1/ai-envelope.md` §\"Capability handshake integration\" (DRAFT v1.x). Optional in v1.x. When `advertised: true`, the host's node-pack manifests carry `EnvelopeContract` blocks per `ai-envelope.md` §\"Envelope Contract\"; tooling and conformance scenarios gate on this flag. When `advertised: false` or the block is absent, hosts MAY accept envelopes without per-typeId `accepts[]` enforcement.",
135
+ "required": [
136
+ "advertised"
137
+ ],
138
+ "properties": {
139
+ "advertised": {
140
+ "type": "boolean",
141
+ "description": "Host's node-pack manifests carry `EnvelopeContract` blocks."
142
+ }
143
+ },
144
+ "additionalProperties": false
145
+ },
146
+ "envelopes": {
147
+ "type": "object",
148
+ "description": "Envelope LLM-contract advertisement container introduced by the RFC 0030–0033 envelope-hardening track. Each sub-block is independently opt-in; hosts that adopt a subset advertise only the sub-blocks they implement. `additionalProperties: true` reserved for future RFCs that extend the track; host-private extensions go under `x-host-<host>-<key>` per `host-extensions.md` §\"Canonical-prefix table\".",
149
+ "additionalProperties": true,
150
+ "properties": {
151
+ "reasoning": {
152
+ "type": "object",
153
+ "description": "RFC 0030 §A + §C. Host's envelope payload schemas support the OPTIONAL `reasoning` field on kinds where multi-step reasoning materially improves output quality; the host's system-prompt-injection helper instructs the model to populate it. Absent block = no advertisement (the field MAY still appear on envelope schemas; this advertisement signals the host's prompt-injection posture).",
154
+ "additionalProperties": false,
155
+ "required": [
156
+ "supported"
157
+ ],
158
+ "properties": {
159
+ "supported": {
160
+ "type": "boolean",
161
+ "description": "Host's envelope payload schemas carry `reasoning` (OPTIONAL) on kinds where reasoning materially improves output quality, per RFC 0030 §A. Hosts SHOULD prompt the model to populate the field; hosts MUST NOT reject envelopes where `reasoning` is absent (the field is OPTIONAL by spec); hosts SHALL NOT route on `reasoning` contents."
162
+ },
163
+ "promptDirective": {
164
+ "type": "string",
165
+ "enum": [
166
+ "mandatory",
167
+ "advisory",
168
+ "off"
169
+ ],
170
+ "description": "Strength of the host's system-prompt instruction to populate `reasoning`. `mandatory`: the directive is firmly worded (the host instructs the model very forcefully to emit `reasoning`). `advisory` (default when absent): the directive is suggestive. `off`: the host emits no directive — applications that want `reasoning` populated must inject the instruction themselves. Note: `mandatory` is a prompt-injection posture, NOT a wire-level refusal contract — hosts MUST NOT reject envelopes where `reasoning` is absent regardless of this value."
171
+ }
172
+ }
173
+ },
174
+ "tierOneSubsetCompliance": {
175
+ "type": "string",
176
+ "enum": [
177
+ "strict",
178
+ "warn",
179
+ "off"
180
+ ],
181
+ "description": "RFC 0030 §B + §C. Host's self-attested compliance posture for the Tier-1 cross-vendor structured-output subset documented in `spec/v1/structured-output-subset.md`. `strict`: every host-served envelope schema passes the static subset-compliance check (object-root, `additionalProperties: false` everywhere, every property in `required`, no `oneOf`/`allOf`/`not`/`prefixItems`/`propertyNames`, no string format/pattern/length constraints, no number bounds, no array bounds, ≤5 nesting depth, ≤100 property count). `warn`: host serves non-compliant schemas but logs the violations. `off` (default when absent): no self-attestation. The conformance-suite static-subset scenario gates on this flag."
182
+ },
183
+ "reliability": {
184
+ "type": "object",
185
+ "additionalProperties": false,
186
+ "required": [
187
+ "supported"
188
+ ],
189
+ "description": "RFC 0032 §C. Host emits the envelope-reliability event family on documented adverse paths. Hosts opt into the family via `supported: true` AND explicitly list the events they emit via `events[]`. Hosts that advertise `supported: true` MUST include at least `envelope.retry.exhausted` and `envelope.refusal` in `events[]` (the two MUST-tier events). The other four (`envelope.retry.attempted`, `envelope.truncated`, `envelope.nlToFormat.engaged`, `envelope.recovery.applied`) are SHOULD/MAY-tier per RFC 0032 §B and may be omitted.",
190
+ "properties": {
191
+ "supported": {
192
+ "type": "boolean",
193
+ "description": "Host emits the RFC 0032 envelope-reliability event family on the documented adverse paths. When `false` or absent, conformance scenarios for the family soft-skip."
194
+ },
195
+ "events": {
196
+ "type": "array",
197
+ "items": {
198
+ "type": "string",
199
+ "enum": [
200
+ "envelope.retry.attempted",
201
+ "envelope.retry.exhausted",
202
+ "envelope.refusal",
203
+ "envelope.truncated",
204
+ "envelope.nlToFormat.engaged",
205
+ "envelope.recovery.applied"
206
+ ]
207
+ },
208
+ "uniqueItems": true,
209
+ "description": "Subset of the six reliability events the host actually emits. Hosts that advertise `supported: true` MUST include `envelope.retry.exhausted` and `envelope.refusal`. Conformance scenarios soft-skip for events absent from this list (the event-emission assertion is skipped; other host behavior assertions remain in force)."
210
+ },
211
+ "maxRetryAttempts": {
212
+ "type": "integer",
213
+ "minimum": 1,
214
+ "maximum": 16,
215
+ "description": "Host's retry budget per envelope emission. Conformance scenarios use this to construct fixtures that exercise the retry-exhausted path. Independent of `capabilities.limits.schemaRounds` (which is the engine-side per-emission cap) — `maxRetryAttempts` reports the host's actual configured value, not the spec's upper bound."
216
+ },
217
+ "completion": {
218
+ "type": "object",
219
+ "additionalProperties": false,
220
+ "required": [
221
+ "distinguishesTruncation"
222
+ ],
223
+ "description": "RFC 0033 §E. Host's self-attested envelope-completion contract posture — does the host distinguish truncation from schema-violation in its retry routing per RFC 0033 §A + §B + §C?",
224
+ "properties": {
225
+ "distinguishesTruncation": {
226
+ "type": "boolean",
227
+ "description": "Host implements RFC 0033's truncation-vs-schema-violation retry-routing distinction: truncation → increased output budget (NO corrective fragment); schema-violation → corrective system fragment (NO budget increase). When `false` or absent, the host conflates the two paths (legacy behavior); RFC 0033 conformance scenarios soft-skip."
228
+ },
229
+ "truncationBudgetMultiplier": {
230
+ "type": "number",
231
+ "minimum": 1,
232
+ "maximum": 8,
233
+ "description": "Host's per-attempt output-budget multiplier on truncation retries. Informational; clients MAY surface this in cost-estimation UIs. Defaults to 2 when absent and `distinguishesTruncation: true`. Spec recommendation is 2× per RFC 0033 §B."
234
+ }
235
+ }
236
+ }
237
+ }
238
+ }
239
+ }
240
+ },
241
+ "prompts": {
242
+ "type": "object",
243
+ "description": "RFC 0027 + RFC 0028 prompt-template support advertisement. The `supported` flag gates **node-execution resolution** of PromptRef values per RFC 0027 (Phase A); the separate `endpointsSupported` flag gates the **REST surface** at `/v1/prompts*` per RFC 0028 (Phase B). The two axes are independent — a host MAY implement Phase A composition without exposing the REST surface, and vice versa. Absent block = no support; consumers passing PromptRef values to such a host MUST tolerate the keys being treated as opaque strings.",
244
+ "required": [
245
+ "supported"
246
+ ],
247
+ "properties": {
248
+ "supported": {
249
+ "type": "boolean",
250
+ "description": "RFC 0027 Phase A. When `true`, the host resolves PromptRef values in `WorkflowNode.config.{systemPromptRef, userPromptRef, additionalPromptRefs}` at node-execution time AND emits `prompt.composed` run events per spec/v1/prompts.md §\"Composition + observability\". Does NOT imply the `/v1/prompts*` REST surface is available — see `endpointsSupported` for that. False or absent = the PromptRef keys on `WorkflowNode.config` are treated as opaque strings and never composed."
251
+ },
252
+ "endpointsSupported": {
253
+ "type": "boolean",
254
+ "description": "RFC 0028 §A. When `true`, the host serves the `/v1/prompts*` REST surface — at minimum the read endpoints (`listPromptTemplates`, `getPromptTemplate`, `renderPromptTemplate`). The mutating endpoints additionally require `mutableLibrary: true`; the pack-install path additionally requires `packsSupported: true`. False or absent = every `/v1/prompts*` request returns `501 capability_not_provided`. Independent of `supported` — a host MAY advertise `supported: true, endpointsSupported: false` (composition without library REST) or `supported: false, endpointsSupported: true` (library REST against an external resolver, e.g., a façade host)."
255
+ },
256
+ "templateKinds": {
257
+ "type": "array",
258
+ "items": {
259
+ "$ref": "./prompt-kind.schema.json"
260
+ },
261
+ "uniqueItems": true,
262
+ "description": "Subset of PromptKind values the host accepts. Defaults to all four (`system`, `user`, `few-shot`, `schema-hint`) when omitted. The $ref resolves to the shared `prompt-kind.schema.json` `$def` per RFC 0027 §A — uses a relative URI so redocly's lint walker resolves it against the local file rather than fetching the openwop.dev URL."
263
+ },
264
+ "variableSources": {
265
+ "type": "array",
266
+ "items": {
267
+ "type": "string",
268
+ "enum": [
269
+ "input",
270
+ "variable",
271
+ "secret",
272
+ "context"
273
+ ]
274
+ },
275
+ "uniqueItems": true,
276
+ "description": "Subset of PromptVariable.source values the host supports. `secret` SHOULD only appear when `capabilities.secrets.supported` is also true."
277
+ },
278
+ "maxTemplateBytes": {
279
+ "type": "integer",
280
+ "minimum": 1,
281
+ "maximum": 65536,
282
+ "description": "Host limit on PromptTemplate.text length. MUST NOT exceed the schema cap (65536). When advertised, the host MUST reject larger bodies on install + on `POST /v1/prompts` (RFC 0028)."
283
+ },
284
+ "observability": {
285
+ "type": "string",
286
+ "enum": [
287
+ "off",
288
+ "hashed",
289
+ "full"
290
+ ],
291
+ "description": "How `prompt.composed` events expose resolved bodies. `off`: event not emitted. `hashed`: payload carries only sha256 + per-variable-binding hashes (no plaintext). `full`: payload carries the composed body with secret-sourced values redacted to `[REDACTED:<secretId>]` markers and untrusted segments wrapped in `<UNTRUSTED>` markers. Default when absent: `hashed`."
292
+ },
293
+ "packsSupported": {
294
+ "type": "boolean",
295
+ "description": "RFC 0028 §C. When `true`, the host installs `kind: \"prompt\"` registry packs and exposes their templates at `GET /v1/prompts` with `meta.source: \"pack\"` + `meta.packName` + `meta.packVersion`. When `false` or absent, packs are not loaded; only host built-ins and (when `mutableLibrary: true`) user-created templates are visible."
296
+ },
297
+ "mutableLibrary": {
298
+ "type": "boolean",
299
+ "description": "RFC 0028 §C. When `true`, the host honors the mutating endpoints `POST /v1/prompts`, `PUT /v1/prompts/{templateId}`, `DELETE /v1/prompts/{templateId}`. When `false` or absent, those endpoints return 501. Pack-sourced and host-built-in templates remain read-only even under `mutableLibrary: true` — deletion of those returns 403 per RFC 0028 §A."
300
+ },
301
+ "library": {
302
+ "type": "object",
303
+ "additionalProperties": false,
304
+ "description": "RFC 0028 §C. Per-library configuration knobs that influence how clients call the prompt surface.",
305
+ "properties": {
306
+ "id": {
307
+ "type": "string",
308
+ "description": "Host's library identifier exposed at `GET /v1/prompts` (filterable via the structured `PromptRef.libraryId` field). Single-library hosts MAY omit this."
309
+ },
310
+ "renderEndpoint": {
311
+ "type": "string",
312
+ "format": "uri-reference",
313
+ "description": "Absolute or relative URI of the `:render` endpoint. Defaults to `/v1/prompts:render`. Useful for hosts mounting the surface under a prefix."
314
+ },
315
+ "maxRenderRequestBytes": {
316
+ "type": "integer",
317
+ "minimum": 1,
318
+ "description": "Hard cap on `POST /v1/prompts:render` request body size."
319
+ }
320
+ }
321
+ },
322
+ "defaults": {
323
+ "type": "object",
324
+ "additionalProperties": false,
325
+ "description": "RFC 0029 §B. Per-kind host-default PromptRefs that apply at resolution chain layer 4 (`host-defaults`) per `spec/v1/prompts.md` §\"Resolution chain (normative)\". Advertised so clients can preview the full chain at edit time without dispatching a node. Hosts MAY ship per-kind defaults; the openwop spec ships none.",
326
+ "properties": {
327
+ "system": {
328
+ "$ref": "./prompt-ref.schema.json"
329
+ },
330
+ "user": {
331
+ "$ref": "./prompt-ref.schema.json"
332
+ },
333
+ "few-shot": {
334
+ "$ref": "./prompt-ref.schema.json"
335
+ },
336
+ "schema-hint": {
337
+ "$ref": "./prompt-ref.schema.json"
338
+ }
339
+ }
340
+ },
341
+ "agentBindings": {
342
+ "type": "boolean",
343
+ "description": "RFC 0029 §B. When `true`, the host honors `AgentManifest.promptOverrides[kind]` and `AgentManifest.promptLibraryRef` at resolution chain layer 2 (`agent-overrides` / `agent-library-default`) per `spec/v1/prompts.md` §\"Resolution chain (normative)\". When `false` or absent, layer 2 is skipped — the host applies only layers 1, 3, 4. Conformance scenarios for agent-binding resolution are gated on this flag."
344
+ }
345
+ },
346
+ "additionalProperties": false
347
+ },
348
+ "extensions": {
349
+ "type": "object",
350
+ "description": "Optional per-canvas-type or per-workflow extensions (additional envelope types, override limits)."
351
+ },
352
+ "implementation": {
353
+ "type": "object",
354
+ "description": "Optional v1 server identity (name + version + vendor).",
355
+ "properties": {
356
+ "name": {
357
+ "type": "string"
358
+ },
359
+ "version": {
360
+ "type": "string"
361
+ },
362
+ "vendor": {
363
+ "type": "string"
364
+ }
365
+ },
366
+ "additionalProperties": false
367
+ },
368
+ "engineVersion": {
369
+ "type": "integer",
370
+ "minimum": 0,
371
+ "description": "Optional v1 server engineVersion (see version-negotiation.md). Independent of `protocolVersion`."
372
+ },
373
+ "eventLogSchemaVersion": {
374
+ "type": "integer",
375
+ "minimum": 0,
376
+ "description": "Optional v1 server eventLogSchemaVersion (per-run subcollection contract)."
377
+ },
378
+ "supportedTransports": {
379
+ "type": "array",
380
+ "items": {
381
+ "type": "string",
382
+ "enum": [
383
+ "rest",
384
+ "mcp",
385
+ "a2a",
386
+ "grpc"
387
+ ]
388
+ },
389
+ "description": "Optional v1 transport advertisement. REST is required whether or not this field is present."
390
+ },
391
+ "grpc": {
392
+ "type": "object",
393
+ "description": "RFC 0094 §H. gRPC transport advertisement per `grpc-transport.md` §\"Capability advertisement\". Optional — absent ⇒ the host exposes no gRPC transport. A host that exposes the gRPC surface advertises this block AND includes `\"grpc\"` in `supportedTransports`. REST + SSE remain exposed regardless.",
394
+ "required": [
395
+ "supported",
396
+ "service",
397
+ "tls"
398
+ ],
399
+ "properties": {
400
+ "supported": {
401
+ "type": "boolean",
402
+ "description": "Toggle — `true` when the gRPC surface is live."
403
+ },
404
+ "endpoint": {
405
+ "type": "string",
406
+ "minLength": 1,
407
+ "pattern": "^grpcs?://",
408
+ "description": "Full URI: `grpc://` (cleartext, intra-trusted-network only) OR `grpcs://` (TLS). Hosts SHOULD require TLS in production."
409
+ },
410
+ "service": {
411
+ "type": "string",
412
+ "const": "openwop.v1.Engine",
413
+ "description": "Canonical service name. v1 hosts MUST use `openwop.v1.Engine` per `grpc-transport.md` §\"Field semantics\"."
414
+ },
415
+ "tls": {
416
+ "type": "string",
417
+ "enum": [
418
+ "required",
419
+ "optional",
420
+ "disabled"
421
+ ],
422
+ "description": "TLS posture. Production hosts MUST set `\"required\"`."
423
+ }
424
+ },
425
+ "additionalProperties": false
426
+ },
427
+ "configurable": {
428
+ "type": "object",
429
+ "description": "Optional v1 per-run overlay schema — what `RunOptions.configurable` keys this server honors."
430
+ },
431
+ "nodePackRuntimes": {
432
+ "type": "object",
433
+ "description": "Optional v1 advertisement of node-pack runtimes the host loads. See `node-packs.md` §runtime formats and RFC 0008 (WASM ABI). Hosts that don't load packs MAY omit this block entirely. NOTE: this block intentionally uses `additionalProperties: true` (here and on the nested runtime objects) so a future RFC may add a runtime type (e.g., `python-wasm`, `js-wasm`) without a breaking-change rev of this schema; the trade is that a strict-mode validator will accept arbitrary extra keys under these objects. A future RFC that promotes the open-set fields to first-class SHOULD tighten them to `additionalProperties: false` in the same RFC.",
434
+ "properties": {
435
+ "wasm": {
436
+ "type": "object",
437
+ "description": "WASM core-module runtime per RFC 0008. Hosts that load `runtime.language: \"wasm\"` packs MUST advertise `supported: true` and at least one entry in `abiVersions[]`. `additionalProperties: true` preserved for future RFC 0008 amendments (e.g., per-pack memory accounting fields).",
438
+ "required": [
439
+ "supported"
440
+ ],
441
+ "properties": {
442
+ "supported": {
443
+ "type": "boolean",
444
+ "description": "Host loads RFC 0008 WASM packs."
445
+ },
446
+ "abiVersions": {
447
+ "type": "array",
448
+ "items": {
449
+ "type": "integer",
450
+ "minimum": 1
451
+ },
452
+ "uniqueItems": true,
453
+ "minItems": 1,
454
+ "description": "ABI versions the loader accepts. v1.1 hosts MUST include `1`. Packs declaring `openwop_abi_version()` outside this list MUST be rejected at load time per RFC 0008 §H."
455
+ },
456
+ "maxMemoryBytes": {
457
+ "type": "integer",
458
+ "minimum": 1048576,
459
+ "maximum": 8589934592,
460
+ "description": "Per-pack memory ceiling enforced by the host loader. Range: 1 MiB ≤ value ≤ 8 GiB. RFC 0008 §K — when a pack exceeds this, the host MUST emit `cap.breached` with `kind: \"wasm-memory\"`."
461
+ },
462
+ "loadedPacks": {
463
+ "type": "array",
464
+ "items": {
465
+ "type": "string",
466
+ "minLength": 1
467
+ },
468
+ "uniqueItems": true,
469
+ "description": "Pack names that passed instantiation (ABI check + load). Packs rejected per RFC 0008 §H MUST NOT appear here. Hosts MAY advertise this for observability; conformance asserts rejection by absence (Track 7)."
470
+ }
471
+ },
472
+ "additionalProperties": true
473
+ },
474
+ "wasmComponent": {
475
+ "type": "object",
476
+ "description": "WASM Component Model variant (WIT-defined interfaces). Reserved for hosts that load `runtime.language: \"wasm-component\"` packs. `additionalProperties: true` preserved until the Component Model spec stabilises in the v1.x line; tighten in the RFC that promotes wasm-component to a first-class runtime alongside `wasm`.",
477
+ "properties": {
478
+ "supported": {
479
+ "type": "boolean"
480
+ }
481
+ },
482
+ "additionalProperties": true
483
+ }
484
+ },
485
+ "additionalProperties": true
486
+ },
487
+ "observability": {
488
+ "type": "object",
489
+ "description": "Optional v1 hints about emitted spans/metrics/logs. See observability.md.",
490
+ "properties": {
491
+ "otel": {
492
+ "type": "object",
493
+ "description": "OTel-specific advertisement. Hosts that emit OTLP traces/metrics MAY advertise the transport protocols they support so collectors can configure exporters accordingly. Track 11 follow-up.",
494
+ "properties": {
495
+ "exportProtocols": {
496
+ "type": "array",
497
+ "items": {
498
+ "type": "string",
499
+ "enum": [
500
+ "http/json",
501
+ "http/protobuf",
502
+ "grpc"
503
+ ]
504
+ },
505
+ "uniqueItems": true,
506
+ "description": "OTLP export protocols the host supports emitting. `http/json` and `http/protobuf` are mandatory for hosts advertising OTel emission; `grpc` is opt-in per Track 11."
507
+ }
508
+ },
509
+ "additionalProperties": true
510
+ },
511
+ "testSeams": {
512
+ "type": "object",
513
+ "additionalProperties": false,
514
+ "description": "RFC 0034 — Conformance-only test seams under the host-extension namespace. Hosts that opt in expose introspection endpoints so cross-host conformance scenarios can verify BYOK canaries do not leak into OTel span attributes or debug-bundle exports. Production hosts SHOULD return 404 or 403 from these seams unless an env-gate (e.g., `OPENWOP_TEST_OTEL_SCRAPE=true`) is set.",
515
+ "properties": {
516
+ "otelScrape": {
517
+ "type": "boolean",
518
+ "description": "Host exposes `GET /v1/host/sample/test/otel/spans?runId=<id>` returning `{ spans: Array<{ name, attributes, events }> }` scoped to the run. Conformance verifies span attributes don't carry BYOK canaries. When advertised true, the host MUST serve a 200 response with the documented shape."
519
+ },
520
+ "debugBundleExport": {
521
+ "type": "boolean",
522
+ "description": "Host exposes `POST /v1/host/sample/test/debug-bundle/export` returning the same payload shape as `GET /v1/runs/{runId}/debug-bundle` per `spec/v1/debug-bundle.md`. Conformance verifies the serialized bundle doesn't carry BYOK canaries. When advertised true, the host MUST serve a 200 response with the documented shape."
523
+ }
524
+ }
525
+ }
526
+ },
527
+ "additionalProperties": true
528
+ },
529
+ "minClientVersion": {
530
+ "type": "string",
531
+ "description": "Optional v1 minimum client SDK version the server interops with."
532
+ },
533
+ "secrets": {
534
+ "type": "object",
535
+ "description": "Optional v1 secret/credential resolution advertisement. Clients gate BYOK flows on this. Hosts that don't store credentials return `supported: false`.",
536
+ "required": [
537
+ "supported"
538
+ ],
539
+ "properties": {
540
+ "supported": {
541
+ "type": "boolean",
542
+ "description": "Host has any secret-resolution at all."
543
+ },
544
+ "scopes": {
545
+ "type": "array",
546
+ "items": {
547
+ "type": "string",
548
+ "enum": [
549
+ "tenant",
550
+ "user",
551
+ "run",
552
+ "workspace"
553
+ ]
554
+ },
555
+ "uniqueItems": true,
556
+ "description": "Subset of scopes the host implements. Tenant-scoped secrets are workspace-shared; user-scoped are per-end-user; run-scoped are ephemeral per-run; `workspace` (RFC 0046/0048) is the explicit sub-tenant scope. Appended `workspace` is additive — hosts that omit it are unaffected."
557
+ },
558
+ "resolution": {
559
+ "type": "string",
560
+ "enum": [
561
+ "host-managed"
562
+ ],
563
+ "description": "Resolution mode. v1.x supports only `host-managed` (clients reference stored secrets via opaque ids); reserved for future modes."
564
+ }
565
+ },
566
+ "additionalProperties": false
567
+ },
568
+ "connections": {
569
+ "type": "object",
570
+ "description": "RFC 0095 (`Draft`). Connection packs — portable, registry-distributable provider definitions (`kind: \"connection\"`, `connection-pack-manifest.schema.json`) that the RFC 0045/0047 `provider` string resolves against. Only useful alongside `oauth.supported` (RFC 0047) or `credentials.supported` (RFC 0046); a host SHOULD NOT advertise this block without at least one of those.",
571
+ "required": [
572
+ "packsSupported"
573
+ ],
574
+ "properties": {
575
+ "supported": {
576
+ "type": "boolean",
577
+ "description": "OPTIONAL convenience flag mirroring the other capability families. RFC 0095 keys behavior on `packsSupported`; hosts MAY also advertise `supported` for family-shape uniformity."
578
+ },
579
+ "packsSupported": {
580
+ "type": "boolean",
581
+ "description": "RFC 0095 §C. When `true`, the host installs `kind: \"connection\"` registry packs and MUST implement the §B.6 resolution contract: an RFC 0045 connector's `auth.provider` (or an RFC 0047 `host.oauth` provider string) resolves against the installed connection pack whose `provider.id` matches, with installed-vs-built-in precedence per SemVer §11 and `connection_provider_unresolved` / `connection_provider_conflict` diagnostics. When `false` or absent, connection packs are not loaded and provider resolution stays implementation-defined."
582
+ }
583
+ },
584
+ "additionalProperties": false
585
+ },
586
+ "selfHostedRunner": {
587
+ "type": "object",
588
+ "additionalProperties": false,
589
+ "description": "RFC 0122 (`Active`). The host routes a run's per-step model/tool dispatch to a user-controlled RUNNER that dials OUT to the host over a persistent channel (SSE receive + POST result) and holds the local credentials the host cannot reach (a subscription CLI login per RFC 0121, a private endpoint per RFC 0108). The host stays the sole orchestration/persistence/replay authority; the runner is a stateless dispatch executor. This is the discovery-time capability shape ONLY; the per-subject `SelfHostedRunnerRegistration` record stays OFF `/.well-known/openwop`. A host MUST NOT advertise `supported: true` until RFC 0122 reaches `Accepted` and the channel is honored (truthful-advertisement, `OPENWOP_REQUIRE_BEHAVIOR=true`). Absent block ⇒ no runner advertisement.",
590
+ "required": [
591
+ "supported"
592
+ ],
593
+ "properties": {
594
+ "supported": {
595
+ "type": "boolean",
596
+ "description": "RFC 0122. When `true`, the host accepts runner registrations and routes matching dispatch steps to a registered runner owned by the run's subject. When `false` or absent, no runner channel is offered."
597
+ },
598
+ "dispatchKinds": {
599
+ "type": "array",
600
+ "items": {
601
+ "type": "string",
602
+ "enum": [
603
+ "model",
604
+ "tool"
605
+ ]
606
+ },
607
+ "uniqueItems": true,
608
+ "description": "OPTIONAL. Which dispatch kinds the host routes to runners. A host MAY implement `model` first and add `tool` later behind the same capability gate (openwop-app ADR 0182 Phase 5 lands model-dispatch first). Absent ⇒ implementation-defined, but a host SHOULD enumerate what it actually routes."
609
+ }
610
+ }
611
+ },
612
+ "purposePropagation": {
613
+ "type": "object",
614
+ "additionalProperties": false,
615
+ "description": "RFC 0128 (`Active`). Purpose-propagation — the host reads and RE-EMITS `permittedPurposes` labels (opaque purpose strings riding the A2A `metadata.openwop.permittedPurposes` extension and the `TriggerEvent.permittedPurposes` field) on onward OpenWOP-envelope hops of the same data, narrowing at most and never widening; a `[]` label is honored as no-onward-use. This family advertises label PROPAGATION only — it does NOT claim the host enforces purpose limits on its own internal use (that is declared intent under the receiver's local governance, RFC 0128 §4, deliberately not conformance-gated). A host MUST NOT advertise `supported: true` until RFC 0128 reaches `Accepted` and propagation is honored (truthful-advertisement, `OPENWOP_REQUIRE_BEHAVIOR=true`). Absent block ⇒ labels pass through unread as unknown metadata (pre-RFC behavior).",
616
+ "required": [
617
+ "supported"
618
+ ],
619
+ "properties": {
620
+ "supported": {
621
+ "type": "boolean",
622
+ "description": "RFC 0128. When `true`, the host preserves and re-emits received `permittedPurposes` labels per RFC 0128 §3 (re-emit on OpenWOP-envelope onward hops; MAY narrow; MUST NOT widen; derived outputs never carry a purpose absent from any contributing labelled input; `[]` ⇒ no onward hop)."
623
+ },
624
+ "propagatesOnward": {
625
+ "type": "boolean",
626
+ "description": "RFC 0128. When `true`, the host's onward OpenWOP-envelope hops (A2A forwards, trigger/sync events to OpenWOP peers) re-emit the label. Distinct from `supported` only for hosts that read labels but have no onward-hop surface at all; a host WITH onward hops advertising `supported: true` MUST also propagate."
627
+ }
628
+ }
629
+ },
630
+ "dataResidency": {
631
+ "type": "object",
632
+ "additionalProperties": false,
633
+ "description": "RFC 0129 (`Active`). Data-residency — the host advertises the operator region codes it can pin a run to, and honors-or-rejects an OPTIONAL `residency.region` on run-creation (`POST /v1/runs`): accept iff the requested region is in `regions[]`, else reject `residency_unavailable` (never silently accept-and-ignore). This family advertises the ADMISSION decision only (falsifiable over the wire) — it does NOT assert that the run's data physically remains in-region (unobservable; declared operator intent under RFC 0129 §4, not conformance-gated). A host MUST advertise a region in `regions[]` only if it can actually process a run entirely within it. Absent block ⇒ the host makes no residency promise and MAY ignore or reject a `residency` constraint (but MUST NOT claim to honor it).",
634
+ "required": [
635
+ "supported",
636
+ "regions"
637
+ ],
638
+ "properties": {
639
+ "supported": {
640
+ "const": true,
641
+ "description": "RFC 0129. Present-and-true when the host performs region admission control per §3. The block is omitted entirely when the host offers no residency pinning."
642
+ },
643
+ "regions": {
644
+ "type": "array",
645
+ "items": {
646
+ "type": "string",
647
+ "minLength": 1
648
+ },
649
+ "uniqueItems": true,
650
+ "description": "RFC 0129. Opaque operator region codes the host can pin a run to (e.g. `\"eu\"`, `\"us\"`, `\"eu-west-1\"`). No closed registry — codes are the host's own vocabulary (a global region enum would be unfalsifiable). A host MUST advertise a code here only if it can process a run entirely within it; a run-create `residency.region` not in this list MUST be rejected `residency_unavailable`."
651
+ }
652
+ }
653
+ },
654
+ "anonymousActor": {
655
+ "type": "object",
656
+ "additionalProperties": false,
657
+ "description": "RFC 0132 (`Active`). Anonymous-actor authorization for public agent surfaces — an opaque, origin-bound, ephemeral, non-cross-linkable, non-PII principal (`run-snapshot.owner.principalKind: \"anonymous\"`) whose authority is a DEFAULT-DENY, explicit per-surface tool grant, never the default-on tool baseline (§C.1). Two tiers: `read` (tenant-scoped, no egress, no secret/BYOK reach — §C.2) and `bounded-write-egress` (writes/egress permitted ONLY behind a mandatory HITL/approval gate (RFC 0051) OR a hard rate-limit + per-session cap, over the RFC 0076 §B / RFC 0079 SSRF-guarded audience-bound egress path, never attaching a tenant credential out-of-audience — §C.3). Every anon tool call emits `authorization.decided` (RFC 0049) attributable to the opaque session id, carrying no PII/credential (§D). The block is OMITTED ENTIRELY when unsupported (const-true `supported`, `dataResidency`-style — never `supported: false`). Absent ⇒ the host runs no tool-enabled public surface under RFC 0132 (e.g. a no-tools single-turn public gateway needs no advertisement).",
658
+ "required": [
659
+ "supported",
660
+ "tiers"
661
+ ],
662
+ "properties": {
663
+ "supported": {
664
+ "const": true,
665
+ "description": "RFC 0132. Present-and-true when the host honors anonymous-actor authorization per §C. The block is omitted entirely when the host runs no tool-enabled public surface (never `supported: false`)."
666
+ },
667
+ "tiers": {
668
+ "type": "array",
669
+ "minItems": 1,
670
+ "uniqueItems": true,
671
+ "items": {
672
+ "enum": [
673
+ "read",
674
+ "bounded-write-egress"
675
+ ]
676
+ },
677
+ "description": "RFC 0132 §B. The anon capability tiers this host BEHAVIORALLY honors (truthful-advertisement — a tier the host does not enforce per §C is a dishonest claim; `OPENWOP_REQUIRE_BEHAVIOR=true` MUST fail it). `read`: tenant-scoped reads, no egress, no secrets (§C.2). `bounded-write-egress`: writes/egress ONLY behind a §C.3 control."
678
+ },
679
+ "writeEgressControls": {
680
+ "type": "array",
681
+ "minItems": 1,
682
+ "uniqueItems": true,
683
+ "items": {
684
+ "enum": [
685
+ "hitl",
686
+ "rate-limit-session-cap"
687
+ ]
688
+ },
689
+ "description": "RFC 0132 §B. REQUIRED and non-empty IFF `bounded-write-egress` ∈ `tiers`; MUST be absent otherwise. The mandatory control(s) the host enforces before an anon write/egress: `hitl` = a per-action approval gate (RFC 0051 — the action suspends pending a human decision); `rate-limit-session-cap` = a hard per-IP/per-window rate limit AND a per-session action cap. A `bounded-write-egress` tier with no advertised control is the fail-open shape §B.2 forbids."
690
+ },
691
+ "failClosed": {
692
+ "const": true,
693
+ "description": "RFC 0132. An anon tool call whose grant is absent, unresolvable, or errors MUST deny (never default-allow). There is no fail-open anonymous mode. Mirrors `capabilities.authorization.failClosed`."
694
+ }
695
+ },
696
+ "allOf": [
697
+ {
698
+ "$comment": "RFC 0132 §B.2 — writeEgressControls is REQUIRED and non-empty when bounded-write-egress is advertised, and MUST be absent otherwise (a control-less write/egress tier is the fail-open shape this RFC forbids).",
699
+ "if": {
700
+ "properties": {
701
+ "tiers": {
702
+ "contains": {
703
+ "const": "bounded-write-egress"
704
+ }
705
+ }
706
+ },
707
+ "required": [
708
+ "tiers"
709
+ ]
710
+ },
711
+ "then": {
712
+ "required": [
713
+ "writeEgressControls"
714
+ ]
715
+ },
716
+ "else": {
717
+ "not": {
718
+ "required": [
719
+ "writeEgressControls"
720
+ ]
721
+ }
722
+ }
723
+ }
724
+ ]
725
+ },
726
+ "credentials": {
727
+ "type": "object",
728
+ "description": "RFC 0046 (`Draft`). Portable credential resolution + lifecycle contract — sibling to `secrets`, first-class store-at-rest + workspace sharing + two-key-overlap rotation. A pack references a credential by `{ ref, scope }` (see `credential-reference.schema.json`); the host resolves it into the node sandbox ONLY — never into inputs, persisted variables, channels, any run.* event payload, the debug bundle, or replay state (SECURITY invariant `credential-payload-redaction`). Supersedes the informal BYOK annex; the `secrets` advertisement stays valid.",
729
+ "required": [
730
+ "supported"
731
+ ],
732
+ "properties": {
733
+ "supported": {
734
+ "type": "boolean",
735
+ "description": "Host implements the host.credentials resolution + lifecycle contract."
736
+ },
737
+ "scopes": {
738
+ "type": "array",
739
+ "items": {
740
+ "type": "string",
741
+ "enum": [
742
+ "user",
743
+ "workspace",
744
+ "tenant"
745
+ ]
746
+ },
747
+ "uniqueItems": true,
748
+ "description": "Subset of resolution scopes the host implements. `workspace` is the RFC 0048 sub-tenant; `tenant` and `user` align with the `secrets.scopes` vocabulary."
749
+ },
750
+ "encryptionAtRest": {
751
+ "type": "boolean",
752
+ "description": "Host encrypts stored credential material at rest."
753
+ },
754
+ "rotation": {
755
+ "type": "string",
756
+ "enum": [
757
+ "none",
758
+ "two-key-overlap"
759
+ ],
760
+ "description": "`two-key-overlap`: old + new credential both resolve as valid during a grace window, then the old fails with `credential_not_found` (mirrors `openwop-auth-api-key-rotation`). `none`: no rotation surface."
761
+ },
762
+ "sharing": {
763
+ "type": "boolean",
764
+ "description": "A single stored credential can be referenced by many workflows within a scope (e.g. a workspace-shared key) without copying material between references."
765
+ }
766
+ },
767
+ "additionalProperties": false
768
+ },
769
+ "feedback": {
770
+ "type": "object",
771
+ "description": "RFC 0056 (`Draft`). Non-blocking human/agent quality signals (rating / correction / label / flag) attached to a run, event, or node. Annotations are a per-run side-resource recorded via `POST /v1/runs/{runId}/annotations`, listed via `GET`, and surfaced live via the `run.annotated` SSE notification — they are NOT entries in the replayable run event log (see RFC 0056 §B/§D). Hosts that do not advertise `supported: true` return `501 capability_not_provided` on the annotation endpoints.",
772
+ "required": [
773
+ "supported"
774
+ ],
775
+ "properties": {
776
+ "supported": {
777
+ "type": "boolean",
778
+ "description": "Host implements the RFC 0056 annotation side-store + endpoints + `run.annotated` notification."
779
+ },
780
+ "targets": {
781
+ "type": "array",
782
+ "items": {
783
+ "type": "string",
784
+ "enum": [
785
+ "run",
786
+ "event",
787
+ "node"
788
+ ]
789
+ },
790
+ "uniqueItems": true,
791
+ "description": "Which annotation-target granularities the host accepts. Absent = `run` only."
792
+ },
793
+ "signals": {
794
+ "type": "array",
795
+ "items": {
796
+ "type": "string",
797
+ "enum": [
798
+ "rating",
799
+ "correction",
800
+ "label",
801
+ "flag"
802
+ ]
803
+ },
804
+ "uniqueItems": true,
805
+ "description": "Which signal kinds the host accepts. Absent = all four."
806
+ }
807
+ },
808
+ "additionalProperties": false
809
+ },
810
+ "replay": {
811
+ "type": "object",
812
+ "description": "Replay / fork surface (`spec/v1/replay.md`; profile `openwop-replay-fork`). DECLARED FOR THE FIRST TIME BY RFC 0140 — hosts have emitted this block since the fork endpoint shipped, but it was never in this schema and validated only via the root's `additionalProperties: true`. The pre-existing fields are declared here exactly as already emitted and consumed, so no host's current document is invalidated; `additionalProperties` stays permissive for the same reason.",
813
+ "properties": {
814
+ "supported": {
815
+ "type": "boolean",
816
+ "description": "Host implements `POST /v1/runs/{runId}:fork`."
817
+ },
818
+ "modes": {
819
+ "type": "array",
820
+ "items": {
821
+ "type": "string",
822
+ "enum": [
823
+ "replay",
824
+ "branch"
825
+ ]
826
+ },
827
+ "uniqueItems": true,
828
+ "description": "Fork modes the host implements. `replay` = deterministic re-execution; `branch` = divergent execution with an optional `runOptionsOverlay`. A host supporting only `branch` still satisfies the `openwop-replay-fork` predicate."
829
+ },
830
+ "fork": {
831
+ "type": "boolean",
832
+ "description": "Legacy spelling retained for existing consumers; `modes` is authoritative."
833
+ },
834
+ "retention": {
835
+ "type": "object",
836
+ "properties": {
837
+ "windowSeconds": {
838
+ "type": "integer",
839
+ "minimum": 0,
840
+ "description": "How long a run stays fork-eligible (`replay.md` §\"Retention and garbage collection\"). OPTIONAL — the spec requires hosts to document retention, not to advertise the window."
841
+ }
842
+ },
843
+ "description": "OPTIONAL retention metadata."
844
+ },
845
+ "sideEffectSuppression": {
846
+ "type": "string",
847
+ "enum": [
848
+ "recorded-outcome",
849
+ "none"
850
+ ],
851
+ "default": "none",
852
+ "description": "RFC 0140. An ASSURANCE ADVERTISEMENT of the mechanism by which the host meets `replay.md` §\"Determinism guarantees\" caveat 1 for a `mode:\"replay\"` fork — NOT a gate on the obligation, which is unconditional. `recorded-outcome`: a side-effecting node does NOT execute during a replay — the host reproduces the source run's recorded terminal outcome for the same `(nodeId, attempt)`, or fails the node closed with `replay_source_missing`. The guarantee is WHOLE-RUN: a host MUST NOT advertise it if any class of side-effecting node can still fire during a replay (a partial guarantee would violate `replay.md` §C.2 observable-state equivalence for the unsuppressed classes). `none` (default; absent means this) — NO mechanism is declared. The host remains bound by `replay.md` §\"Determinism guarantees\" caveat 1 (an unconditional MUST that a node calling an external API consult the durable invocation log, so the external system is NOT called twice) exactly as before; conformance simply has nothing to probe. This is NOT permission to re-fire effects. Scoped to `replay` mode; `branch` is a new execution whose effects the operator asked for."
853
+ }
854
+ }
855
+ },
856
+ "oauth": {
857
+ "type": "object",
858
+ "description": "RFC 0047 (`Draft`). Host performs OAuth 2.0 grants (authorization-code + refresh) on a user's behalf for connector nodes, stores the acquired token as a `host.credentials` (RFC 0046) entry, refreshes it transparently, and resolves it into the node sandbox as a bearer token. Token material NEVER crosses the wire (SECURITY invariant `credential-payload-redaction`). Distinct from `auth` host-authentication profiles (RFC 0010 = who is the caller; this = what third-party token a node holds).",
859
+ "required": [
860
+ "supported"
861
+ ],
862
+ "properties": {
863
+ "supported": {
864
+ "type": "boolean",
865
+ "description": "Host implements the host.oauth third-party token acquisition + refresh contract."
866
+ },
867
+ "grants": {
868
+ "type": "array",
869
+ "items": {
870
+ "type": "string",
871
+ "enum": [
872
+ "authorization_code",
873
+ "client_credentials",
874
+ "refresh_token"
875
+ ]
876
+ },
877
+ "uniqueItems": true,
878
+ "description": "OAuth 2.0 grant types the host performs on a node's behalf."
879
+ },
880
+ "providers": {
881
+ "type": "array",
882
+ "description": "Provider catalog the host can acquire tokens for. A connector node's `auth.provider` MUST match an `id` here.",
883
+ "items": {
884
+ "type": "object",
885
+ "required": [
886
+ "id"
887
+ ],
888
+ "properties": {
889
+ "id": {
890
+ "type": "string",
891
+ "minLength": 1,
892
+ "description": "Stable provider id, e.g. `slack`, `google`."
893
+ },
894
+ "authUrl": {
895
+ "type": "string",
896
+ "format": "uri",
897
+ "description": "Authorization endpoint."
898
+ },
899
+ "tokenUrl": {
900
+ "type": "string",
901
+ "format": "uri",
902
+ "description": "Token endpoint."
903
+ },
904
+ "scopesSupported": {
905
+ "type": "array",
906
+ "items": {
907
+ "type": "string"
908
+ },
909
+ "description": "Scopes this provider exposes."
910
+ }
911
+ },
912
+ "additionalProperties": false
913
+ }
914
+ }
915
+ },
916
+ "additionalProperties": false
917
+ },
918
+ "authorization": {
919
+ "type": "object",
920
+ "description": "RFC 0049 (`Draft`). Maps an RFC 0048 principal's role to scopes (reusing the API-key scope grammar in `auth.md`) and surfaces authorization decisions as `authorization.decided` events. Fail-closed: an absent/unseeded role denies (SECURITY invariant `authorization-fail-closed`).",
921
+ "required": [
922
+ "supported"
923
+ ],
924
+ "properties": {
925
+ "supported": {
926
+ "type": "boolean",
927
+ "description": "Host implements the role→scope authorization-decision contract."
928
+ },
929
+ "failClosed": {
930
+ "const": true,
931
+ "description": "Absent/unseeded role denies; resolver errors deny. MUST be true when present — see SECURITY invariant `authorization-fail-closed`."
932
+ },
933
+ "roles": {
934
+ "type": "array",
935
+ "description": "Role catalog. A principal's role resolves to this scope set; a request is authorized when any role-derived scope matches the required scope per the API-key scope-match semantics.",
936
+ "items": {
937
+ "type": "object",
938
+ "required": [
939
+ "role",
940
+ "scopes"
941
+ ],
942
+ "properties": {
943
+ "role": {
944
+ "type": "string",
945
+ "minLength": 1
946
+ },
947
+ "scopes": {
948
+ "type": "array",
949
+ "items": {
950
+ "type": "string",
951
+ "minLength": 1
952
+ },
953
+ "uniqueItems": true
954
+ }
955
+ },
956
+ "additionalProperties": false
957
+ }
958
+ }
959
+ },
960
+ "additionalProperties": false
961
+ },
962
+ "runtimeCapabilities": {
963
+ "type": "array",
964
+ "items": {
965
+ "type": "string",
966
+ "minLength": 1
967
+ },
968
+ "uniqueItems": true,
969
+ "description": "Optional v1 host-advertised opaque capability ids that NodeModules may declare in `NodeModule.requires`. Naming convention: dotted, domain-scoped (`chat.sendPrompt`, `canvas.write`, `secrets.byok`). Provider value shapes are documented per-capability alongside consumers, NOT in the protocol package — the protocol owns the *check*, not domain provider contracts. A client that submits a workflow whose nodes declare a `requires` entry SHOULD first verify the host advertises that capability; a host that lacks a capability MUST refuse to dispatch nodes that declare it, terminating the run with `RunSnapshot.error.code = 'capability_not_provided'`. See `capabilities.md` §\"Runtime capabilities\"."
970
+ },
971
+ "multiPartyConversation": {
972
+ "type": "object",
973
+ "description": "RFC 0101 — Multi-party group conversation (shared transcript + speaker attribution). When advertised with `supported: true`, the host honors the RFC 0101 normative contract: (1) it accepts an OPTIONAL `participants: AgentRef[]` roster on `conversation.opened` (`conversation-event.schema.json`); (2) it REQUIRES a `speakerId` (the roster INSTANCE id, `host:<id>` per RFC 0086) on every `role: 'agent'` turn; and (3) when a `participants` roster is present it MUST reject a turn whose `speakerId` is not a roster member. Absent block ⇒ no advertisement: the host runs single-agent conversations (RFC 0005) and treats `participants`/`speakerId` as opaque, unenforced fields. Advertising `supported: true` without honoring (1)+(2)+(3) is a dishonest wire claim (`OPENWOP_REQUIRE_BEHAVIOR=true` fails the gated conformance scenarios). Host product policy — turn-taking order, round count, synchronous vs. async rounds — stays NON-normative (RFC 0101 §Spec); only roster membership, agent-turn attribution, and this capability are normative. Requires `conversationPrimitive: true` (RFC 0005) — this block extends the single-agent conversation primitive, it does not replace it.",
974
+ "required": [
975
+ "supported"
976
+ ],
977
+ "additionalProperties": false,
978
+ "properties": {
979
+ "supported": {
980
+ "type": "boolean",
981
+ "description": "RFC 0101. Host enforces the multi-party roster + speaker-attribution contract. When `false` or absent, RFC 0101 conformance scenarios soft-skip and the host applies no roster/attribution enforcement."
982
+ },
983
+ "maxParticipants": {
984
+ "type": "integer",
985
+ "minimum": 2,
986
+ "description": "RFC 0101. OPTIONAL host ceiling on the size of the `conversation.opened.participants` roster. A multi-party council has at least 2 participants. When advertised, the host MUST reject a `conversation.opened` whose `participants` array exceeds this count. Absent ⇒ the host does not bound the roster size on the wire."
987
+ }
988
+ }
989
+ },
990
+ "conversationTurnModelProvenance": {
991
+ "type": "object",
992
+ "description": "RFC 0109 — Conversation-turn model provenance. When advertised with `supported: true`, the host stamps the OPTIONAL `agent.model` object (`{ provider, model }`) on `role: 'agent'` conversation turns (`conversation-turn.schema.json`), recording which model produced the turn. The stamp is NON-SECRET + NON-PII (`additionalProperties: false` on `agent.model` forbids any credential/endpoint/prompt — the SR-1 guard) and is read VERBATIM on `:fork` (never re-resolved, so a forked transcript preserves the original provenance). Absent block ⇒ no advertisement: the host omits `agent.model` and treats it as an opaque, unenforced field. Advertising `supported: true` without stamping is a dishonest wire claim (`OPENWOP_REQUIRE_BEHAVIOR=true` fails the gated scenario). Additive over RFC 0005 — extends the conversation primitive, does not replace it.",
993
+ "required": [
994
+ "supported"
995
+ ],
996
+ "additionalProperties": false,
997
+ "properties": {
998
+ "supported": {
999
+ "type": "boolean",
1000
+ "description": "RFC 0109. Host stamps `agent.model` ({ provider, model }) on `role: 'agent'` conversation turns. When `false` or absent, the RFC 0109 conformance scenario soft-skips and the host emits no model provenance."
1001
+ }
1002
+ }
1003
+ },
1004
+ "channelPresence": {
1005
+ "type": "object",
1006
+ "description": "RFC 0110 — Channel presence (online + typing). When advertised with `supported: true`, the host emits the OPTIONAL `channel.presence` RunEvent (`channel-presence-payload.schema.json`) for a `type:'channel'` conversation, carrying the currently-present member subject refs + optional per-member typing. Presence is EPHEMERAL live state: the host MUST NOT persist it to the replayable event log / transcript and it MUST NOT affect replay or `:fork` (the load-bearing distinction from the persisted `conversation.exchanged` turn). Membership-gated: every ref MUST be a current participant and the event MUST NOT be delivered to a non-member (DEFAULT-DENY, CTI-1). NON-PII (opaque RFC 0041 subject refs only). Absent block ⇒ no advertisement: the host emits no presence. Advertising `supported: true` without emitting is a dishonest wire claim (`OPENWOP_REQUIRE_BEHAVIOR=true` fails the gated scenario). Additive over RFC 0005.",
1007
+ "required": [
1008
+ "supported"
1009
+ ],
1010
+ "additionalProperties": false,
1011
+ "properties": {
1012
+ "supported": {
1013
+ "type": "boolean",
1014
+ "description": "RFC 0110. Host emits the ephemeral `channel.presence` RunEvent for channel conversations. When `false` or absent, the RFC 0110 conformance scenario soft-skips and the host emits no presence."
1015
+ }
1016
+ }
1017
+ },
1018
+ "multiAgent": {
1019
+ "type": "object",
1020
+ "description": "RFC 0037 — Multi-agent execution model + handoff state machine. Hosts that advertise implement the supervisor→dispatch→harvest loop + the 4-state handoff state machine + the `core.workflowChain.event` emission contract per spec/v1/multi-agent-execution.md. Absent block = host implements RFCs 0006/0007/0022 individually with implementation flexibility on integration semantics; conformance scenarios gating on this flag soft-skip on absence.",
1021
+ "additionalProperties": false,
1022
+ "properties": {
1023
+ "executionModel": {
1024
+ "type": "object",
1025
+ "additionalProperties": false,
1026
+ "required": [
1027
+ "supported",
1028
+ "version"
1029
+ ],
1030
+ "if": {
1031
+ "properties": {
1032
+ "tier": {
1033
+ "const": "experimental"
1034
+ }
1035
+ },
1036
+ "required": [
1037
+ "tier"
1038
+ ]
1039
+ },
1040
+ "then": {
1041
+ "required": [
1042
+ "experimentalUntil"
1043
+ ]
1044
+ },
1045
+ "properties": {
1046
+ "supported": {
1047
+ "type": "boolean",
1048
+ "description": "Host implements the execution loop + handoff state machine per spec/v1/multi-agent-execution.md §\"Execution loop\" + §\"Handoff state machine\". When true, the host MUST emit `core.workflowChain.event` records on every handoff transition per the §\"Transition events\" table."
1049
+ },
1050
+ "tier": {
1051
+ "type": "string",
1052
+ "enum": [
1053
+ "stable",
1054
+ "experimental"
1055
+ ],
1056
+ "default": "stable",
1057
+ "description": "RFC 0042 — stability claim for this capability advertisement. `stable` (the default when absent) means the host commits to the wire shape across v1.x minors. `experimental` means the host advertises the surface as a preview; the wire shape MAY shift compatibly without notice until the underlying RFC graduates to `Accepted` and the host re-advertises as `stable`. Hosts MUST omit the field for capabilities whose underlying RFC is already `Accepted`."
1058
+ },
1059
+ "experimentalUntil": {
1060
+ "type": "string",
1061
+ "format": "date",
1062
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
1063
+ "description": "RFC 0042 §B — required when `tier` is `experimental`. ISO-8601 `YYYY-MM-DD` no more than 12 months past the discovery response date. Reaching this date without graduating the underlying RFC to `Accepted` MUST result in the host either flipping tier to `stable` OR retracting the capability advertisement (or — with an open deprecation RFC — extending with a new `experimentalUntil` per §B sub-block 2)."
1064
+ },
1065
+ "version": {
1066
+ "type": "integer",
1067
+ "minimum": 1,
1068
+ "maximum": 6,
1069
+ "description": "Profile version. 1 = Phase 1 (execution-loop framework + planner→worker handoff). 2 = Phase 2 (confidence-floor escalation + agent-memory lifecycle, RFC 0039). 3 = Phase 3 (cross-host causation, RFC 0040). 4 = Phase 4 (replay determinism under nondeterministic models, RFC 0041). 5 = Phase 5 (stateful agent-loop lifecycle — per-iteration workspace+memory snapshot inputs, the observable `iteration` counter on `runOrchestrator.decided`, and stateful HITL resume, RFC 0061). 6 = Phase 6 (verifier/critic turn — the `agent.verified` event + `successCriteria` on the `terminate` decision, RFC 0090). A host advertising `version: N` MUST implement all phases 1..N additively."
1070
+ },
1071
+ "verifier": {
1072
+ "type": "object",
1073
+ "additionalProperties": false,
1074
+ "required": [
1075
+ "supported"
1076
+ ],
1077
+ "description": "RFC 0090 (`version >= 6`). The verifier/critic turn: the host emits `agent.verified` over a prior result and (optionally) gates commit on the verdict. Absent ⇒ no verifier turn; conformance scenarios soft-skip.",
1078
+ "properties": {
1079
+ "supported": {
1080
+ "type": "boolean",
1081
+ "description": "Host emits `agent.verified` and honors RFC 0090 §A. Applies only when `version >= 6`."
1082
+ },
1083
+ "gating": {
1084
+ "type": "boolean",
1085
+ "description": "Host enforces the RFC 0090 §B commit-gating contract: a `fail` verdict blocks merge/terminate (fail-closed, composing RFC 0063); `revise` routes back to an actor turn. Absent/false ⇒ the verdict is observability-only."
1086
+ }
1087
+ }
1088
+ },
1089
+ "confidenceEscalationFloor": {
1090
+ "type": "number",
1091
+ "minimum": 0.5,
1092
+ "maximum": 1.0,
1093
+ "description": "RFC 0039 §A. Operator-declared confidence floor at or above the spec floor of 0.5; when an OrchestratorDecision carries `confidence` below this floor, the host MUST escalate via a `clarify` or `escalate` interrupt instead of executing the decision. Absent: the spec floor 0.5 applies. Values < 0.5 are non-conformant; values > 1.0 are nonsense. Applies only when `version >= 2`."
1094
+ },
1095
+ "confidenceEscalationInterruptKind": {
1096
+ "type": "string",
1097
+ "anyOf": [
1098
+ {
1099
+ "const": "clarification"
1100
+ },
1101
+ {
1102
+ "const": "approval"
1103
+ },
1104
+ {
1105
+ "pattern": "^x-host-[a-z][a-z0-9-]*-[a-z][a-z0-9-]*$"
1106
+ }
1107
+ ],
1108
+ "description": "RFC 0044 — the literal `interrupt.kind` the host emits when escalating a below-floor confidence decision per RFC 0039 §A. `clarification` and `approval` are the canonical values matching the clarify-OR-approval choice in RFC 0039 §A; vendor-extension kinds use the canonical host-extension namespace `^x-host-<host>-<kind>$` per `spec/v1/host-extensions.md` §\"Canonical prefixes\". When advertised, hosts MUST emit an interrupt of the advertised kind on every confidence-escalation event AND the host's downstream `interrupt.md` mapping determines the `waiting-*` terminal status. Absent: conformance assumes the host uses one of the two canonical kinds (the relaxed assertion accepts either). Hosts using vendor kinds MUST also publish a non-normative kind-mapping document per RFC 0044 §C."
1109
+ },
1110
+ "crossChildMemoryConcurrency": {
1111
+ "type": "string",
1112
+ "enum": [
1113
+ "strict",
1114
+ "advisory"
1115
+ ],
1116
+ "description": "RFC 0039 §B. Cross-child memory-write concurrency posture when version >= 2. `strict` (default when absent): the host serializes concurrent writes from sibling dispatched children to the parent's shared memory scope. `advisory`: the host opts out of the serialization MUST and documents last-write-wins semantics out-of-band. Hosts that choose advisory SHOULD also advertise their resolution rule under `crossChildMemoryConcurrencyResolution` (reserved field; follow-up clarification)."
1117
+ },
1118
+ "crossHostCausation": {
1119
+ "type": "object",
1120
+ "description": "RFC 0040 §D — Phase 3 cross-host causation linking. When advertised, the host honors the cross-host `causationId` extension (a `causationHostId` field on event payloads pointing at the originating host), W3C `traceparent` propagation across MCP + A2A composition boundaries, and (when `ancestryEndpointSupported: true`) the `GET /v1/runs/{runId}/ancestry` cross-host parent-chain endpoint. Hosts advertising `version: 3` MUST advertise this sub-block with `supported: true` and a stable `hostId`.",
1121
+ "additionalProperties": false,
1122
+ "required": [
1123
+ "supported"
1124
+ ],
1125
+ "properties": {
1126
+ "supported": {
1127
+ "type": "boolean",
1128
+ "description": "Host implements RFC 0040 Phase 3 cross-host causation contracts."
1129
+ },
1130
+ "hostId": {
1131
+ "type": "string",
1132
+ "minLength": 1,
1133
+ "description": "Stable identifier for this host instance, used as the `causationHostId` value on cross-host events. SHOULD be a URL or DNS-style identifier (e.g., `myndhyve.ai/workflow-runtime`); the protocol does not normate a stricter format."
1134
+ },
1135
+ "ancestryEndpointSupported": {
1136
+ "type": "boolean",
1137
+ "description": "Host serves `GET /v1/runs/{runId}/ancestry` returning the cross-host parent chain per RFC 0040 §C. Optional even when crossHostCausation.supported is true — hosts that emit `causationHostId` but don't expose the ancestry-walking endpoint still satisfy the per-event chaining contract; this flag advertises the additional endpoint."
1138
+ }
1139
+ }
1140
+ },
1141
+ "replayDeterminism": {
1142
+ "type": "object",
1143
+ "description": "RFC 0041 §D — Phase 4 replay determinism. When advertised, the host honors the LLM cache-key recipe in `replay.md` §\"LLM cache-key recipe\" as NORMATIVE (graduated from informative for version >= 4), emits `replay.divergedAtRefusal` events + fails with `replay_diverged_at_refusal` on refusal-divergence (RFC 0041 §B), and guarantees observable-output-sequence determinism per RFC 0041 §C. Hosts advertising `version: 4` MUST advertise this sub-block with `supported: true`.",
1144
+ "additionalProperties": false,
1145
+ "required": [
1146
+ "supported"
1147
+ ],
1148
+ "properties": {
1149
+ "supported": {
1150
+ "type": "boolean",
1151
+ "description": "Host implements RFC 0041 Phase 4 replay-determinism contracts."
1152
+ },
1153
+ "llmCacheKeyRecipe": {
1154
+ "type": "string",
1155
+ "anyOf": [
1156
+ {
1157
+ "const": "spec-rfc-0041"
1158
+ },
1159
+ {
1160
+ "pattern": "^x-host-[a-z][a-z0-9-]*-[a-z][a-z0-9-]*$"
1161
+ }
1162
+ ],
1163
+ "description": "The LLM cache-key recipe the host honors. `spec-rfc-0041` = the canonical recipe in `replay.md` §\"LLM cache-key recipe\" §A + §B + §C. Vendor-specific recipes use the canonical host-extension namespace string matching `^x-host-<host>-<recipe-name>$` per `spec/v1/host-extensions.md` §\"Canonical prefixes\"; the matching algorithm MUST be documented at the host's discovery doc."
1164
+ },
1165
+ "refusalDivergenceEmission": {
1166
+ "type": "boolean",
1167
+ "description": "Host emits `replay.divergedAtRefusal` events + fails replay with `error.code: replay_diverged_at_refusal` per RFC 0041 §B. Hosts advertising `version: 4` MUST set this to `true`."
1168
+ }
1169
+ }
1170
+ },
1171
+ "statefulResume": {
1172
+ "type": "boolean",
1173
+ "description": "RFC 0061 (`version >= 5`). When `true`, a `clarify`/`escalate` HITL suspend resumes the execution loop at the SAME iteration — the `runOrchestrator.decided.iteration` counter does not reset or skip — with the per-iteration memory (RFC 0039 MAE-3) + workspace (RFC 0059) snapshot lineage intact, so a mid-loop human interrupt does not lose progress. A distinct claim from plain replay re-entrancy (deterministic replay of a completed prefix); this is about a LIVE suspend preserving the counter. Omitted by hosts on `version < 5`."
1174
+ },
1175
+ "transcriptWindow": {
1176
+ "type": "integer",
1177
+ "minimum": 1,
1178
+ "description": "RFC 0061 (`version >= 5`). Host-advertised count of recent event-log entries the host feeds each orchestrator turn as the iteration's transcript input (§C input 3). Advertise-and-honor; not a fixed wire constant. Absent ⇒ the host does not bound the transcript window on the wire."
1179
+ },
1180
+ "contextBudget": {
1181
+ "type": "object",
1182
+ "description": "RFC 0111 (`Active`, `version >= 5`). Opt-in, token-denominated bound on the orchestrator transcript the host feeds each iteration, plus a declared summarization contract for turns evicted beyond that budget. SCOPE: governs the RFC 0061 per-iteration ORCHESTRATOR-LOOP transcript (`multi-agent-execution.md` §\"Per-iteration state inputs\" input 3 — the same transcript `transcriptWindow` bounds), NOT a general chat-conversation history. A host whose orchestrator loop does not run real model turns (e.g. a mock supervisor) MUST NOT advertise this block, exactly as it MUST NOT dishonestly advertise `transcriptWindow`. Budget-only advertisement (`transcriptTokenBudget` + `tokenCounter` WITHOUT `summarization.supported`) is valid for a host that HAS a real orchestrator loop but does not summarize. Purely additive: a host that omits this block behaves exactly as today, including `transcriptWindow`'s `absent ⇒ unbounded` default (NOT flipped by this RFC). Complements (does not replace) the event-count `transcriptWindow`; when both bound a turn the host MUST honor whichever is tighter. Summarization here is a NONDETERMINISTIC host output governed exactly like an RFC 0041 envelope: each substitution MUST be recorded as a `context.summarized` event whose `summaryRef` artifact replay reuses (never re-summarizes) per `multi-agent-execution.md` §\"Context economy (RFC 0111)\".",
1183
+ "additionalProperties": false,
1184
+ "properties": {
1185
+ "transcriptTokenBudget": {
1186
+ "type": "integer",
1187
+ "minimum": 1,
1188
+ "description": "RFC 0111. Max tokens of transcript the host feeds any single orchestrator turn, measured in the unit named by `tokenCounter`. Advertise-and-honor; complements (does not replace) `transcriptWindow`. When both are present the host MUST honor whichever bound is tighter for a given turn. Absent ⇒ no token bound on the transcript (only the event-count `transcriptWindow`, if any, applies)."
1189
+ },
1190
+ "tokenCounter": {
1191
+ "type": "string",
1192
+ "enum": [
1193
+ "o200k_base",
1194
+ "cl100k_base",
1195
+ "chars",
1196
+ "host-defined"
1197
+ ],
1198
+ "description": "RFC 0111. The unit `transcriptTokenBudget` is denominated in, so the bound is interpretable across hosts. REQUIRED when `transcriptTokenBudget` is present (enforced via the `if/then` clause). `o200k_base`/`cl100k_base` are tokenizer encodings; `chars` counts UTF-8/Unicode characters (a tokenizer-free unit a client can reason about directly); `host-defined` is an opaque host unit. Same enum as RFC 0113 `memory.injectionBudget.tokenCounter` — transcript (0111) and memory (0113) budgets denominate in one consistent vocabulary; no shared `$ref` (decoupled)."
1199
+ },
1200
+ "summarization": {
1201
+ "type": "object",
1202
+ "additionalProperties": false,
1203
+ "required": [
1204
+ "supported"
1205
+ ],
1206
+ "description": "RFC 0111. Declared contract for turns evicted beyond `transcriptTokenBudget`. When `supported: true`, the host MAY replace older in-window turns with a host-produced summary; it MUST keep the most recent `keepLastTurns` turns verbatim and MUST NOT summarize the active (most recent) turn. Each substitution MUST be recorded as a `context.summarized` event and is replay-governed under RFC 0041 (replay reuses the recorded `summaryRef`, never re-summarizes).",
1207
+ "properties": {
1208
+ "supported": {
1209
+ "type": "boolean",
1210
+ "description": "REQUIRED when the sub-block is present. When `true`, the host implements the RFC 0111 declared-summarization contract; conformance scenario `context-summarization-replay` gates on this flag and soft-skips when absent/false."
1211
+ },
1212
+ "strategy": {
1213
+ "type": "string",
1214
+ "enum": [
1215
+ "sliding-window",
1216
+ "recursive",
1217
+ "map-reduce"
1218
+ ],
1219
+ "description": "RFC 0111. Informational descriptor of the host's summarization strategy. `sliding-window` keeps a recent verbatim tail and summarizes the prefix; `recursive` folds prior summaries into new ones; `map-reduce` summarizes chunks then combines. Does not change the replay-determinism contract — all strategies record `context.summarized` and reuse `summaryRef` on replay."
1220
+ },
1221
+ "keepLastTurns": {
1222
+ "type": "integer",
1223
+ "minimum": 0,
1224
+ "description": "RFC 0111. Number of most-recent turns kept verbatim at the head of the window; older in-window turns MAY be replaced by a summary. The active (most recent) turn MUST NOT be summarized regardless of this value. Absent ⇒ host-defined verbatim floor."
1225
+ }
1226
+ }
1227
+ }
1228
+ },
1229
+ "if": {
1230
+ "required": [
1231
+ "transcriptTokenBudget"
1232
+ ]
1233
+ },
1234
+ "then": {
1235
+ "required": [
1236
+ "transcriptTokenBudget",
1237
+ "tokenCounter"
1238
+ ]
1239
+ }
1240
+ }
1241
+ }
1242
+ }
1243
+ }
1244
+ },
1245
+ "modelCapabilities": {
1246
+ "type": "object",
1247
+ "description": "RFC 0031. Host implements model-capability gating + (optional) substitution per `NodeModule.requiredModelCapabilities` + `NodeModule.fallbackModel`. Distinct from `runtimeCapabilities` which gates on HOST capabilities — `modelCapabilities` gates on MODEL capabilities (structured-output support, discriminator-enum support, long-context, native reasoning, function-calling). Absent block = no advertisement; NodeModules' `requiredModelCapabilities` are treated as opaque metadata and dispatch proceeds without checks.",
1248
+ "additionalProperties": false,
1249
+ "required": [
1250
+ "supported"
1251
+ ],
1252
+ "properties": {
1253
+ "supported": {
1254
+ "type": "boolean",
1255
+ "description": "Host honors `NodeModule.requiredModelCapabilities` and emits `model.capability.substituted` / `model.capability.insufficient` events per RFC 0031 §B + §D. When `false` or absent, the fields are treated as opaque metadata and dispatch proceeds without capability checks."
1256
+ },
1257
+ "advertised": {
1258
+ "type": "array",
1259
+ "items": {
1260
+ "type": "string",
1261
+ "pattern": "^([a-z][a-z0-9-]*|x-host-[a-z][a-z0-9-]*-[a-z][a-z0-9-]*)$"
1262
+ },
1263
+ "uniqueItems": true,
1264
+ "description": "Capability identifiers the host's active model advertises. Clients MAY introspect this at install time to determine whether their NodeModules' `requiredModelCapabilities` are satisfiable without fallback. Spec-reserved identifiers per RFC 0031 §C + RFC 0055: `structured-output`, `discriminator-enum`, `long-context`, `reasoning` (model-native thinking-tokens), `function-calling`, `vision-input` (model accepts image content in the prompt), `audio-input` (model accepts audio content), `audio-output` (model emits audio content), `image-output` (model emits images directly in its completion, distinct from the host-side `aiProviders.imageGeneration` tool surface). This is an open, pattern-validated registry — NOT a closed enum; growth requires an RFC. Host-private extensions MUST prefix with `x-host-<host>-`."
1265
+ },
1266
+ "substitutionSupported": {
1267
+ "type": "boolean",
1268
+ "description": "Host honors `NodeModule.fallbackModel` substitution per RFC 0031 §B step 3. When `false` or absent, hosts MUST refuse to dispatch (step 4) on any unmet capability — they MUST NOT attempt fallback even when the field is declared on the NodeModule. Recursive fallback is NOT permitted (RFC 0031 §\"Unresolved questions\" #3)."
1269
+ }
1270
+ }
1271
+ },
1272
+ "providerUsage": {
1273
+ "type": "object",
1274
+ "description": "RFC 0026. Hosts that emit `provider.usage` events after every LLM provider invocation per RFC 0026 §B. The event carries per-call token counts in the durable event log; cost rollup remains in `RunSnapshot.metrics.openwopCost`. Old hosts ignore.",
1275
+ "properties": {
1276
+ "supported": {
1277
+ "type": "boolean",
1278
+ "description": "Host emits one `provider.usage` event per LLM provider call."
1279
+ },
1280
+ "costEstimates": {
1281
+ "type": "boolean",
1282
+ "description": "When true, the host includes `costEstimateUsd` on `provider.usage` events using its internal rate table. When false/absent, only token counts are emitted."
1283
+ },
1284
+ "currency": {
1285
+ "type": "string",
1286
+ "pattern": "^[A-Z]{3}$",
1287
+ "description": "Default ISO 4217 currency for `costEstimateUsd` values. When absent, USD is assumed."
1288
+ }
1289
+ },
1290
+ "required": [
1291
+ "supported"
1292
+ ],
1293
+ "additionalProperties": false
1294
+ },
1295
+ "aiProviders": {
1296
+ "type": "object",
1297
+ "description": "Optional v1 companion to `secrets`. Advertises which AI providers the host's AI-proxy can route to and which permit BYOK.",
1298
+ "properties": {
1299
+ "supported": {
1300
+ "type": "array",
1301
+ "items": {
1302
+ "type": "string",
1303
+ "minLength": 1
1304
+ },
1305
+ "uniqueItems": true,
1306
+ "description": "Provider ids the host's AI-proxy can route to. Conventional ids (RFC 0067 §C recommended vocabulary — advisory, not a closed set): `anthropic`, `openai`, `gemini`, `vertex`, `bedrock`, `mistral`, `cohere`, `openrouter`, `litellm`, `together`, `huggingface`, `qwen`, `ollama`, `vllm`. Hosts MAY add vendor-prefixed extensions; clients MUST tolerate unknown ids."
1307
+ },
1308
+ "byok": {
1309
+ "type": "array",
1310
+ "items": {
1311
+ "type": "string",
1312
+ "minLength": 1
1313
+ },
1314
+ "uniqueItems": true,
1315
+ "description": "Subset of `supported` for which BYOK is permitted. Empty array → all calls use platform-managed keys; non-empty → clients MAY pass `ai.credentialRef` in `RunOptions.configurable` for matching providers."
1316
+ },
1317
+ "selfHosted": {
1318
+ "type": "array",
1319
+ "items": {
1320
+ "type": "string",
1321
+ "minLength": 1
1322
+ },
1323
+ "uniqueItems": true,
1324
+ "description": "RFC 0108. Subset of `supported` whose entries are operator- or tenant-configured OpenAI-compatible endpoints (e.g. an Ollama / vLLM / LM Studio / any `/v1/chat/completions`-compatible server), as opposed to a host-managed connection to a known public vendor. Each entry MUST also appear in `supported`. An entry MAY also appear in `byok` (the endpoint requires a client/tenant-supplied key) or be absent from it (the endpoint needs no key, e.g. a default Ollama). The provider id is an OPAQUE label chosen by the host; it MUST NOT encode the endpoint's network location (scheme, host, port, path, or base-URL) per RFC 0108 §A.3, and the endpoint location MUST NOT be disclosed on any wire surface (see the `self-hosted-endpoint-no-disclosure` SECURITY invariant). A client MUST NOT infer model capabilities from a `selfHosted` id (RFC 0108 §B); the only authoritative capability sources are `modelCapabilities.advertised[]` (RFC 0031) and `aiProviders.input.modalities` (RFC 0091)."
1325
+ },
1326
+ "speechSynthesis": {
1327
+ "const": "supported",
1328
+ "description": "RFC 0105. When present (value MUST be the string `\"supported\"`), the host exposes `ctx.callSpeechSynthesizer({ text, voiceId, mimeType?, format?, languageCode?, ... })` returning a binary audio asset (host-served `url` OR inline `base64` + `mimeType`), paralleling `aiProviders.imageGeneration`. Absent ⇒ no TTS; a call MUST be rejected with `speech_synthesis_unsupported` (never a no-op). Voices are referenced by an opaque host-resolved `voiceId`; the spec does NOT enumerate voices. TTS provider ids are advertised via the existing `aiProviders.supported[]` (the call's `provider?` MUST be a member); there is no `speechSynthesis` sub-field for providers. Per-host model/voice routing is a host choice."
1329
+ },
1330
+ "input": {
1331
+ "type": "object",
1332
+ "additionalProperties": false,
1333
+ "description": "RFC 0091. Multimodal PERCEPTION input on `ctx.callAI` — the modalities a `callAI` message ContentPart may carry as model INPUT. Absent ⇒ text-only (today's behavior); a `string` message content is always valid. Distinct from `imageGeneration` (output) and the `ai-envelope.md` media emission types (output).",
1334
+ "properties": {
1335
+ "modalities": {
1336
+ "type": "array",
1337
+ "uniqueItems": true,
1338
+ "items": {
1339
+ "type": "string",
1340
+ "enum": [
1341
+ "text",
1342
+ "image",
1343
+ "audio",
1344
+ "document"
1345
+ ]
1346
+ },
1347
+ "description": "Input modalities the host's `callAI` accepts as ContentParts. `text` is implicit even when omitted. A ContentPart whose `type` is not advertised here MUST be rejected with `unsupported_modality` (never silently dropped)."
1348
+ },
1349
+ "maxBytesPerPart": {
1350
+ "type": "integer",
1351
+ "minimum": 1,
1352
+ "description": "Optional host cap on a single inline (`data`) or `mediaRef` part."
1353
+ }
1354
+ }
1355
+ },
1356
+ "authModes": {
1357
+ "type": "object",
1358
+ "description": "RFC 0067 (`Active`), extended by RFC 0121 (`Active`). Optional per-provider advertisement of HOW the host expects a provider's credential to be supplied. Keys are provider ids appearing in `supported`; values are the auth modes the host honors for that provider. Absent ⇒ no advertisement: a provider in `byok` defaults to `apiKey` semantics (client passes `ai.credentialRef`); a provider in `supported` but not in `byok` defaults to `none` (platform-managed). This map only DESCRIBES the supply mechanism — `oauth-pkce`/`oauth-device` flow mechanics compose RFC 0047 `host.oauth` and resolve credentials by `ref` (RFC 0046), never on `ai.credentialRef`. A provider with `apiKey` MUST appear in `byok`; a provider whose modes are exactly `[\"none\"]` MUST NOT appear in `byok`. `subscription` (RFC 0121) advertises that a provider credential may be supplied by reusing the caller's existing personal, non-metered consumer subscription (e.g. Claude Pro/Max, ChatGPT Plus) rather than a metered API key; a `subscription` provider MUST appear in `byok`, is referenced by `ref` (RFC 0046) like the `oauth-*` modes (never on `ai.credentialRef`), and its credential MUST bind at `host.credentials` `scope:\"user\"` — never tenant/workspace-shared (SECURITY invariant `subscription-credential-user-scope-only`). Consumers MUST ignore an auth mode they don't recognize rather than reject the discovery doc.",
1359
+ "additionalProperties": {
1360
+ "type": "array",
1361
+ "minItems": 1,
1362
+ "uniqueItems": true,
1363
+ "items": {
1364
+ "type": "string",
1365
+ "enum": [
1366
+ "apiKey",
1367
+ "oauth-pkce",
1368
+ "oauth-device",
1369
+ "none",
1370
+ "subscription"
1371
+ ]
1372
+ }
1373
+ }
1374
+ },
1375
+ "policies": {
1376
+ "type": "object",
1377
+ "description": "Optional v1 host-side policy enforcement modes for per-provider gating. Omitted → no enforcement; clients see only `optional` semantics. When present, MUST declare `modes` — an empty `{}` is not a valid third state. See `capabilities.md` §`aiProviders.policies`.",
1378
+ "required": [
1379
+ "modes"
1380
+ ],
1381
+ "properties": {
1382
+ "modes": {
1383
+ "type": "array",
1384
+ "items": {
1385
+ "type": "string",
1386
+ "enum": [
1387
+ "disabled",
1388
+ "optional",
1389
+ "required",
1390
+ "restricted"
1391
+ ]
1392
+ },
1393
+ "uniqueItems": true,
1394
+ "description": "Subset of policy modes this host can enforce. `disabled` = provider may not be used; `optional` = no restriction (default); `required` = BYOK required; `restricted` = model must match the policy's `allowedModels` glob list. Hosts MAY support a subset; clients MUST tolerate any subset."
1395
+ },
1396
+ "scopes": {
1397
+ "type": "array",
1398
+ "items": {
1399
+ "type": "string",
1400
+ "minLength": 1
1401
+ },
1402
+ "uniqueItems": true,
1403
+ "description": "Resolution layers the host evaluates. Conventional ids: `workspace`, `project`, `canvas-type`. Precedence is host-defined and SHOULD be documented per-deployment."
1404
+ },
1405
+ "errorCode": {
1406
+ "type": "string",
1407
+ "minLength": 1,
1408
+ "description": "Wire-format error code returned on denial. Defaults to `provider_policy_denied`. Reserved for hosts that need a vendor-prefixed alias."
1409
+ }
1410
+ },
1411
+ "additionalProperties": false
1412
+ },
1413
+ "maxInlineMediaBytes": {
1414
+ "type": "integer",
1415
+ "minimum": 0,
1416
+ "default": 262144,
1417
+ "description": "RFC 0055 §C rule 2 — optional cap (bytes) on inline base64 in `media.*` envelope payloads. A `media.{image,audio,file}` asset above this size MUST be served by a tenant-scoped `url` reference rather than inlined (bounds event-log + replay-payload size). Default 256 KiB (262144) when absent. A host MAY set 0 to force URL references for all emitted media."
1418
+ },
1419
+ "realtimeVoice": {
1420
+ "type": "object",
1421
+ "additionalProperties": false,
1422
+ "description": "RFC 0106. Optional real-time voice profile. Absent ⇒ no live voice (a call to `ctx.callTranscriber` MUST be rejected with `transcription_unsupported`, and `stream:true` on `ctx.callSpeechSynthesizer` with `streaming_unsupported`). `ctx.callTranscriber` resolves a `Promise` at `turn_commit` with the settled final transcript and emits the interim/final/endpoint signals as `voice.*` run-events on the durable event log (the single canonical record); the streaming synthesis arm resolves a `Promise` at completion and announces clause-boundary chunks as `voice.synthesis_chunk` metadata run-events (bytes by `streamRef`/`url`, not inlined past the host cap).",
1423
+ "properties": {
1424
+ "transcription": {
1425
+ "const": "streaming",
1426
+ "description": "When present (value MUST be `\"streaming\"`), the host exposes `ctx.callTranscriber(...)` (streaming STT). `turnDetection` and `bargeIn` require `transcription`."
1427
+ },
1428
+ "synthesis": {
1429
+ "const": "streaming",
1430
+ "description": "When present (value MUST be `\"streaming\"`), the RFC 0105 `ctx.callSpeechSynthesizer` request honors `stream: true` (chunked synthesis). REQUIRES `aiProviders.speechSynthesis: \"supported\"` (enforced by the if/then closure on `aiProviders`)."
1431
+ },
1432
+ "turnDetection": {
1433
+ "enum": [
1434
+ "vad",
1435
+ "semantic"
1436
+ ],
1437
+ "description": "Endpointing sophistication. `vad` = silence-threshold endpointing only; `semantic` = a turn detector that emits `voice.endpoint_candidate` distinct from `voice.turn_commit`. Requires `transcription`."
1438
+ },
1439
+ "bargeIn": {
1440
+ "const": "supported",
1441
+ "description": "When present, the host MUST emit `voice.barge_in` on overlapping speech during playback and `voice.cancelled` when it actually cancels downstream work. Requires `transcription`."
1442
+ }
1443
+ },
1444
+ "dependentRequired": {
1445
+ "turnDetection": [
1446
+ "transcription"
1447
+ ],
1448
+ "bargeIn": [
1449
+ "transcription"
1450
+ ]
1451
+ }
1452
+ },
1453
+ "promptPrefixCache": {
1454
+ "type": "object",
1455
+ "additionalProperties": false,
1456
+ "description": "RFC 0116. Host honors the AI-envelope `generate` request's optional `cachePrefixId` as a provider-cache routing hint — tenant-namespaced (SECURITY invariant `prompt-prefix-cache-cross-tenant-isolation`), secret-free, and replay-invariant (a cache hit/miss MUST NOT change the recorded envelope or `provider.usage.inputTokens`/`outputTokens`). Absent ⇒ a host MUST ignore `cachePrefixId` (no error, no behavior change). PROVIDER-SCOPED: prefix caching is provider-specific (e.g. Anthropic ephemeral), so this is NOT a universal claim — see `providers`.",
1457
+ "properties": {
1458
+ "supported": {
1459
+ "type": "boolean",
1460
+ "description": "Whether the host honors `cachePrefixId` as a provider-cache routing hint."
1461
+ },
1462
+ "providers": {
1463
+ "type": "array",
1464
+ "items": {
1465
+ "type": "string",
1466
+ "minLength": 1
1467
+ },
1468
+ "uniqueItems": true,
1469
+ "description": "RFC 0116. The subset of `aiProviders.supported[]` for which the host honors `cachePrefixId` (prefix caching is provider-specific). A request whose routed provider is NOT in this list MUST have `cachePrefixId` ignored. Absent ⇒ host-defined per-provider routing; NOT a universal claim across providers."
1470
+ }
1471
+ },
1472
+ "required": [
1473
+ "supported"
1474
+ ]
1475
+ }
1476
+ },
1477
+ "additionalProperties": false,
1478
+ "allOf": [
1479
+ {
1480
+ "$comment": "RFC 0106 §A closure: realtimeVoice.synthesis (streaming TTS) requires the whole-file TTS surface aiProviders.speechSynthesis to be advertised.",
1481
+ "if": {
1482
+ "type": "object",
1483
+ "required": [
1484
+ "realtimeVoice"
1485
+ ],
1486
+ "properties": {
1487
+ "realtimeVoice": {
1488
+ "type": "object",
1489
+ "required": [
1490
+ "synthesis"
1491
+ ]
1492
+ }
1493
+ }
1494
+ },
1495
+ "then": {
1496
+ "required": [
1497
+ "speechSynthesis"
1498
+ ]
1499
+ }
1500
+ }
1501
+ ]
1502
+ },
1503
+ "testing": {
1504
+ "type": "object",
1505
+ "description": "Testing-mode capabilities advertised to clients (closes F1). Lets conformance suites + dev clients discover which mock providers the server supports and which API-key prefix denotes test keys (since mock providers are test-keys-only).",
1506
+ "properties": {
1507
+ "mockProviders": {
1508
+ "type": "array",
1509
+ "items": {
1510
+ "type": "string",
1511
+ "minLength": 1
1512
+ },
1513
+ "uniqueItems": true,
1514
+ "description": "Mock-provider IDs this server recognizes via `RunOptions.configurable.mockProvider.id`. Servers claiming OpenWOP v1.0 conformance MUST include `stream-text`. Other canonical providers (`tool-calls`, `error`, `usage-only`) are recommended. Implementations MAY add their own (vendor-prefixed)."
1515
+ },
1516
+ "testKeyPrefix": {
1517
+ "type": "string",
1518
+ "description": "API-key prefix that marks a key as test-mode. Mock providers and other test-only surfaces gate on this prefix.",
1519
+ "minLength": 1,
1520
+ "maxLength": 32
1521
+ },
1522
+ "forceEngineVersionRange": {
1523
+ "type": "object",
1524
+ "description": "Range of engine versions the server can be forced into via the `X-Force-Engine-Version` request header (test-keys-only). Used by the conformance suite to verify forward-compat fold-best-effort across the version-negotiation matrix. Closes F5.",
1525
+ "required": [
1526
+ "min",
1527
+ "max"
1528
+ ],
1529
+ "properties": {
1530
+ "min": {
1531
+ "type": "integer",
1532
+ "minimum": 0,
1533
+ "description": "Lowest forceable engine version. Typically (current - 1) so back-compat is exercised."
1534
+ },
1535
+ "max": {
1536
+ "type": "integer",
1537
+ "minimum": 0,
1538
+ "description": "Highest forceable engine version. Typically (current + 1) so forward-compat is exercised."
1539
+ }
1540
+ },
1541
+ "additionalProperties": false
1542
+ }
1543
+ },
1544
+ "additionalProperties": true
1545
+ },
1546
+ "conformance": {
1547
+ "type": "object",
1548
+ "description": "Conformance-only capability block (RFC 0023). Advertises which conformance-only typeIds (`core.conformance.*`) the host has registered. Hosts that don't ship conformance-only typeIds omit this block entirely. Production deployments SHOULD omit this block; conformance-only typeIds carry test hooks (e.g., `mockReasoning`, `mockConfidence`) that MUST NOT be reachable from production tenants.",
1549
+ "properties": {
1550
+ "mockAgent": {
1551
+ "type": "boolean",
1552
+ "description": "RFC 0023 §B.2. When `true`, the host has registered the `core.conformance.mock-agent` typeId. The scenarios `agentReasoningEvents.test.ts` and `agentConfidenceEscalation.test.ts` rely on the typeId being reachable. Hosts that register the typeId only for workflow ids matching the conformance fixture prefix (`conformance-*`) and refuse it for other tenants MAY still advertise `true` — the advertisement says only that the typeId is reachable from the conformance suite, not that it is reachable from arbitrary workflows."
1553
+ },
1554
+ "certificationBundleUrl": {
1555
+ "type": "string",
1556
+ "format": "uri",
1557
+ "description": "OPTIONAL (RFC 0089). URL of the host's most recent conformance certification bundle (`conformance-certification-bundle.schema.json`) — a machine-readable attestation binding this host's claimed profiles to the reproducible run that substantiates them. Omitting it is fully conformant; clients MUST tolerate its absence."
1558
+ }
1559
+ },
1560
+ "additionalProperties": false
1561
+ },
1562
+ "fixtures": {
1563
+ "type": "array",
1564
+ "items": {
1565
+ "type": "string",
1566
+ "minLength": 1
1567
+ },
1568
+ "uniqueItems": true,
1569
+ "description": "Optional v1 fixture workflow IDs the host has seeded. The conformance suite uses this list to decide which fixture-dependent scenarios run vs. skip. Each ID matches a stub at `node_modules/@openwop/openwop-conformance/fixtures/{id}.json`. Hosts MAY advertise vendor-prefixed IDs; clients MUST tolerate unknown IDs. Empty array or absent means the host advertises no fixtures."
1570
+ },
1571
+ "agents": {
1572
+ "type": "object",
1573
+ "description": "Multi-Agent Shift capability block (Phases 1-6). Hosts that implement any multi-agent surface declare it here; pre-MAS hosts omit the block entirely (consumers treat as 'no agent support'). Each field gates a specific conformance scenario class; conformance scenarios skip honestly when the host's advertisement doesn't claim the capability.",
1574
+ "properties": {
1575
+ "supported": {
1576
+ "type": "boolean",
1577
+ "description": "Master switch for agent identity (Phase 1). When `true`, host accepts run-level `RunSnapshot.agent` / `runOrchestrator` fields, emits `agent.*` events, and honors the confidence-escalation contract."
1578
+ },
1579
+ "profile": {
1580
+ "type": "string",
1581
+ "description": "Optional named capability profile (e.g., `openwop-agents-phase-1-skeleton` for identity-only, `openwop-agents-full` for orchestrator+dispatch+memory+conversation). Clients pattern-match on profile to gate feature usage."
1582
+ },
1583
+ "modelClasses": {
1584
+ "type": "array",
1585
+ "items": {
1586
+ "type": "string",
1587
+ "enum": [
1588
+ "reasoning",
1589
+ "tool-using",
1590
+ "chat",
1591
+ "code",
1592
+ "vision",
1593
+ "multimodal",
1594
+ "embedding",
1595
+ "classification",
1596
+ "retrieval"
1597
+ ]
1598
+ },
1599
+ "uniqueItems": true,
1600
+ "description": "Optional list of `AgentRef.modelClass` values this host supports for hosted agents (Phase 2). Pack manifests whose `modelClass` is not in this list MUST refuse install with `unsupported_model_class`."
1601
+ },
1602
+ "orchestratorPattern": {
1603
+ "type": "string",
1604
+ "description": "Optional orchestration pattern (Phase 2). Canonical: `single`, `delegate`, `delegate.smart`. Vendor extensions ship under `vendor.<host>.<pattern>`."
1605
+ },
1606
+ "memoryBackends": {
1607
+ "type": "array",
1608
+ "items": {
1609
+ "type": "string",
1610
+ "enum": [
1611
+ "long-term"
1612
+ ]
1613
+ },
1614
+ "uniqueItems": true,
1615
+ "description": "Optional list of memory backends (Phase 3). `long-term` means the host implements `ExecutionHost.memory` against a durable store with the SR-1 redaction invariant intact end-to-end. Hosts that don't wire `MemoryAdapter` omit this field."
1616
+ },
1617
+ "memoryConsolidation": {
1618
+ "type": "object",
1619
+ "description": "RFC 0068 (`Draft`). Background reconciliation of LONG-TERM memory (merge/dedup/supersede/strengthen) — distinct from RFC 0062 token-budgeted distillation of TRANSACTIONAL memory. A host advertising this emits `agent.memory.consolidated` (content-free) after a consolidation pass. Requires `agents.memoryBackends` to include `long-term`. SR-1 carry-forward + CTI-1 (RFC 0004) hold across the pass. Hosts that omit this block do not consolidate; the conformance scenarios skip cleanly.",
1620
+ "additionalProperties": false,
1621
+ "required": [
1622
+ "supported"
1623
+ ],
1624
+ "properties": {
1625
+ "supported": {
1626
+ "type": "boolean",
1627
+ "description": "REQUIRED when the block is present. When `true`, the host performs background consolidation over long-term memory and emits `agent.memory.consolidated`."
1628
+ },
1629
+ "schedule": {
1630
+ "type": "string",
1631
+ "enum": [
1632
+ "host-managed",
1633
+ "scheduled",
1634
+ "on-demand"
1635
+ ],
1636
+ "description": "How a consolidation pass is initiated. `host-managed`: a host-internal cadence clients do not control (default when absent and supported:true). `scheduled`: bound to a `capabilities.scheduling` (RFC 0052) trigger. `on-demand`: the host runs a pass when explicitly requested. A host MAY honor more than one path but advertises the primary."
1637
+ }
1638
+ }
1639
+ },
1640
+ "commitments": {
1641
+ "type": "object",
1642
+ "description": "RFC 0068 (`Draft`). Inferred STANDING commitments — durable, memory-derived intentions the host promotes into a time- or predicate-gated arm that fires a run later, without a fresh user turn. When an arm fires the host emits `commitment.fired` (content-free — the intention text lives in SR-1-redacted memory). Composes RFC 0052 (time arms) / RFC 0060 (predicate arms) for the fire substrate. Hosts that omit this block do not infer commitments; the conformance scenario skips cleanly.",
1643
+ "additionalProperties": false,
1644
+ "required": [
1645
+ "supported"
1646
+ ],
1647
+ "properties": {
1648
+ "supported": {
1649
+ "type": "boolean",
1650
+ "description": "REQUIRED when the block is present. When `true`, the host infers standing commitments from memory and emits `commitment.fired` when one fires."
1651
+ },
1652
+ "fireConditions": {
1653
+ "type": "array",
1654
+ "items": {
1655
+ "type": "string",
1656
+ "enum": [
1657
+ "time",
1658
+ "predicate"
1659
+ ]
1660
+ },
1661
+ "uniqueItems": true,
1662
+ "description": "Which fire-condition kinds the host supports. `time` composes RFC 0052 scheduling; `predicate` composes RFC 0060 heartbeat. Absent ⇒ `['time']`."
1663
+ }
1664
+ }
1665
+ },
1666
+ "orchestrator": {
1667
+ "type": "boolean",
1668
+ "description": "Phase 5. When `true`, host advertises that it implements the `core.orchestrator.supervisor` node typeId AND honors the conservative-path suspend semantics (CP-1: low-confidence suspend via `node.suspended { reason: 'low-confidence' }`)."
1669
+ },
1670
+ "dispatch": {
1671
+ "type": "boolean",
1672
+ "description": "Phase 6. When `true`, host advertises that it implements the `core.dispatch` Core typeId AND honors the conservative-path commitment CP-2 (`core.dispatch` MUST NOT mutate the run's DAG mid-run). Implies (but does NOT require) `agents.orchestrator: true`."
1673
+ },
1674
+ "manifestRuntime": {
1675
+ "type": "object",
1676
+ "description": "RFC 0070. Agent-manifest runtime floor — the minimal tier that makes a published agent pack (RFC 0003) runnable. When `supported: true`, the host implements RFC 0003 `installAgents`: it loads each installed pack's `agents[]` into an in-process AgentRegistry, resolves `systemPromptRef` + `handoff.*SchemaRef` from the tarball at install (RFC 0003 §C/§D), and can DISPATCH a manifest agent on the existing `core.dispatch`/orchestrator loop (RFC 0007/0037/0061). Does NOT imply swarm/consensus (`host.agentRuntime`), long-term memory (`agents.memoryBackends`), or crews beyond `agents.dispatch`. A host advertising `host.agentRuntime: supported` is treated as also satisfying this flag (RFC 0070 §B). Hosts that omit this block do not instantiate manifest agents (today's default).",
1677
+ "additionalProperties": false,
1678
+ "required": [
1679
+ "supported"
1680
+ ],
1681
+ "properties": {
1682
+ "supported": {
1683
+ "type": "boolean",
1684
+ "description": "REQUIRED when the block is present. When `true`, the host loads + dispatches pack-declared manifest agents. A host with `supported: true` MUST enforce each dispatched agent's `toolAllowlist` (RFC 0002 §A14) and MUST NOT leak BYOK plaintext into `agent.*` events or handoff payloads (SR-1)."
1685
+ },
1686
+ "handoffValidation": {
1687
+ "type": "boolean",
1688
+ "default": false,
1689
+ "description": "When `true`, the host validates inbound task payloads against the agent's `handoff.taskSchemaRef` before dispatch and outbound results against `handoff.returnSchemaRef` before persistence (RFC 0003 §D). When `false`/absent, manifests carrying `handoff` schemas are dispatched with opaque payloads."
1690
+ },
1691
+ "installScope": {
1692
+ "type": "string",
1693
+ "enum": [
1694
+ "host",
1695
+ "tenant"
1696
+ ],
1697
+ "default": "host",
1698
+ "description": "RFC 0074. Scope at which manifest agents are installed/approved and therefore enumerated by GET /v1/agents. 'host' (default): a single host-global inventory; the endpoint returns the same set for every caller (RFC 0072's original behavior). 'tenant': agents are installed per tenant·workspace (RFC 0048 owner triple); GET /v1/agents returns ONLY the agents available to the authenticated principal's workspace, and an unapproved/unknown agent 404s — the surface never discloses another tenant's inventory. Does not change dispatch (RFC 0072 §B, owner-triple-scoped POST /v1/runs) or any floor safety guarantee (toolAllowlist/systemPromptRef/SR-1 stay mandatory regardless of scope)."
1699
+ }
1700
+ }
1701
+ },
1702
+ "liveRuntime": {
1703
+ "type": "object",
1704
+ "description": "RFC 0077. The host executes manifest agents against LIVE models and tools (not the deterministic RFC 0070 sample floor) per the normative AgentManifest→live-run mapping (`multi-agent-execution.md` §\"Live manifest dispatch\"), and emits the `agent.invocation.started`/`agent.invocation.completed` content-free bracket around the existing `agent.*` family. REQUIRES `agents.manifestRuntime.supported: true` — `liveRuntime` is a strict superset of the floor. The floor's mandatory safety guarantees (toolAllowlist enforcement, handoff inbound validation, tenant scoping, untrusted-model-output handling, fail-closed per-tool authorization) stay unconditional under `liveRuntime`. Hosts that omit this block run the floor only; the behavioral conformance scenarios skip cleanly.",
1705
+ "additionalProperties": false,
1706
+ "required": [
1707
+ "supported"
1708
+ ],
1709
+ "properties": {
1710
+ "supported": {
1711
+ "type": "boolean",
1712
+ "description": "REQUIRED when present. When `true`, the host performs live manifest dispatch per the §B mapping and emits the `agent.invocation.*` bracket. Gated on `agents.manifestRuntime.supported: true`."
1713
+ },
1714
+ "structuredOutput": {
1715
+ "type": "boolean",
1716
+ "description": "When `true`, the host validates the terminal result against the agent's `handoff.returnSchemaRef` and fails the run with a structured-output error on a non-conforming result rather than shipping it. Absent ⇒ `false` (runs live but does not enforce `returnSchemaRef`)."
1717
+ },
1718
+ "confidenceEscalation": {
1719
+ "type": "boolean",
1720
+ "description": "When `true`, the host honors `AgentManifest.confidence.defaultThreshold` and triggers the RFC 0002 §F escalation contract when an `agent.decided` confidence falls below the effective threshold, rather than silently accepting the decision. Absent ⇒ `false`."
1721
+ },
1722
+ "sources": {
1723
+ "type": "array",
1724
+ "uniqueItems": true,
1725
+ "items": {
1726
+ "type": "string",
1727
+ "enum": [
1728
+ "workflow-node",
1729
+ "run-api",
1730
+ "chat-mention"
1731
+ ]
1732
+ },
1733
+ "description": "Which invocation entry points the host exposes for live manifest dispatch. `workflow-node`: an agent step inside a workflow run (RFC 0072 §B); `run-api`: an agent as the root of POST /v1/runs; `chat-mention`: a chat @agent invocation mapped onto the run surface. Enum membership is NOT mandatory — a host with no chat surface simply omits `chat-mention`. Absent ⇒ `['workflow-node']` (the RFC 0072 §B normative path). All advertised sources MUST emit the identical `agent.invocation.*` + `agent.*` event family."
1734
+ }
1735
+ }
1736
+ },
1737
+ "evalSuite": {
1738
+ "type": "object",
1739
+ "description": "RFC 0081. The host runs portable `agent-eval-suite.schema.json` suites as eval runs (a `mode: \"eval\"` projection over POST /v1/runs, RFC 0081 §B), emits the `eval.started`/`eval.scored`/`eval.completed` content-free family, and terminates with an `eval-summary.schema.json` scorecard. Composes RFC 0026 (per-task cost), RFC 0054 (regression baseline diff), RFC 0056 (human override of an auto-score). Hosts that omit this block reject `mode: \"eval\"` with 501; the behavioral conformance scenario soft-skips. The summary + events are content-free (SECURITY invariant `eval-summary-no-content-leak`).",
1740
+ "additionalProperties": false,
1741
+ "required": [
1742
+ "supported"
1743
+ ],
1744
+ "properties": {
1745
+ "supported": {
1746
+ "type": "boolean",
1747
+ "description": "REQUIRED when present. When `true`, the host accepts `mode: \"eval\"` runs against an `evalSuiteRef`, scores each task, and serves `GET /v1/runs/{runId}/eval-summary`."
1748
+ },
1749
+ "modes": {
1750
+ "type": "array",
1751
+ "uniqueItems": true,
1752
+ "items": {
1753
+ "type": "string",
1754
+ "enum": [
1755
+ "golden",
1756
+ "rubric",
1757
+ "adversarial",
1758
+ "regression",
1759
+ "live-shadow"
1760
+ ]
1761
+ },
1762
+ "description": "Which eval modes the host actually implements (RFC 0081 §D closed vocabulary). Truthful advertisement (RFC 0031): a host advertises ONLY the modes it gates on; a suite requesting an unadvertised mode is rejected at run-create with `400 validation_error`. Absent ⇒ no modes (the host advertises `supported` but gates nothing — effectively shape-only)."
1763
+ },
1764
+ "maxTasksPerSuite": {
1765
+ "type": "integer",
1766
+ "minimum": 1,
1767
+ "description": "MAY. Host ceiling on tasks per suite; a suite exceeding it is rejected at run-create (the RFC 0058 §A clamp pattern)."
1768
+ },
1769
+ "maxCostUsdPerSuite": {
1770
+ "type": "number",
1771
+ "minimum": 0,
1772
+ "description": "MAY. Host ceiling on total eval-run cost; composes RFC 0084 budget enforcement when advertised."
1773
+ }
1774
+ }
1775
+ },
1776
+ "deployment": {
1777
+ "type": "object",
1778
+ "description": "RFC 0082. The host implements an agent deployment lifecycle: per-(agentId, version) deployment records with the seven-state machine (draft/test/staged/active/paused/deprecated/rolled-back), named-channel binding (`agentId@channel` / `@latest` resolved + pinned per-(run, agentId, channel) at first resolution per §B), optional canary traffic-split, a rollback pointer, the content-free `deployment.*` audit events, and the `POST /v1/agents/{agentId}/deployments` promotion contract composing RFC 0049 (`deploy:*` fail-closed scopes) + RFC 0051 (approvalGate) + RFC 0081 (`requiredEval`). Hosts that omit this block reject a `channel`-bearing `AgentRef` with `validation_error` and 501 the deployment endpoint. The promotion endpoint + behavioral lifecycle scenario + reference-host store land at Active → Accepted.",
1779
+ "additionalProperties": false,
1780
+ "required": [
1781
+ "supported"
1782
+ ],
1783
+ "properties": {
1784
+ "supported": {
1785
+ "type": "boolean",
1786
+ "description": "REQUIRED when present. When `true`, the host resolves `AgentRef.channel` bindings, serves deployment records, and accepts promotion transitions."
1787
+ },
1788
+ "channels": {
1789
+ "type": "array",
1790
+ "uniqueItems": true,
1791
+ "items": {
1792
+ "type": "string",
1793
+ "minLength": 1
1794
+ },
1795
+ "description": "The named channels the host resolves (e.g. `[\"stable\", \"canary\", \"latest\"]`). Truthful advertisement (RFC 0031): a `channel` not in this list resolves to no version and fails the run with `no_active_deployment`."
1796
+ },
1797
+ "canary": {
1798
+ "type": "boolean",
1799
+ "description": "When `true`, the host implements canary traffic-split (a per-run §B draw assigns the run to one of the channel's active versions by `canaryPercent`). When `false`/absent, the host MUST reject any `canaryPercent < 100`."
1800
+ },
1801
+ "rollback": {
1802
+ "type": "boolean",
1803
+ "description": "When `true`, the host implements the `rollbackPointer` recovery path (active→rolled-back restoring a prior version to active)."
1804
+ },
1805
+ "states": {
1806
+ "type": "array",
1807
+ "uniqueItems": true,
1808
+ "items": {
1809
+ "type": "string",
1810
+ "enum": [
1811
+ "draft",
1812
+ "test",
1813
+ "staged",
1814
+ "active",
1815
+ "paused",
1816
+ "deprecated",
1817
+ "rolled-back"
1818
+ ]
1819
+ },
1820
+ "description": "The subset of the seven lifecycle states the host implements. Truthful advertisement (RFC 0031): the host MUST reject a transition into a state not advertised here."
1821
+ }
1822
+ }
1823
+ },
1824
+ "roster": {
1825
+ "type": "object",
1826
+ "description": "RFC 0086. The host maintains a standing agent roster: named, tenant-scoped agent INSTANCES (the 'digital-twin employee') that reference a manifest/deployment and own a workflow portfolio, discoverable via GET /v1/agents/roster, with trigger-fired portfolio runs attributed to the member via the content-free `roster.run.initiated` event. REQUIRES `agents.manifestRuntime.supported: true` (a roster entry instantiates a manifest agent). Triggers compose RFC 0052 (schedule) + RFC 0083 (durable work-item bridge) — no new WorkflowTrigger.type; the concrete work surface (a Kanban board) stays a host/vendor extension (§E). Hosts that omit this block do not maintain a roster (the roster reads 501). The roster-management endpoints + behavioral attribution scenario + reference-host store land at Active → Accepted.",
1827
+ "additionalProperties": false,
1828
+ "required": [
1829
+ "supported"
1830
+ ],
1831
+ "properties": {
1832
+ "supported": {
1833
+ "type": "boolean",
1834
+ "description": "REQUIRED when present. When `true`, the host serves the standing roster, projects it on the inventory, and emits `roster.run.initiated` on trigger-fired portfolio runs."
1835
+ },
1836
+ "installScope": {
1837
+ "type": "string",
1838
+ "enum": [
1839
+ "host",
1840
+ "tenant"
1841
+ ],
1842
+ "description": "RFC 0074 carry-forward. `host`: a single global roster. `tenant`: roster entries are scoped per owner triple; GET /v1/agents/roster returns only the caller's entries and a cross-tenant entry 404s. MUST equal `agents.manifestRuntime.installScope` (a roster cannot be host-global while its manifests are tenant-scoped, or vice-versa)."
1843
+ },
1844
+ "portfolioTriggerSources": {
1845
+ "type": "array",
1846
+ "uniqueItems": true,
1847
+ "items": {
1848
+ "type": "string",
1849
+ "minLength": 1
1850
+ },
1851
+ "description": "Which RFC 0052/0083 trigger sources fire portfolio runs on this host (e.g. `[\"schedule\", \"queue\", \"webhook\"]`). Truthful advertisement (RFC 0031): a source not listed does not fire portfolios here."
1852
+ }
1853
+ }
1854
+ },
1855
+ "orgChart": {
1856
+ "type": "object",
1857
+ "description": "RFC 0087. The host maintains a tenant-scoped, DESCRIPTIVE org-chart over RFC 0086 roster members: departments + roles with acyclic `reportsTo` edges + a derived responsibility roll-up, discoverable via GET /v1/agents/org-chart. The load-bearing guarantee (§B `org-position-no-authority-escalation`): an org edge confers NO authority — it MUST NOT widen `toolAllowlist` (RFC 0002 §A14), grant an RBAC scope (RFC 0049), or bypass an approval gate (RFC 0051); org position MUST NOT be an authorization input. The schema carries no authority-bearing field, and a conformant host MUST NOT derive authority from position out-of-band. REQUIRES `agents.roster.supported: true` (the chart's members are roster entries). Hosts that omit this block have no org-chart surface (the read 501s). The org-chart-management endpoints + behavioral non-authority scenario + reference-host store land at Active → Accepted.",
1858
+ "additionalProperties": false,
1859
+ "required": [
1860
+ "supported"
1861
+ ],
1862
+ "properties": {
1863
+ "supported": {
1864
+ "type": "boolean",
1865
+ "description": "REQUIRED when present. When `true`, the host serves the tenant-scoped org-chart + the responsibility roll-up. The §B non-authority guarantee holds at every `installScope` — it is never gated, weakened, or opt-out."
1866
+ },
1867
+ "installScope": {
1868
+ "type": "string",
1869
+ "enum": [
1870
+ "host",
1871
+ "tenant"
1872
+ ],
1873
+ "description": "RFC 0074 carry-forward. `host`: a single global chart. `tenant`: charts are scoped per owner triple; GET /v1/agents/org-chart returns only the caller's chart. SHOULD equal `agents.roster.installScope` (the members are roster entries)."
1874
+ },
1875
+ "departmentNesting": {
1876
+ "type": "boolean",
1877
+ "description": "When `true`, the host supports `parentDepartmentId` trees. When `false`/absent, the host MUST reject a non-null `parentDepartmentId` (truthful advertisement, RFC 0031)."
1878
+ },
1879
+ "responsibilityView": {
1880
+ "type": "boolean",
1881
+ "description": "When `true`, the host computes the §D responsibility roll-up (the union of a department's members' RFC 0086 portfolios) on GET /v1/agents/org-chart/{departmentId}."
1882
+ }
1883
+ }
1884
+ },
1885
+ "dispatchMapping": {
1886
+ "type": "boolean",
1887
+ "default": false,
1888
+ "description": "Phase 6.1 (RFC 0022 §A). When `true`, host honors `inputMapping` / `outputMapping` / `perWorkerInputMappings` / `perWorkerOutputMappings` on `DispatchConfig` — building child inputs from parent variables before dispatch and harvesting child variables into parent variables on completion. Implies (but does NOT require) `agents.dispatch: true`. Hosts that set `agents.dispatch: true` but omit / `false` this flag MUST refuse workflows that carry non-empty mapping fields at registration with `validation_error` + `details.requiredCapability: 'agents.dispatchMapping'`."
1889
+ },
1890
+ "reasoning": {
1891
+ "type": "object",
1892
+ "description": "Phase 1 reasoning-event verbosity configuration.",
1893
+ "properties": {
1894
+ "verbosity": {
1895
+ "type": "string",
1896
+ "enum": [
1897
+ "summary",
1898
+ "full",
1899
+ "off"
1900
+ ],
1901
+ "description": "Default reasoning verbosity for `agent.reasoned` events when the run does not override via `RunOptions.configurable.reasoningVerbosity`."
1902
+ },
1903
+ "tokenLimit": {
1904
+ "type": "integer",
1905
+ "minimum": 0,
1906
+ "description": "Effective cap on reasoning trace length when verbosity is `summary`. Default 512 tokens."
1907
+ },
1908
+ "streaming": {
1909
+ "type": "boolean",
1910
+ "default": false,
1911
+ "description": "RFC 0024. When `true`, the host MAY emit `agent.reasoning.delta` events while a reasoning block is still open, in addition to the final `agent.reasoned`. Hosts that omit / `false` this flag emit only the final `agent.reasoned`. Consumers MUST tolerate both modes."
1912
+ }
1913
+ },
1914
+ "additionalProperties": false
1915
+ },
1916
+ "subRunAttestation": {
1917
+ "type": "boolean",
1918
+ "default": false,
1919
+ "description": "RFC 0063 (`Active`). When `true`, host honors the optional `outputAttestation` block on `core.subWorkflow`: computes a content checksum (RFC 8785 JCS + SHA-256, the `replay.md` recipe) over a child's harvested outputs and surfaces it as the additive optional `attestation` object on the existing `core.workflowChain.event { phase: 'output.harvested' }` (RFC 0037) BEFORE applying `outputMapping`; when the config sets `requireApproval: true`, suspends the parent via an `approval` interrupt (RFC 0051) before merge and fails closed (no `accept`/`edit-accept` ⇒ no merge). Reuses RFC 0051's `approval` kind + RFC 0049 scopes for `principalScope` — no new interrupt kind, event type, or error code. Hosts that omit / `false` this flag treat `outputAttestation` as inert (blind merge, today's behavior)."
1920
+ },
1921
+ "proposals": {
1922
+ "type": "object",
1923
+ "description": "RFC 0096 (`Active`). Reviewable learning — the host synthesizes reusable artifacts (skills/packs/templates/automations) from run/tool traces as INERT, reviewable drafts that MUST NOT influence the resolution, planning, or execution of any run until an authorized principal activates them. A host advertising this serves the `/v1/host/sample/proposals` surface (promotable to `/v1/proposals`) and emits the content-free `proposal.created` / `proposal.activated` events. Activation is delegated to RFC 0051 approval-gate or RFC 0049 RBAC — no new authorization path. On `apply` the installed artifact MUST byte-match the last-persisted `artifact` (no silent re-synthesis). Hosts that omit this block do not synthesize proposals; the conformance scenarios skip cleanly.",
1924
+ "additionalProperties": false,
1925
+ "required": [
1926
+ "artifactKinds",
1927
+ "activation"
1928
+ ],
1929
+ "properties": {
1930
+ "artifactKinds": {
1931
+ "type": "array",
1932
+ "items": {
1933
+ "type": "string",
1934
+ "enum": [
1935
+ "agent-pack",
1936
+ "workflow-chain-pack",
1937
+ "prompt-template",
1938
+ "automation"
1939
+ ]
1940
+ },
1941
+ "uniqueItems": true,
1942
+ "description": "Which reusable artifact kinds the host can propose. `agent-pack` (RFC 0003), `workflow-chain-pack` (RFC 0013), `prompt-template` (RFC 0027), `automation` (RFC 0052 scheduled job)."
1943
+ },
1944
+ "duplicationDetection": {
1945
+ "type": "boolean",
1946
+ "default": false,
1947
+ "description": "When `true`, the host populates `Proposal.duplicateOf` with an existing artifact ref the proposal restates/overlaps (the 'Curator' duplication signal)."
1948
+ },
1949
+ "activation": {
1950
+ "type": "string",
1951
+ "enum": [
1952
+ "approval-gate",
1953
+ "direct-rbac"
1954
+ ],
1955
+ "description": "`approval-gate`: `apply` MUST drive an RFC 0051 gate (role/scope/quorum, audited override) and MUST NOT install unless granted/overridden. `direct-rbac`: `apply` requires only the RFC 0049 scope the host advertises for activation."
1956
+ }
1957
+ }
1958
+ },
1959
+ "goals": {
1960
+ "type": "object",
1961
+ "description": "RFC 0097 (`Active`). Standing goals — a durable objective with explicit completion criteria, evaluated by a host-side judge (RFC 0090 verifier or host evaluator), that keeps an agent working across turns/runs until the judge is satisfied, a declared RFC 0058 bound is crossed, or the agent escalates (RFC 0044). A host advertising this serves `/v1/host/sample/goals` (promotable to `/v1/goals`) and emits the content-free `goal.evaluated` / `goal.closed` events. Completion MUST be the judge's verdict — a client MUST NOT set `state: satisfied` directly. Continuation MUST be bounded. Hosts that omit this block do not run standing goals; the conformance scenarios skip cleanly.",
1962
+ "additionalProperties": false,
1963
+ "required": [
1964
+ "judge",
1965
+ "continuation"
1966
+ ],
1967
+ "properties": {
1968
+ "judge": {
1969
+ "type": "string",
1970
+ "enum": [
1971
+ "verifier",
1972
+ "host"
1973
+ ],
1974
+ "description": "`verifier`: completion is an RFC 0090 verifier verdict. `host`: an opaque host evaluator."
1975
+ },
1976
+ "continuation": {
1977
+ "type": "array",
1978
+ "items": {
1979
+ "type": "string",
1980
+ "enum": [
1981
+ "schedule",
1982
+ "commitment",
1983
+ "heartbeat",
1984
+ "manual"
1985
+ ]
1986
+ },
1987
+ "uniqueItems": true,
1988
+ "description": "How a goal re-engages work between judge checks. `schedule` (RFC 0052), `commitment` (RFC 0068), `heartbeat` (RFC 0060), `manual`."
1989
+ },
1990
+ "requiresBounds": {
1991
+ "type": "boolean",
1992
+ "default": true,
1993
+ "description": "When `true` (default), a goal MUST declare valid RFC 0058 `bounds` before it may activate; a `POST /goals` without bounds returns 422. The host MUST stop continuation and set `state: bound-exceeded` when any declared bound (iteration count / accumulated cost / wall-clock deadline) is crossed."
1994
+ }
1995
+ }
1996
+ }
1997
+ },
1998
+ "additionalProperties": true
1999
+ },
2000
+ "memory": {
2001
+ "type": "object",
2002
+ "description": "MemoryAdapter capability block per RFC 0004 + the optional compaction profile per RFC 0012. Hosts that don't wire any memory surface omit the entire block.",
2003
+ "properties": {
2004
+ "supported": {
2005
+ "type": "boolean",
2006
+ "description": "When `true`, host implements the four-operation MemoryAdapter contract (`list`, `get`, `put`, `delete`) per RFC 0004 §A."
2007
+ },
2008
+ "maxEntrySizeBytes": {
2009
+ "type": "integer",
2010
+ "minimum": 1,
2011
+ "description": "Upper bound on `MemoryEntry.content` size. Hosts SHOULD reject `put` exceeding this with `validation_error`."
2012
+ },
2013
+ "ttlSupported": {
2014
+ "type": "boolean",
2015
+ "description": "When `true`, host honors `expiresAt` per RFC 0004 §E."
2016
+ },
2017
+ "compaction": {
2018
+ "type": "object",
2019
+ "description": "RFC 0012 Memory Compaction Profile (Accepted 2026-05-15). Hosts that distill many short-lived MemoryEntry rows into fewer long-lived ones MAY advertise here; advertising implies the SR-1 carry-forward invariant (`SECURITY/invariants.yaml` row `memory-compaction-sr-1-carry-forward`).",
2020
+ "required": [
2021
+ "supported"
2022
+ ],
2023
+ "properties": {
2024
+ "supported": {
2025
+ "type": "boolean",
2026
+ "description": "REQUIRED when the sub-block is present. When `true`, host performs compaction over `longTerm` memory and emits the `memory.compacted` event per `observability.md` §Canonical event vocabulary."
2027
+ },
2028
+ "trigger": {
2029
+ "type": "string",
2030
+ "enum": [
2031
+ "host-managed",
2032
+ "client-requested",
2033
+ "both"
2034
+ ],
2035
+ "description": "REQUIRED when `supported: true` per RFC 0012 §A (enforced via the `if/then` clause). `host-managed` runs on a host-internal schedule clients do not control. `client-requested` and `both` are reserved enum values; v1.x normates only `host-managed`."
2036
+ },
2037
+ "maxInputEntries": {
2038
+ "type": "integer",
2039
+ "minimum": 1,
2040
+ "description": "Informational ceiling on how many source entries one compaction call collapses. Not wire-enforced."
2041
+ },
2042
+ "maxOutputBytes": {
2043
+ "type": "integer",
2044
+ "minimum": 0,
2045
+ "description": "Informational ceiling on the distilled entry size. SHOULD be ≤ `memory.maxEntrySizeBytes`."
2046
+ }
2047
+ },
2048
+ "additionalProperties": false,
2049
+ "if": {
2050
+ "properties": {
2051
+ "supported": {
2052
+ "const": true
2053
+ }
2054
+ },
2055
+ "required": [
2056
+ "supported"
2057
+ ]
2058
+ },
2059
+ "then": {
2060
+ "required": [
2061
+ "supported",
2062
+ "trigger"
2063
+ ]
2064
+ }
2065
+ },
2066
+ "distillation": {
2067
+ "type": "object",
2068
+ "description": "RFC 0062 (`Active`). Scheduled, token-budgeted background compaction — the 'dream' pattern — built on compaction (RFC 0012) + scheduling (RFC 0052) + the workspace index (RFC 0059). A distillation run IS a compaction run with a mandatory token budget, an optional schedule, and a retrieval index wrapped around it; it reuses the `memory.compacted` event (extended with the additive optional `distillation` sub-object) rather than a parallel `memory.distilled` event. SR-1 carry-forward (RFC 0012 §D) holds — a distilled archive MUST NOT re-expose a redacted secret. Hosts that omit this block keep plain on-demand compaction (RFC 0012) or no memory.",
2069
+ "required": [
2070
+ "supported"
2071
+ ],
2072
+ "additionalProperties": false,
2073
+ "properties": {
2074
+ "supported": {
2075
+ "type": "boolean",
2076
+ "description": "REQUIRED when the sub-block is present. When `true`, host honors the `distillation.tokenBudget` reserved run-option key, runs budgeted distillation over `longTerm` memory, writes a stable archive, and emits `memory.compacted` with the `distillation` sub-object."
2077
+ },
2078
+ "maxTokenBudget": {
2079
+ "type": "integer",
2080
+ "minimum": 1,
2081
+ "description": "Largest per-run distillation token budget the host honors. A supplied `distillation.tokenBudget` is clamped to this; absent ⇒ the host defaults to this."
2082
+ },
2083
+ "scheduled": {
2084
+ "type": "boolean",
2085
+ "description": "When `true`, host can initiate distillation on a schedule (requires `capabilities.scheduling`, RFC 0052). Distillation MAY also run on-demand without scheduling."
2086
+ },
2087
+ "indexEmitted": {
2088
+ "type": "boolean",
2089
+ "description": "When `true`, host writes a retrievable memory-index manifest (`MEMORY-INDEX.json`, a workspace file per RFC 0059) after distillation; updating it emits `workspace.updated`."
2090
+ },
2091
+ "tokenizerName": {
2092
+ "type": "string",
2093
+ "description": "Identifier of the tokenizer the budget is counted against (e.g. `claude`, `gpt-4`). The budget is best-effort-honest per this tokenizer (±10% conformance tolerance), not byte-exact."
2094
+ },
2095
+ "archiveRetention": {
2096
+ "type": "string",
2097
+ "description": "ISO-8601 duration (e.g. `P30D`) the distilled archives persist before GC. Recursive distillation (distilling prior archives) is allowed; each level re-checks SR-1."
2098
+ }
2099
+ }
2100
+ },
2101
+ "attribution": {
2102
+ "type": "object",
2103
+ "description": "RFC 0057 Memory write-attribution. Hosts that emit per-node memory provenance on the run event log MAY advertise here. Advertising `emitsWriteEvents: true` commits the host to emit a `memory.written` RunEvent for every memory write a run makes (identifiers only, never content), and implies the SECURITY invariants `memory-attribution-no-content` + `memory-attribution-tenant-scoped`.",
2104
+ "required": [
2105
+ "supported"
2106
+ ],
2107
+ "properties": {
2108
+ "supported": {
2109
+ "const": true,
2110
+ "description": "REQUIRED when the sub-block is present. The block is omitted entirely by hosts that do not support write attribution."
2111
+ },
2112
+ "emitsWriteEvents": {
2113
+ "type": "boolean",
2114
+ "description": "When `true`, the host emits `memory.written` (per `run-event-payloads.schema.json#/$defs/memoryWritten`) for every memory write during a run. When `false` or absent, consumers MUST tolerate the event never appearing."
2115
+ }
2116
+ },
2117
+ "additionalProperties": false
2118
+ },
2119
+ "writable": {
2120
+ "type": "boolean",
2121
+ "description": "RFC 0080 §A (`write` dimension). Absent ⇒ the host implements the full RFC 0004 four-operation MemoryAdapter (`put`/`delete` available — i.e. writable), the back-compatible default. A read-only host (`get`/`list` only) MUST set `writable: false` so a consumer can distinguish a read-only store from a read/write one. Only meaningful when `supported: true`."
2122
+ },
2123
+ "search": {
2124
+ "type": "object",
2125
+ "description": "RFC 0080 §A (`search` dimension) — NEW optional. Semantic or filtered query beyond the RFC 0004 `list` enumeration. Absent ⇒ only `list`/`get` retrieval is advertised. The query path itself stays the host-internal MemoryAdapter (RFC 0080 §B — no portable `GET /v1/memory` at v1.x).",
2126
+ "required": [
2127
+ "supported"
2128
+ ],
2129
+ "additionalProperties": false,
2130
+ "properties": {
2131
+ "supported": {
2132
+ "type": "boolean",
2133
+ "description": "REQUIRED when the sub-block is present. When `true`, the host supports memory query beyond `list` (semantic and/or filter modes per `modes`)."
2134
+ },
2135
+ "modes": {
2136
+ "type": "array",
2137
+ "items": {
2138
+ "type": "string",
2139
+ "enum": [
2140
+ "semantic",
2141
+ "filter"
2142
+ ]
2143
+ },
2144
+ "uniqueItems": true,
2145
+ "description": "The query modes the host supports. `semantic` = embedding/similarity retrieval; `filter` = structured predicate query over entry metadata. Omitted ⇒ unspecified mode (the host supports some query beyond `list`)."
2146
+ }
2147
+ }
2148
+ },
2149
+ "retention": {
2150
+ "type": "object",
2151
+ "description": "RFC 0080 §A (`retention/forget` dimension) — NEW optional. TTL expiry (`agent-memory.md §TTL`) and/or an explicit forget operation. Absent ⇒ no TTL beyond `ttlSupported` and no forget operation advertised. `forget` is a host-managed mutation OUTSIDE the replay envelope (RFC 0080 §UQ3 / `replay.md` §Recorded-fact events — a replay re-reads the log-recorded snapshot, not live memory).",
2152
+ "additionalProperties": false,
2153
+ "properties": {
2154
+ "ttl": {
2155
+ "type": "boolean",
2156
+ "description": "When `true`, memory entries expire per `expiresAt` (the `ttlSupported` semantics surfaced as a named retention dimension)."
2157
+ },
2158
+ "forget": {
2159
+ "type": "boolean",
2160
+ "description": "When `true`, the host supports a tenant-scoped delete-by-subject forget operation (composes the CTI-1 cross-tenant invariant — a forget MUST NOT cross tenant boundaries)."
2161
+ }
2162
+ }
2163
+ },
2164
+ "injectionBudget": {
2165
+ "type": "object",
2166
+ "description": "RFC 0113 (`Active`). The host honors `MemoryListOptions.tokenBudget` — a token-denominated bound on a single injection read (the live read that feeds a turn), distinct from RFC 0062 distillation's background-compaction budget. Advertising it commits the host to return a token-bounded prefix of the ranked entry list (over-budget single entry omitted, never truncated mid-entry) over the SR-1-redacted, CTI-1-single-tenant result set. Relevance ranking is NOT advertised here — `rank:'relevance'` delegates to the existing `memory.search` semantic mode (RFC 0080), so there is exactly one relevance surface in the corpus. Hosts that omit this block do not honor `tokenBudget` (a supplied `tokenBudget` is ignored, today's `limit`/`tag` behavior).",
2167
+ "additionalProperties": false,
2168
+ "required": [
2169
+ "supported"
2170
+ ],
2171
+ "properties": {
2172
+ "supported": {
2173
+ "type": "boolean",
2174
+ "description": "REQUIRED when the sub-block is present. When `true`, the host honors `MemoryListOptions.tokenBudget` per `agent-memory.md` §\"Injection budget\"."
2175
+ },
2176
+ "tokenCounter": {
2177
+ "type": "string",
2178
+ "enum": [
2179
+ "o200k_base",
2180
+ "cl100k_base",
2181
+ "chars",
2182
+ "host-defined"
2183
+ ],
2184
+ "description": "The unit `tokenBudget` is denominated in. REQUIRED when `injectionBudget.supported` (enforced via the `if/then` clause). `o200k_base`/`cl100k_base` are tokenizer encodings; `chars` counts UTF-8/Unicode characters of the entry `content` (a tokenizer-free unit a client can reason about directly — preferred over opaque `host-defined`); `host-defined` is an opaque host unit. The over-budget-single-entry-omitted rule applies regardless of unit. RFC 0111 aligns to these same values when it lands (0113 lands first); no shared `$ref` (decoupled)."
2185
+ }
2186
+ },
2187
+ "if": {
2188
+ "properties": {
2189
+ "supported": {
2190
+ "const": true
2191
+ }
2192
+ },
2193
+ "required": [
2194
+ "supported"
2195
+ ]
2196
+ },
2197
+ "then": {
2198
+ "required": [
2199
+ "supported",
2200
+ "tokenCounter"
2201
+ ]
2202
+ }
2203
+ }
2204
+ },
2205
+ "additionalProperties": true
2206
+ },
2207
+ "conversationPrimitive": {
2208
+ "type": "boolean",
2209
+ "description": "Multi-Agent Shift Phase 4. When `true`, host advertises that it implements the `core.conversationGate` typeId AND honors the `conversation.start` / `conversation.exchange` / `conversation.close` suspend variants. Hosts that don't claim this fall back to `clarification.requested` interrupts for multi-turn user interjections."
2210
+ },
2211
+ "subWorkflow": {
2212
+ "type": "object",
2213
+ "description": "Capability surface for `core.subWorkflow` extensions. The baseline `core.subWorkflow` contract (RFC 0007 + `node-packs.md` §contract) is unconditional and does NOT require a capability flag; this object carries the additive extensions a host MAY support. Added by RFC 0022 §B + §C.",
2214
+ "properties": {
2215
+ "inputMapping": {
2216
+ "type": "boolean",
2217
+ "default": false,
2218
+ "description": "RFC 0022 §B. When `true`, host honors the `inputMapping` field on `core.subWorkflow` configs — seeding the child workflow's initial variable bag from `parentVariables[parentKey]` projections, overriding any matching `variables[].defaultValue` declaration on the child. When `false` or absent, hosts MUST refuse workflows that carry a non-empty `inputMapping` at registration with `validation_error` + `details.requiredCapability: 'subWorkflow.inputMapping'`. Silent ignore is NOT conformant."
2219
+ }
2220
+ },
2221
+ "additionalProperties": false
2222
+ },
2223
+ "fs": {
2224
+ "type": "object",
2225
+ "description": "RFC 0014 (`Active`). Filesystem capability — read/write/list/stat/delete inside a sandbox root. Required by the `core.openwop.files` pack. Hosts MUST resolve every input path relative to `sandboxRoot`, reject any path that escapes via `..` segments or symlinks, and enforce `maxFileSizeBytes` on write. Path-traversal rejection is normative — see `SECURITY/invariants.yaml` row `fs-path-traversal`.",
2226
+ "properties": {
2227
+ "supported": {
2228
+ "type": "boolean",
2229
+ "description": "Host advertises `ctx.fs.{read,write,delete,stat,list}`. `false` (or omission) signals the host does NOT expose a filesystem surface; `core.openwop.files` registration MUST refuse on such hosts."
2230
+ },
2231
+ "sandboxRoot": {
2232
+ "type": "string",
2233
+ "description": "Absolute path. Every fs operation is resolved relative to this root. MUST be set when `supported: true`."
2234
+ },
2235
+ "maxFileSizeBytes": {
2236
+ "type": "integer",
2237
+ "minimum": 0,
2238
+ "description": "Per-file size cap enforced on write. 0 = unlimited (NOT recommended). Reads of files larger than this MAY return `file_too_large`."
2239
+ },
2240
+ "image": {
2241
+ "type": "object",
2242
+ "description": "Image-processing sub-capability. Optional.",
2243
+ "properties": {
2244
+ "supported": {
2245
+ "type": "boolean"
2246
+ },
2247
+ "formats": {
2248
+ "type": "array",
2249
+ "items": {
2250
+ "type": "string",
2251
+ "enum": [
2252
+ "jpeg",
2253
+ "png",
2254
+ "webp",
2255
+ "avif",
2256
+ "gif"
2257
+ ]
2258
+ }
2259
+ }
2260
+ },
2261
+ "additionalProperties": false
2262
+ },
2263
+ "pdf": {
2264
+ "type": "object",
2265
+ "description": "PDF-processing sub-capability. Optional.",
2266
+ "properties": {
2267
+ "supported": {
2268
+ "type": "boolean"
2269
+ }
2270
+ },
2271
+ "additionalProperties": false
2272
+ },
2273
+ "transport": {
2274
+ "type": "object",
2275
+ "description": "Network file-transport sub-capabilities. Optional.",
2276
+ "properties": {
2277
+ "ftp": {
2278
+ "type": "boolean"
2279
+ },
2280
+ "sftp": {
2281
+ "type": "boolean"
2282
+ },
2283
+ "ssh": {
2284
+ "type": "boolean"
2285
+ }
2286
+ },
2287
+ "additionalProperties": false
2288
+ }
2289
+ },
2290
+ "if": {
2291
+ "properties": {
2292
+ "supported": {
2293
+ "const": true
2294
+ }
2295
+ },
2296
+ "required": [
2297
+ "supported"
2298
+ ]
2299
+ },
2300
+ "then": {
2301
+ "required": [
2302
+ "supported",
2303
+ "sandboxRoot"
2304
+ ]
2305
+ },
2306
+ "additionalProperties": false
2307
+ },
2308
+ "kvStorage": {
2309
+ "type": "object",
2310
+ "description": "RFC 0015 (`Active`). TTL-aware key-value store with atomic increment + compare-and-swap. Required by `core.openwop.storage` kv-* nodes. Hosts MUST partition values by tenant (`kv-cross-tenant-isolation` invariant) and atomically apply increments + CAS when those flags are advertised.",
2311
+ "properties": {
2312
+ "supported": {
2313
+ "type": "boolean"
2314
+ },
2315
+ "maxKeyBytes": {
2316
+ "type": "integer",
2317
+ "minimum": 0
2318
+ },
2319
+ "maxValueBytes": {
2320
+ "type": "integer",
2321
+ "minimum": 0
2322
+ },
2323
+ "maxTtlSeconds": {
2324
+ "type": "integer",
2325
+ "minimum": 0
2326
+ },
2327
+ "atomicIncrement": {
2328
+ "type": "boolean"
2329
+ },
2330
+ "compareAndSwap": {
2331
+ "type": "boolean"
2332
+ }
2333
+ },
2334
+ "additionalProperties": false
2335
+ },
2336
+ "tableStorage": {
2337
+ "type": "object",
2338
+ "description": "RFC 0016 (`Active`). Structured-record store with user-defined schemas. Sibling to kvStorage. Cross-tenant isolation enforced (mirrors RFC 0015 invariant).",
2339
+ "properties": {
2340
+ "supported": {
2341
+ "type": "boolean"
2342
+ },
2343
+ "maxRowsPerTable": {
2344
+ "type": "integer",
2345
+ "minimum": 0
2346
+ },
2347
+ "maxColumnsPerRow": {
2348
+ "type": "integer",
2349
+ "minimum": 0
2350
+ },
2351
+ "indexable": {
2352
+ "type": "boolean"
2353
+ },
2354
+ "fullTextSearch": {
2355
+ "type": "boolean"
2356
+ }
2357
+ },
2358
+ "additionalProperties": false
2359
+ },
2360
+ "queueBus": {
2361
+ "type": "object",
2362
+ "description": "RFC 0017 (`Active`). Inbound queue + stream capability — publish, consume (trigger), ack/nack/dead-letter. Cross-tenant message isolation invariant (`queue-cross-tenant-isolation`). Sibling to host.messaging (which is outbound-egress-only).",
2363
+ "properties": {
2364
+ "supported": {
2365
+ "type": "boolean"
2366
+ },
2367
+ "backends": {
2368
+ "type": "array",
2369
+ "items": {
2370
+ "type": "string",
2371
+ "enum": [
2372
+ "rabbitmq",
2373
+ "kafka",
2374
+ "sqs",
2375
+ "sns",
2376
+ "pubsub",
2377
+ "mqtt",
2378
+ "nats",
2379
+ "redis-streams",
2380
+ "in-memory"
2381
+ ]
2382
+ }
2383
+ },
2384
+ "deadLetterSupported": {
2385
+ "type": "boolean"
2386
+ },
2387
+ "stream": {
2388
+ "type": "object",
2389
+ "properties": {
2390
+ "supported": {
2391
+ "type": "boolean"
2392
+ },
2393
+ "fromBeginning": {
2394
+ "type": "boolean"
2395
+ }
2396
+ },
2397
+ "additionalProperties": false
2398
+ }
2399
+ },
2400
+ "additionalProperties": false
2401
+ },
2402
+ "scheduling": {
2403
+ "type": "object",
2404
+ "description": "RFC 0052 (`Draft`). Time-based run initiation behind the `schedule` trigger — gives the trigger a portable, durable, once-per-tick execution contract. Composes with `queueBus` (RFC 0017) where the host backs scheduling with a queue; orthogonal to the in-DAG `core.control.delay` primitive (which delays a node mid-run, not run initiation).",
2405
+ "required": [
2406
+ "supported"
2407
+ ],
2408
+ "properties": {
2409
+ "supported": {
2410
+ "type": "boolean"
2411
+ },
2412
+ "cron": {
2413
+ "type": "boolean",
2414
+ "description": "Host honors cron-expression schedules."
2415
+ },
2416
+ "delayed": {
2417
+ "type": "boolean",
2418
+ "description": "Host honors one-shot delayed execution."
2419
+ },
2420
+ "calendar": {
2421
+ "type": "boolean",
2422
+ "description": "Host honors calendar-reference schedules."
2423
+ },
2424
+ "maxFutureHorizon": {
2425
+ "type": "string",
2426
+ "description": "ISO-8601 duration (e.g. `P90D`); the farthest-future a run may be scheduled. Schedules beyond it MUST be rejected with `schedule_horizon_exceeded`."
2427
+ }
2428
+ },
2429
+ "additionalProperties": false
2430
+ },
2431
+ "heartbeat": {
2432
+ "type": "object",
2433
+ "description": "RFC 0060 (`Draft`). System-managed, predicate-gated polling: a short-interval, runtime-bounded evaluation of an idempotent predicate that emits state-change events and conditionally enqueues a run, rather than re-running an agent blindly. Composes with `scheduling` (RFC 0052) for the once-per-tick interval substrate; the controlled, request-shaped exception to openwop's poll-free design (`positioning.md`).",
2434
+ "required": [
2435
+ "supported"
2436
+ ],
2437
+ "properties": {
2438
+ "supported": {
2439
+ "type": "boolean"
2440
+ },
2441
+ "minIntervalSec": {
2442
+ "type": "integer",
2443
+ "minimum": 1,
2444
+ "description": "Smallest interval the host honors; requests below it clamp up."
2445
+ },
2446
+ "maxRuntimeMs": {
2447
+ "type": "integer",
2448
+ "minimum": 1,
2449
+ "description": "Per-tick predicate-evaluation budget; bounded above by `capabilities.limits.maxRunDurationMs` (RFC 0058) as the hard ceiling. Over-budget evaluation is terminated and reported as `heartbeat.evaluated { status: 'timeout' }`."
2450
+ }
2451
+ },
2452
+ "additionalProperties": false
2453
+ },
2454
+ "toolHooks": {
2455
+ "type": "object",
2456
+ "description": "RFC 0064 (`Active`) — sibling of `heartbeat`. Per-tool authorization + rate limiting + content-free tool-call audit fields, layered on the existing `agent.toolCalled` / `agent.toolReturned` events (RFC 0002). Generalizes the MCP-specific bridges across transports (mcp / http / native). Reuses RFC 0049's `forbidden` error + `authorization-fail-closed` invariant and the existing `rate_limited` error — no new event type, error code, or invariant.",
2457
+ "required": [
2458
+ "supported"
2459
+ ],
2460
+ "additionalProperties": false,
2461
+ "properties": {
2462
+ "supported": {
2463
+ "type": "boolean"
2464
+ },
2465
+ "prePostEvents": {
2466
+ "type": "boolean",
2467
+ "description": "Host populates `argsHash`/`principal`/`transport` on `agent.toolCalled` + `status`/`durationMs` on `agent.toolReturned` for every external tool call."
2468
+ },
2469
+ "perToolAuthorization": {
2470
+ "type": "boolean",
2471
+ "description": "Host enforces per-tool scopes against the run principal (RFC 0049), fail-closed; a lacked-or-unevaluable scope yields `agent.toolReturned { status: 'forbidden' }` + a `forbidden` (403) error and the tool is never invoked."
2472
+ },
2473
+ "perToolRateLimit": {
2474
+ "type": "boolean",
2475
+ "description": "Host applies a per-`(principal, tool)` token-bucket rate limit; exhaustion yields `agent.toolReturned { status: 'rate_limited' }` + a `rate_limited` (429) error."
2476
+ }
2477
+ }
2478
+ },
2479
+ "toolCatalog": {
2480
+ "type": "object",
2481
+ "description": "RFC 0078 (`Active`). The host exposes a read-only projection of its tool surfaces (node-pack / workflow / MCP / connector / host-extension) at `GET /v1/tools` + `GET /v1/tools/{toolId}`, returning `ToolDescriptor` records (`tool-descriptor.schema.json`). Optional; hosts that omit it expose no catalog (today's behavior) and the conformance scenarios skip cleanly. Read-only — the catalog never mutates tools; tool invocation stays on the existing surfaces (agent dispatch, `core.dispatch`, MCP). The listing is authorization-scoped + non-disclosing (RFC 0074 pattern).",
2482
+ "required": [
2483
+ "supported"
2484
+ ],
2485
+ "additionalProperties": false,
2486
+ "properties": {
2487
+ "supported": {
2488
+ "type": "boolean",
2489
+ "description": "REQUIRED when present. `true` ⇒ `GET /v1/tools` + `GET /v1/tools/{toolId}` are served per RFC 0078 §B."
2490
+ },
2491
+ "sources": {
2492
+ "type": "array",
2493
+ "uniqueItems": true,
2494
+ "items": {
2495
+ "type": "string",
2496
+ "enum": [
2497
+ "node-pack",
2498
+ "workflow",
2499
+ "mcp",
2500
+ "connector",
2501
+ "host-extension"
2502
+ ]
2503
+ },
2504
+ "description": "Which tool sources the catalog projects. A host advertises only the sources it actually surfaces; a consumer MUST tolerate any subset. Absent ⇒ all sources the host implements."
2505
+ },
2506
+ "sessionLifecycle": {
2507
+ "type": "boolean",
2508
+ "description": "`true` ⇒ the host emits the RFC 0078 §D tool-session lifecycle events (`tool.session.opened`/`tool.session.closed`, content-free) bracketing the existing RFC 0064 `agent.toolCalled`/`agent.toolReturned` call events for multi-step interactions. Absent ⇒ `false` (single-shot tool calls only)."
2509
+ },
2510
+ "compactView": {
2511
+ "type": "boolean",
2512
+ "description": "RFC 0112. `true` ⇒ the host honors `GET /v1/tools?view=compact` + `GET /v1/tools/{toolId}?view=compact`, returning the `{ tools: CompactToolDescriptor[] }` projection (`compact-tool-descriptor.schema.json`): the heavy descriptor fields (`outputSchema`/`auth`/`egress`/`approval`/`replayPolicy`/`costHint`/`latencyHint`) are dropped and any `inputSchema` is bounded to the compact structural subset. The compact `tools[]` carries the same `toolId` set as the standard view for the same principal. Absent ⇒ the host treats `view=compact` as an unknown query param (standard view)."
2513
+ }
2514
+ }
2515
+ },
2516
+ "httpClient": {
2517
+ "type": "object",
2518
+ "description": "Host outbound-HTTP surface. The host's HTTP-client node egress (e.g. `core.http.request`) MUST be SSRF-guarded (`ssrfGuard: true`) with a positive `maxResponseBodyBytes` cap — the `http-client-ssrf-guard` protocol invariant. RFC 0076 §B adds the OPTIONAL `safeFetch` sub-capability: a host-mediated `ctx.http.safeFetch(url, init?)` exposed to pack runtime code, backed by the SAME SSRF guard (resolve→pin→connect, metadata-endpoint blocklist, DNS-rebinding defeat) so packs need not reach for raw DNS/sockets. See `host-capabilities.md` §host.http.",
2519
+ "required": [
2520
+ "supported"
2521
+ ],
2522
+ "additionalProperties": false,
2523
+ "properties": {
2524
+ "supported": {
2525
+ "type": "boolean"
2526
+ },
2527
+ "ssrfGuard": {
2528
+ "type": "boolean",
2529
+ "description": "Host rejects egress to loopback / RFC 1918 / link-local / cloud-metadata addresses (resolve→pin→connect). MUST be `true` when `supported` (the `http-client-ssrf-guard` invariant)."
2530
+ },
2531
+ "maxResponseBodyBytes": {
2532
+ "type": "integer",
2533
+ "minimum": 1,
2534
+ "description": "Positive ceiling on a response body the host will buffer. Reused by `safeFetch`."
2535
+ },
2536
+ "requestTimeoutMs": {
2537
+ "type": "integer",
2538
+ "minimum": 1,
2539
+ "description": "Host-enforced per-request wall-clock timeout (also applies to `safeFetch`)."
2540
+ },
2541
+ "methods": {
2542
+ "type": "array",
2543
+ "items": {
2544
+ "type": "string"
2545
+ },
2546
+ "description": "HTTP methods the client surface accepts (e.g. `GET`, `POST`)."
2547
+ },
2548
+ "safeFetch": {
2549
+ "type": "object",
2550
+ "description": "RFC 0076 §B. Host-provided `ctx.http.safeFetch(url, init?)` for pack runtime code — the pack-facing exposure of the SSRF-guarded client. When advertised, the host MUST apply the §host.http SSRF defense + clamps (incl. refusing `Connection: upgrade`), and — when `toolHooks.prePostEvents` is also advertised — MUST emit the `agent.toolCalled`/`agent.toolReturned` pair (`transport: 'http'`) for each call. A pack that uses `safeFetch` need not declare `net.dns` in `runtime.requires` (the host owns resolution; RFC 0076 §A).",
2551
+ "required": [
2552
+ "supported"
2553
+ ],
2554
+ "additionalProperties": false,
2555
+ "properties": {
2556
+ "supported": {
2557
+ "type": "boolean"
2558
+ }
2559
+ }
2560
+ },
2561
+ "egressPolicy": {
2562
+ "type": "object",
2563
+ "additionalProperties": false,
2564
+ "description": "RFC 0079. The host evaluates credential provenance (`credential-provenance.schema.json`) + the audience-binding MUST (§C) on credentialed egress and emits `egress.decided` (§B). Requires `httpClient.safeFetch` (the egress mechanism). Absent ⇒ the host does not perform provenance binding (the RFC 0076 §B SSRF guard still applies); the conformance behavioral scenarios skip cleanly. Closes the credential↔destination-binding question RFC 0076 §B parked.",
2565
+ "required": [
2566
+ "supported"
2567
+ ],
2568
+ "properties": {
2569
+ "supported": {
2570
+ "type": "boolean",
2571
+ "description": "REQUIRED when present. `true` ⇒ the §C audience-binding MUST is enforced + `egress.decided` is emitted."
2572
+ },
2573
+ "decisions": {
2574
+ "type": "array",
2575
+ "uniqueItems": true,
2576
+ "items": {
2577
+ "type": "string",
2578
+ "enum": [
2579
+ "allowed",
2580
+ "denied",
2581
+ "downgraded",
2582
+ "approval-required"
2583
+ ]
2584
+ },
2585
+ "description": "MAY — which decision outcomes the host implements. Absent ⇒ at least `allowed` + `denied`."
2586
+ }
2587
+ }
2588
+ }
2589
+ }
2590
+ },
2591
+ "artifactTypes": {
2592
+ "type": "object",
2593
+ "description": "RFC 0071, amended by RFC 0075; declared by RFC 0144. Host artifact-type surface — an advertisement (no `ctx.artifactTypes.*` method) that changes how the host treats the artifact references already on the wire (`nodes[].artifact.typeId`, `WorkflowNode.artifactType`, `artifact.created.artifactType`). Carries the corpus's only emission MUST: a host advertising `store: true` MUST persist registered artifacts and emit `artifact.created`. See `host-capabilities.md` §host.artifactTypes and `artifact-type-packs.md` §\"Host capability\". The discovery key is the PLAIN family name at the document root (RFC 0137 G16); `host.artifactTypes` is the capability IDENTIFIER used in pack `peerDependencies`, not this key.",
2594
+ "additionalProperties": false,
2595
+ "properties": {
2596
+ "supported": {
2597
+ "type": "boolean",
2598
+ "description": "Host honors registered artifact types — validates an artifact whose type is registered (pack-installed or host-native) against that type's schema before emitting `artifact.created`, setting `registered: true` + `registrationSource`. Unregistered types stay accepted unvalidated with `registered: false`."
2599
+ },
2600
+ "store": {
2601
+ "type": "boolean",
2602
+ "description": "`true` ⇒ the host persists artifacts of registered types AND emits `artifact.created`. Advertising this is a commitment to emit; RFC 0142 leg B is the witness."
2603
+ },
2604
+ "render": {
2605
+ "type": "boolean",
2606
+ "description": "Advisory — the spec defines no rendering surface. A host advertising `false` for a type it can `store` MUST still accept and store the artifact and MUST NOT fail the run for lack of a renderer (the store-without-render negotiation guarantee)."
2607
+ },
2608
+ "export": {
2609
+ "type": "array",
2610
+ "items": {
2611
+ "type": "string"
2612
+ },
2613
+ "description": "Export-format identifiers the host can materialize, drawn from declared `exportFormats`. Advisory."
2614
+ },
2615
+ "types": {
2616
+ "type": "object",
2617
+ "description": "RFC 0075 per-type facets, keyed by `artifactTypeId`. Each entry overrides the global object for that type; the global object is the fallback for any type not listed (absent ⇒ host-global semantics; any facet absent ⇒ the global default — additive).",
2618
+ "additionalProperties": {
2619
+ "type": "object",
2620
+ "additionalProperties": false,
2621
+ "properties": {
2622
+ "validated": {
2623
+ "type": "boolean",
2624
+ "description": "The runtime validation guarantee — `true` ⇒ the host validates this type before emit, so emits `registered: true`. Decoupled from `schemaVersions`, which is a version DECLARATION only."
2625
+ },
2626
+ "validation": {
2627
+ "type": "string",
2628
+ "enum": [
2629
+ "open",
2630
+ "closed"
2631
+ ],
2632
+ "description": "Mirrors `ArtifactType.validation`, surfacing schema strictness in discovery so a consumer needn't fetch the schema. Default `\"open\"` per `COMPATIBILITY.md` §2.1."
2633
+ },
2634
+ "schemaVersion": {
2635
+ "type": "integer",
2636
+ "minimum": 0
2637
+ },
2638
+ "registrationSource": {
2639
+ "type": "string",
2640
+ "enum": [
2641
+ "pack",
2642
+ "host"
2643
+ ],
2644
+ "description": "RFC 0145. Provenance of this registered type: `pack` (an installed artifact-type pack backs it) or `host` (host-native, validated against a host-known schema with no pack). Mirrors `artifact.created.registrationSource` and MUST match what the host would emit for this type — the two surfaces MUST NOT disagree. OPTIONAL; absent ⇒ unspecified provenance, NOT a default of `pack`. Discloses which §\"Schema distribution\" regime applies: serving the canonical schema URL is a MUST for host-registered (no-pack) types and only a SHOULD for pack-backed ones, and nothing else in the advert reveals which. A host MUST NOT infer this from the identifier's shape — `vendor.*` is a registry namespace, not an installation claim."
2645
+ },
2646
+ "store": {
2647
+ "type": "boolean"
2648
+ },
2649
+ "render": {
2650
+ "type": "boolean"
2651
+ },
2652
+ "export": {
2653
+ "type": "array",
2654
+ "items": {
2655
+ "type": "string"
2656
+ }
2657
+ }
2658
+ }
2659
+ }
2660
+ }
2661
+ }
2662
+ },
2663
+ "forms": {
2664
+ "type": "object",
2665
+ "description": "RFC 0137; declared by RFC 0144. Host form-content-pack surface — like `artifactTypes` it adds no `ctx.forms.*` method; it advertises that the host resolves registered form templates from installed `kind: \"form-content\"` packs and instantiates them through its own normal create path. See `host-capabilities.md` §host.forms and `form-content-packs.md`. The discovery key is the PLAIN family name at the document root (RFC 0137 G16); a host may additionally carry a deprecated dotted `\"host.forms\"` mirror during migration — root `additionalProperties` stays `true` so that document remains valid.",
2666
+ "additionalProperties": false,
2667
+ "properties": {
2668
+ "contentPacks": {
2669
+ "type": "boolean",
2670
+ "description": "`true` ⇒ the host instantiates registered form templates. Normative consequences (`form-content-packs.md` §\"Instantiation\"): create through the SAME path a hand-authored form uses; degrade an unrecognized field type to a plain text input rather than failing; keep the instantiated form fully editable; execute nothing from the pack; sanitize/validate/authorize submissions exactly as hand-typed input. Pack-authored strings are UNTRUSTED and MUST propagate `meta.contentTrust: \"untrusted\"` into interpolated prompt segments (invariant `form-content-pack-string-trust-boundary`) — a signature proves authorship, not content safety."
2671
+ }
2672
+ }
2673
+ },
2674
+ "aiEnvelope": {
2675
+ "type": "object",
2676
+ "description": "Declared by RFC 0144. Host typed-envelope generation surface (`ctx.aiEnvelope.generate`), routing the call through the host's BYOK provider layer. Opinionated about envelope shape, unlike the lower-level `aiProviders` capability which returns raw model output. See `host-capabilities.md` §host.aiEnvelope and `ai-envelope.md`.",
2677
+ "additionalProperties": false,
2678
+ "properties": {
2679
+ "supported": {
2680
+ "type": "boolean",
2681
+ "description": "`true` ⇒ `ctx.aiEnvelope.generate` is exposed. Absent/`false` ⇒ packs binding this surface fail with `host_capability_missing`."
2682
+ },
2683
+ "await": {
2684
+ "type": "boolean",
2685
+ "description": "`true` ⇒ `ctx.aiEnvelope.await` is ALSO exposed. `generate` is required whenever the family is advertised; `await` is required only when this sub-flag is."
2686
+ }
2687
+ }
2688
+ },
2689
+ "promptLibrary": {
2690
+ "type": "object",
2691
+ "description": "Declared by RFC 0144. Host prompt-lookup surface (`ctx.promptLibrary.get`), returning a prompt pinned to a specific version so replay stays deterministic. Consumed by `core.ai.callPrompt`. See `host-capabilities.md` §host.promptLibrary.",
2692
+ "additionalProperties": false,
2693
+ "properties": {
2694
+ "supported": {
2695
+ "type": "boolean",
2696
+ "description": "`true` ⇒ `ctx.promptLibrary.get(promptId)` resolves a `{ promptId, systemPrompt, version, envelopeType? }` record. The returned `version` MUST pin the prompt text for replay."
2697
+ }
2698
+ }
2699
+ },
2700
+ "agentRuntime": {
2701
+ "type": "object",
2702
+ "description": "Declared by RFC 0144. The heavyweight swarm/consensus SUPERSET of the agent surface — spawn, delegate, consensus, message-send, skill-invoke, swarm-execute over RFC 0002/0003/0007 primitives. Advertising it IMPLIES `agents.manifestRuntime` (RFC 0070 §B), since `spawn({ manifestId })` instantiates a manifest agent; hosts needing only single-agent or crew dispatch advertise that floor and omit this family. Per-tool authorization, rate limiting, and the content-free tool-call audit trail live in the top-level `toolHooks` block, NOT under this family. See `host-capabilities.md` §host.agentRuntime.",
2703
+ "additionalProperties": false,
2704
+ "properties": {
2705
+ "supported": {
2706
+ "type": "boolean",
2707
+ "description": "`true` ⇒ `ctx.agentRuntime.*` is exposed AND `agents.manifestRuntime` is implied (RFC 0070 §B). A multi-tenant host additionally advertises `agents.manifestRuntime.installScope: 'tenant'` (RFC 0074) so `GET /v1/agents` scopes to the caller's owner triple."
2708
+ }
2709
+ }
2710
+ },
2711
+ "deadLetter": {
2712
+ "type": "object",
2713
+ "description": "RFC 0053 (`Draft`). Run-level dead-letter sink for terminally-failed runs/nodes. On retry exhaustion (RFC 0009), the run is routed to a durable, inspectable sink and a `run.dead_lettered` event is emitted; dead-lettered runs remain fork-eligible (RFC 0011) for the retention window. Distinct from `queueBus.deadLetterSupported`, which dead-letters transport *messages*, not *runs*.",
2714
+ "required": [
2715
+ "supported"
2716
+ ],
2717
+ "properties": {
2718
+ "supported": {
2719
+ "type": "boolean"
2720
+ },
2721
+ "retentionDays": {
2722
+ "type": "integer",
2723
+ "minimum": 1,
2724
+ "description": "Days a dead-lettered run is retained for inspection/fork before purge."
2725
+ }
2726
+ },
2727
+ "additionalProperties": false
2728
+ },
2729
+ "webhooks": {
2730
+ "type": "object",
2731
+ "description": "Webhook delivery surface (`webhooks.md`). The base contract is best-effort (5s per-attempt timeout, a circuit breaker, no durable retry). RFC 0083 adds the OPTIONAL `durable` mode: when `true`, webhook delivery participates in the trigger-bridge durable model (subscription states + retry policy + dead-letter on exhaustion) instead of the best-effort circuit-breaker-then-drop. Absent `durable` ⇒ the best-effort default, explicitly unchanged.",
2732
+ "additionalProperties": true,
2733
+ "properties": {
2734
+ "supported": {
2735
+ "type": "boolean",
2736
+ "description": "Host serves the `webhooks.md` signed-delivery surface."
2737
+ },
2738
+ "signatureAlgorithms": {
2739
+ "type": "array",
2740
+ "items": {
2741
+ "type": "string"
2742
+ },
2743
+ "uniqueItems": true,
2744
+ "description": "HMAC signature algorithm ids the host supports (e.g. `v1`)."
2745
+ },
2746
+ "durable": {
2747
+ "type": "boolean",
2748
+ "description": "RFC 0083 §A — OPTIONAL opt-in. `true` ⇒ webhook delivery is durable (a trigger-bridge source); absent/`false` ⇒ the best-effort `webhooks.md` contract, unchanged. The best-effort default is NOT relaxed."
2749
+ }
2750
+ }
2751
+ },
2752
+ "triggerBridge": {
2753
+ "type": "object",
2754
+ "description": "RFC 0083 (`Active`). Composes the existing scheduling (RFC 0052), dead-letter (RFC 0053), queue-bus (RFC 0017), webhook, and cross-host-causation (RFC 0040) primitives into one uniform durable inbound-work contract: standardized subscription states + a delivery-attempt/dedup/retry model + trigger→run causation. Backs the derived `openwop-trigger-bridge` profile. Channels (Slack/email/SMS) stay vendor extensions (§E) — only their bridge into a run is uniform. Hosts that omit it have no uniform trigger contract (today's behavior); the conformance behavioral scenarios skip cleanly.",
2755
+ "required": [
2756
+ "supported"
2757
+ ],
2758
+ "additionalProperties": false,
2759
+ "properties": {
2760
+ "supported": {
2761
+ "type": "boolean",
2762
+ "description": "REQUIRED when present. `true` ⇒ the host implements the §B state machine + §C delivery model + emits the two `trigger.*` events."
2763
+ },
2764
+ "subscriptionStates": {
2765
+ "type": "array",
2766
+ "uniqueItems": true,
2767
+ "items": {
2768
+ "type": "string",
2769
+ "enum": [
2770
+ "active",
2771
+ "paused",
2772
+ "failed",
2773
+ "dead-lettered"
2774
+ ]
2775
+ },
2776
+ "description": "The subscription states the host implements (the §B four-state vocabulary). Absent ⇒ at least `active` + `dead-lettered`."
2777
+ },
2778
+ "dedup": {
2779
+ "type": "boolean",
2780
+ "description": "`true` ⇒ the host de-duplicates inbound events by `dedupKey` within the retention window (§C-1; at-least-once becomes effectively-once)."
2781
+ },
2782
+ "retryPolicy": {
2783
+ "type": "object",
2784
+ "additionalProperties": false,
2785
+ "description": "The host's default delivery retry policy (§C-2).",
2786
+ "properties": {
2787
+ "maxAttempts": {
2788
+ "type": "integer",
2789
+ "minimum": 1,
2790
+ "description": "Max delivery attempts before dead-lettering."
2791
+ },
2792
+ "backoff": {
2793
+ "type": "string",
2794
+ "enum": [
2795
+ "none",
2796
+ "fixed",
2797
+ "exponential"
2798
+ ],
2799
+ "description": "Backoff strategy between attempts."
2800
+ }
2801
+ }
2802
+ },
2803
+ "sources": {
2804
+ "type": "array",
2805
+ "uniqueItems": true,
2806
+ "items": {
2807
+ "type": "string",
2808
+ "enum": [
2809
+ "webhook",
2810
+ "schedule",
2811
+ "queue",
2812
+ "email",
2813
+ "form",
2814
+ "stream",
2815
+ "change"
2816
+ ]
2817
+ },
2818
+ "description": "Which trigger sources bridge uniformly. A source listed here MUST have a registerable `TriggerSubscription` driven through the four-state machine AND emit the two `trigger.*` events for that source — the list MUST NOT over-claim a source the host has as a feature but does not wire as a durable trigger subscription. A consumer MUST tolerate any subset."
2819
+ },
2820
+ "ingestion": {
2821
+ "type": "object",
2822
+ "additionalProperties": false,
2823
+ "description": "RFC 0099 §F.3 (additive). External-event ingestion advertisement — which of `sources[]` the host actually ingests from EXTERNALLY-originated events (`webhook`/`email`/`form`/`stream`/`change`), normalizing each to a `TriggerEvent` (`trigger-event.schema.json`) and starting a run. Absent ⇒ the host does NOT externally-ingest (today's behavior — schedule/queue only). A source in `externalSources[]` MUST actually accept an external event, normalize it, and start a run — over-claiming is a dishonest advertisement. A consumer MUST tolerate any subset.",
2824
+ "properties": {
2825
+ "externalSources": {
2826
+ "type": "array",
2827
+ "uniqueItems": true,
2828
+ "items": {
2829
+ "type": "string",
2830
+ "enum": [
2831
+ "webhook",
2832
+ "email",
2833
+ "form",
2834
+ "stream",
2835
+ "change"
2836
+ ]
2837
+ },
2838
+ "description": "Which of `sources[]` are EXTERNALLY ingested per RFC 0099 (`stream`/`change` per RFC 0127). The honesty gate — each MUST normalize to a `TriggerEvent` and start a run."
2839
+ },
2840
+ "maxBodyBytes": {
2841
+ "type": "integer",
2842
+ "minimum": 1,
2843
+ "description": "Inbound body cap (webhook body / email / form), reusing the RFC 0076 §B response-cap discipline."
2844
+ },
2845
+ "verification": {
2846
+ "type": "array",
2847
+ "uniqueItems": true,
2848
+ "items": {
2849
+ "type": "string",
2850
+ "enum": [
2851
+ "webhook-signature",
2852
+ "email-dmarc",
2853
+ "form-origin"
2854
+ ]
2855
+ },
2856
+ "description": "Which source-authenticity checks the host performs. An advertised check MUST actually be performed (RFC 0099 §F.3 / UQ1)."
2857
+ },
2858
+ "registrationEndpoint": {
2859
+ "type": "boolean",
2860
+ "description": "`true` ⇒ the host serves `POST /v1/trigger-subscriptions` (RFC 0099 §F.2) for portable external-event subscription creation."
2861
+ }
2862
+ }
2863
+ }
2864
+ }
2865
+ },
2866
+ "a2a": {
2867
+ "type": "object",
2868
+ "additionalProperties": false,
2869
+ "description": "RFC 0100 (`Active`). The host exposes itself as an A2A (Agent2Agent) agent. `supported: true` alone ⇒ the SYNCHRONOUS `message/send` → poll `tasks/get` round-trip already specified by `a2a-integration.md` (today's behavior — no regression). The optional `streaming`/`pushNotifications`/`durableTasks` flags gate the RFC 0100 async/durable additions (resubscribe re-attach, push config, persisted `A2ATaskState` so `tasks/get` returns live state after disconnect). Absent block ⇒ no A2A advertisement.",
2870
+ "required": [
2871
+ "supported",
2872
+ "agentCardUrl"
2873
+ ],
2874
+ "properties": {
2875
+ "supported": {
2876
+ "type": "boolean",
2877
+ "description": "Host exposes itself as an A2A agent."
2878
+ },
2879
+ "agentCardUrl": {
2880
+ "type": "string",
2881
+ "format": "uri",
2882
+ "description": "The A2A well-known agent card URL (`/.well-known/agent-card.json` — the same path in 0.3 and 1.0; the protocol version is discovered INSIDE the 1.0 card via `supportedInterfaces[].protocolVersion`, RFC 0152 §C)."
2883
+ },
2884
+ "streaming": {
2885
+ "type": "boolean",
2886
+ "description": "Host supports `message/stream` + `tasks/resubscribe` (0.3 names) / `SendStreamingMessage` + `SubscribeToTask` (1.0 names) — A2A `capabilities.streaming`. Gates the RFC 0100 §3 resubscribe re-attach; MUST equal the Agent Card's `capabilities.streaming` (RFC 0152 §C)."
2887
+ },
2888
+ "pushNotifications": {
2889
+ "type": "boolean",
2890
+ "description": "Host supports A2A push-notification config (A2A `capabilities.push_notifications`). Gates the RFC 0100 §4 push contract; a caller-supplied `pushConfig.url` is SSRF-validated (`a2a-push-egress-ssrf`)."
2891
+ },
2892
+ "durableTasks": {
2893
+ "type": "boolean",
2894
+ "description": "RFC 0100 §2. Host PERSISTS the projected Task (`A2ATaskState`) per backing run; `tasks/get` returns live state after disconnect. Absent/false ⇒ synchronous round-trip only."
2895
+ },
2896
+ "protocolVersions": {
2897
+ "type": "array",
2898
+ "minItems": 1,
2899
+ "uniqueItems": true,
2900
+ "items": {
2901
+ "type": "string",
2902
+ "pattern": "^[0-9]+\\.[0-9]+$"
2903
+ },
2904
+ "description": "RFC 0152 §A — every A2A protocol version this host speaks, newest-preferred first by convention. An A2A-capable host MUST advertise a non-empty array. `supported: true` WITHOUT versions is deprecated and CANNOT substantiate a current-A2A claim: it says the host speaks some A2A, which is not a fact a peer can negotiate against."
2905
+ },
2906
+ "preferredVersion": {
2907
+ "type": "string",
2908
+ "pattern": "^[0-9]+\\.[0-9]+$",
2909
+ "description": "RFC 0152 §A — MUST be present in `protocolVersions`. New hosts SHOULD prefer `1.0`."
2910
+ },
2911
+ "profiles": {
2912
+ "type": "array",
2913
+ "uniqueItems": true,
2914
+ "items": {
2915
+ "type": "string",
2916
+ "pattern": "^a2a-[0-9]+\\.[0-9]+(-legacy)?$"
2917
+ },
2918
+ "description": "RFC 0152 §A — named composition profiles, e.g. `a2a-1.0`, `a2a-0.3-legacy`. A legacy-only host advertises only the `-legacy` profile, which makes the deprecation explicit and time-boundable rather than implicit in a bare `supported: true`."
2919
+ }
2920
+ }
2921
+ },
2922
+ "budget": {
2923
+ "type": "object",
2924
+ "description": "RFC 0084 (`Active`). Enforceable per-run SPEND governance — the reserved `budget` run-options key (`budget-policy.schema.json`), the content-free `budget.{reserved,consumed,threshold.crossed,exhausted}` events, and hard-stop enforcement via `cap.breached{kind:\"budget-*\"}`. Orthogonal to RFC 0058 (which owns wall-time + loop-iterations via `limits.maxRunDurationMs`/`maxLoopIterations`); they share only the `cap.breached` overflow event. Hosts that omit it perform no spend enforcement (today's behavior); the conformance behavioral scenarios skip cleanly.",
2925
+ "required": [
2926
+ "supported"
2927
+ ],
2928
+ "additionalProperties": false,
2929
+ "properties": {
2930
+ "supported": {
2931
+ "type": "boolean",
2932
+ "description": "REQUIRED when present. `true` ⇒ the host resolves the `budget` policy, emits the `budget.*` events, and (when `enforce: \"hard\"`) stops the run on exhaustion."
2933
+ },
2934
+ "dimensions": {
2935
+ "type": "array",
2936
+ "uniqueItems": true,
2937
+ "items": {
2938
+ "type": "string",
2939
+ "enum": [
2940
+ "tokens",
2941
+ "cost",
2942
+ "toolCalls",
2943
+ "retries",
2944
+ "model"
2945
+ ]
2946
+ },
2947
+ "description": "Which budget dimensions the host actually enforces (truthful — advertise only what it honors). A consumer MUST tolerate any subset."
2948
+ },
2949
+ "enforce": {
2950
+ "type": "string",
2951
+ "enum": [
2952
+ "hard",
2953
+ "advisory"
2954
+ ],
2955
+ "description": "`hard`: exhaustion emits `cap.breached` + stops the run. `advisory`: emits the events but MUST NOT stop the run (honest advertisement of observe-only)."
2956
+ },
2957
+ "scopes": {
2958
+ "type": "array",
2959
+ "uniqueItems": true,
2960
+ "items": {
2961
+ "type": "string",
2962
+ "enum": [
2963
+ "run",
2964
+ "workflow",
2965
+ "agent",
2966
+ "project"
2967
+ ]
2968
+ },
2969
+ "description": "Which budget scopes the host honors (§B). Absent ⇒ at least `run`."
2970
+ }
2971
+ }
2972
+ },
2973
+ "nondeterminismPolicy": {
2974
+ "type": "object",
2975
+ "description": "RFC 0085. A host that does NOT support replay/fork (`replay.supported`) MAY instead DECLARE that it is honestly nondeterministic — satisfying the `openwop-agent-platform` floor's replay-OR-policy term (`agent-platform-profile.md` §B) without claiming a replay capability it lacks. Absent ⇒ the floor's replay term must be met by `replay.supported`.",
2976
+ "required": [
2977
+ "declared"
2978
+ ],
2979
+ "additionalProperties": false,
2980
+ "properties": {
2981
+ "declared": {
2982
+ "type": "boolean",
2983
+ "description": "When `true`, the host documents its nondeterminism (it does not guarantee deterministic replay). A bare flag for v1.x (RFC 0085 §UQ2); a structured per-source policy is a future refinement."
2984
+ }
2985
+ }
2986
+ },
2987
+ "workspace": {
2988
+ "type": "object",
2989
+ "description": "RFC 0059 (`Active`). Versioned, tenant·workspace-scoped ground-truth file store (the `host.workspace` capability). Scopes to the RFC 0048 owner triple. Atomic, optimistically-concurrent writes (`If-Match` ETag); a read snapshot is exposed to every run at `run.started` (deterministic for replay). Complements the transactional `MemoryAdapter` (RFC 0004) with a durable, path-addressable file layer. Endpoints (`/v1/host/workspace/files[/{path}]`) are gated on `supported: true`; unsupported hosts return `501 capability_not_provided`. SECURITY invariants `workspace-cross-tenant-isolation` (WCT-1) + the WSR-1 secret-redaction MUST land with their conformance tests at implementation (RFC 0059 §E).",
2990
+ "required": [
2991
+ "supported"
2992
+ ],
2993
+ "properties": {
2994
+ "supported": {
2995
+ "type": "boolean",
2996
+ "description": "Host implements the RFC 0059 workspace file store + endpoints + `workspace.updated` event."
2997
+ },
2998
+ "versioned": {
2999
+ "type": "boolean",
3000
+ "description": "Each write bumps a monotonic `version`; prior versions are retrievable via `GET …/files/{path}?version=N`. Latest-version retrieval is the MUST regardless; history is best-effort up to `maxVersions`."
3001
+ },
3002
+ "maxFileBytes": {
3003
+ "type": "integer",
3004
+ "minimum": 1,
3005
+ "description": "Per-file byte ceiling; writes beyond it return `workspace_too_large`."
3006
+ },
3007
+ "maxFiles": {
3008
+ "type": "integer",
3009
+ "minimum": 1,
3010
+ "description": "Per-workspace file-count ceiling."
3011
+ },
3012
+ "maxVersions": {
3013
+ "type": "integer",
3014
+ "minimum": 1,
3015
+ "description": "When `versioned: true`, the number of historical versions a host advertises it will retain (history best-effort beyond the mandatory latest)."
3016
+ }
3017
+ },
3018
+ "additionalProperties": false
3019
+ },
3020
+ "uiPlugins": {
3021
+ "type": "object",
3022
+ "description": "RFC 0117 (`Active`; amended by RFC 0119). Host loads SIGNED, SANDBOXED front-end plugin packs (`kind: \"frontend-plugin\"`) — canvas editors, custom artifact viewers, settings panels — in an ORIGIN/EXECUTION-ISOLATED sandbox (mechanism named by `isolation`: cross-origin iframe by default, or wasm/process/container/vm) and talks to them over the closed `ui-plugin/1` host-RPC boundary. The wire owns the boundary (isolation + RPC allowlist + manifest), NOT a renderer. Gated on `supported: true`; a host that does not advertise it rejects `kind: \"frontend-plugin\"` packs at registration and renders no plugin surface (graceful degradation to RFC 0071 host rendering). SECURITY invariants `frontend-plugin-isolation` / `frontend-plugin-egress` / `frontend-plugin-rpc-allowlist` / `frontend-plugin-no-byok` (RFC 0117 §Security).",
3023
+ "required": [
3024
+ "supported"
3025
+ ],
3026
+ "properties": {
3027
+ "supported": {
3028
+ "type": "boolean",
3029
+ "description": "Host loads `kind: \"frontend-plugin\"` packs in an origin/execution-isolated sandbox (mechanism per `isolation`) and serves the `ui-plugin/1` host-RPC boundary."
3030
+ },
3031
+ "isolation": {
3032
+ "type": "string",
3033
+ "anyOf": [
3034
+ {
3035
+ "enum": [
3036
+ "cross-origin-iframe",
3037
+ "wasm",
3038
+ "process",
3039
+ "container",
3040
+ "vm"
3041
+ ]
3042
+ },
3043
+ {
3044
+ "pattern": "^x-host-[a-z0-9-]+-[a-z0-9-]+$"
3045
+ }
3046
+ ],
3047
+ "default": "cross-origin-iframe",
3048
+ "description": "RFC 0117 (amended by RFC 0119). The categorical isolation model the host enforces for plugin bytes. `cross-origin-iframe` = a distinct-origin sandboxed browser frame (the browser default; §Isolation). `wasm`/`process`/`container`/`vm` mirror `sandbox.isolationModel` (RFC 0035) for non-browser hosts that enforce the SAME isolation property. Vendor-specific models advertise `^x-host-<host>-<key>$` per `host-extensions.md`. ALL values denote the SAME mandatory property (`frontend-plugin-isolation`): plugin bytes execute in a boundary with no access to the host's execution context / DOM / origin-storage / credentials, deny-egress by default, and all host interaction mediated by the closed `ui-plugin/1` RPC. The field names the MECHANISM, never relaxes the property. In-process / same-origin / module-federation loading is a protocol-tier MUST NOT regardless of the advertised value."
3049
+ },
3050
+ "surfaces": {
3051
+ "type": "array",
3052
+ "description": "Plugin surfaces this host renders. A pack's `uiPlugins[].surface` not in this set is installable-but-inert (§Degradation). `canvas-preview` (RFC 0130) mounts inside a host-owned canvas editor.",
3053
+ "items": {
3054
+ "type": "string",
3055
+ "enum": [
3056
+ "artifact-viewer",
3057
+ "route",
3058
+ "settings-panel",
3059
+ "canvas-preview"
3060
+ ]
3061
+ },
3062
+ "uniqueItems": true
3063
+ },
3064
+ "hostApi": {
3065
+ "type": "array",
3066
+ "description": "The `ui-plugin/1` host-RPC methods this host honors. A plugin call to a method not in this set (regardless of the plugin's declared `hostApi`) MUST be rejected with `method_not_allowed` (`frontend-plugin-rpc-allowlist`). A host advertising `artifact.write` MUST enforce the `version`-token optimistic concurrency (RFC 0117 §Concurrency).",
3067
+ "items": {
3068
+ "type": "string",
3069
+ "enum": [
3070
+ "artifact.read",
3071
+ "artifact.write",
3072
+ "host.toast",
3073
+ "host.navigate",
3074
+ "host.announce"
3075
+ ]
3076
+ },
3077
+ "uniqueItems": true
3078
+ },
3079
+ "maxEntryBytes": {
3080
+ "type": "integer",
3081
+ "minimum": 1,
3082
+ "description": "Per-plugin entry-bundle byte ceiling the host will load."
3083
+ }
3084
+ },
3085
+ "additionalProperties": false
3086
+ },
3087
+ "sql": {
3088
+ "type": "object",
3089
+ "description": "RFC 0018 (`Active`). SQL database adapter with parametric-only enforcement. Hosts MUST reject non-parametric queries that inline user input (`sql-parametric-only` invariant — guards against SQL injection across every workflow).",
3090
+ "properties": {
3091
+ "supported": {
3092
+ "type": "boolean"
3093
+ },
3094
+ "datasources": {
3095
+ "type": "array",
3096
+ "items": {
3097
+ "type": "object",
3098
+ "additionalProperties": true
3099
+ }
3100
+ },
3101
+ "transactions": {
3102
+ "type": "boolean"
3103
+ },
3104
+ "drivers": {
3105
+ "type": "array",
3106
+ "items": {
3107
+ "type": "string",
3108
+ "enum": [
3109
+ "postgres",
3110
+ "mysql",
3111
+ "mariadb",
3112
+ "sqlite",
3113
+ "mssql",
3114
+ "clickhouse",
3115
+ "snowflake",
3116
+ "bigquery",
3117
+ "duckdb"
3118
+ ]
3119
+ }
3120
+ }
3121
+ },
3122
+ "additionalProperties": false
3123
+ },
3124
+ "nosql": {
3125
+ "type": "object",
3126
+ "description": "RFC 0018 (`Active`). MongoDB-shape document store adapter.",
3127
+ "properties": {
3128
+ "supported": {
3129
+ "type": "boolean"
3130
+ },
3131
+ "datasources": {
3132
+ "type": "array"
3133
+ },
3134
+ "drivers": {
3135
+ "type": "array",
3136
+ "items": {
3137
+ "type": "string",
3138
+ "enum": [
3139
+ "mongodb",
3140
+ "dynamodb",
3141
+ "cosmosdb",
3142
+ "firestore"
3143
+ ]
3144
+ }
3145
+ }
3146
+ },
3147
+ "additionalProperties": false
3148
+ },
3149
+ "vectorStore": {
3150
+ "type": "object",
3151
+ "description": "RFC 0018 (`Active`). Vector-DB capability for k-NN search.",
3152
+ "properties": {
3153
+ "supported": {
3154
+ "type": "boolean"
3155
+ },
3156
+ "collections": {
3157
+ "type": "array"
3158
+ },
3159
+ "backends": {
3160
+ "type": "array",
3161
+ "items": {
3162
+ "type": "string",
3163
+ "enum": [
3164
+ "pinecone",
3165
+ "qdrant",
3166
+ "weaviate",
3167
+ "milvus",
3168
+ "pgvector",
3169
+ "redis",
3170
+ "mongodb-atlas",
3171
+ "chroma",
3172
+ "azure-ai-search",
3173
+ "in-memory"
3174
+ ]
3175
+ }
3176
+ }
3177
+ },
3178
+ "additionalProperties": false
3179
+ },
3180
+ "searchIndex": {
3181
+ "type": "object",
3182
+ "description": "RFC 0018 (`Active`). Full-text search index adapter.",
3183
+ "properties": {
3184
+ "supported": {
3185
+ "type": "boolean"
3186
+ },
3187
+ "indexes": {
3188
+ "type": "array"
3189
+ },
3190
+ "backends": {
3191
+ "type": "array",
3192
+ "items": {
3193
+ "type": "string",
3194
+ "enum": [
3195
+ "elasticsearch",
3196
+ "opensearch",
3197
+ "meilisearch",
3198
+ "typesense",
3199
+ "algolia"
3200
+ ]
3201
+ }
3202
+ }
3203
+ },
3204
+ "additionalProperties": false
3205
+ },
3206
+ "blobStorage": {
3207
+ "type": "object",
3208
+ "description": "RFC 0019 (`Active`). Binary artifact store with presigned URLs. Per-bucket tenant isolation. Presigned URLs MUST expire at the advertised TTL.",
3209
+ "properties": {
3210
+ "supported": {
3211
+ "type": "boolean"
3212
+ },
3213
+ "buckets": {
3214
+ "type": "array"
3215
+ },
3216
+ "presignSupported": {
3217
+ "type": "boolean"
3218
+ },
3219
+ "maxObjectBytes": {
3220
+ "type": "integer",
3221
+ "minimum": 0
3222
+ }
3223
+ },
3224
+ "additionalProperties": false
3225
+ },
3226
+ "cache": {
3227
+ "type": "object",
3228
+ "description": "RFC 0019 (`Active`). TTL cache for HTTP / AI response memoization. Per-tenant scoping; TTL drift ≤ 1s.",
3229
+ "properties": {
3230
+ "supported": {
3231
+ "type": "boolean"
3232
+ },
3233
+ "maxValueBytes": {
3234
+ "type": "integer",
3235
+ "minimum": 0
3236
+ },
3237
+ "maxTtlSeconds": {
3238
+ "type": "integer",
3239
+ "minimum": 0
3240
+ }
3241
+ },
3242
+ "additionalProperties": false
3243
+ },
3244
+ "workflowChainPacks": {
3245
+ "type": "object",
3246
+ "description": "RFC 0013 (Phase 1, `Draft`). When `supported: true`, the host's workflow editor implements workflow-chain pack expansion per `workflow-chain-packs.md` — author drops a chain tile, host resolves the pack, prompts for `parameters`, substitutes `{{params.<name>}}` placeholders, rewrites node ids, splices the resulting DAG into the parent workflow. Hosts that don't implement expansion omit this block (or set `supported: false`); conformance scenarios under `conformance/src/scenarios/workflow-chain-*.test.ts` skip cleanly against those hosts.",
3247
+ "properties": {
3248
+ "supported": {
3249
+ "type": "boolean",
3250
+ "description": "Whether the host's workflow editor implements chain expansion at author time. `false` (or omission) signals the host does NOT consume workflow-chain packs."
3251
+ },
3252
+ "deferredParameters": {
3253
+ "type": "object",
3254
+ "description": "RFC 0124 (WCP4, `Active`). OPTIONAL. When `supported: true`, the host offers a capability-gated deferred-parameter expansion mode in ADDITION to expansion-time substitution (which remains the default and floor): at drop time it materializes the chain's `parameters` into top-level workflow `variables[]` (author value as `defaultValue`) and rewrites `{{params.<name>}}` into a spec'd runtime binding (PromptTemplate `{{varName}}` with `source:\"variable\"`, or a variable-sourced PortValue), so chain parameters stay overridable per run via `configurable` while the persisted definition keeps ZERO `{{params.*}}` tokens. A parameter marked `x-openwop-sensitive` MUST be deferred or the expansion fail-closed (`sensitive_param_not_deferrable`, 422) — never frozen into persisted `config`. Omission (or `supported:false`) signals the host does NOT offer deferred mode. No host may advertise `supported:true` until RFC 0124 is `Accepted`. See `workflow-chain-packs.md` §\"Deferred-parameter expansion\".",
3255
+ "properties": {
3256
+ "supported": {
3257
+ "type": "boolean",
3258
+ "description": "Whether the host offers the deferred-parameter expansion mode. Requires `capabilities.prompts.supported: true` with `variable` in `prompts.variableSources` for the prompt-bearing rewrite path."
3259
+ }
3260
+ },
3261
+ "required": [
3262
+ "supported"
3263
+ ],
3264
+ "additionalProperties": false
3265
+ },
3266
+ "hostExpansionSeam": {
3267
+ "type": "boolean",
3268
+ "description": "RFC 0013 erratum (2026-07-05). OPTIONAL. A **conformance-only test seam** advertisement (category: test harness, cf. `observability.testSeams` — NOT a product capability): when `true`, the host serves `POST /v1/host/sample/workflow-chain:expand` returning the `vendor.openwop.workflow-chain-sample` v1.0.0 expansion that `conformance/src/scenarios/workflow-chain-host-expansion.test.ts` asserts against. Absent/`false` ⇒ that live-host expansion scenario soft-skips; the semantic `workflowChainPacks.supported` claim is witnessed by the server-free `workflow-chain-expansion.test.ts` legs. This flag is DISTINCT from `deferredParameters` (RFC 0124), which is witnessed through its own `POST /v1/host/sample/chain/deferred-expand` seam — so a host MAY advertise `supported` / `deferredParameters.supported` without standing up the RFC 0013 sample-pack seam it was never handed a published fixture for. Advertising `supported:true` no longer conscripts a host into the RFC 0013 host-expansion scenario."
3269
+ },
3270
+ "subChains": {
3271
+ "type": "object",
3272
+ "description": "RFC 0133 (workflow-chain composition, `Accepted`). OPTIONAL. When `supported: true`, the host's `POST …/workflows/from-chain` implements RUNTIME sub-chain composition per `workflow-chain-packs.md` §\"Sub-chain composition (RFC 0133)\": for each `config.subChainRef` reachable from a parent chain, it resolves the referenced chain (sibling or external), recursively expands + co-registers it as its own owned workflow (deterministic TENANT-SCOPED id from `(tenantId, childChainId, version)` — two tenants never collide on the global registry, a shared child registers once across parents in a tenant, a repeat instantiation converges), rewrites the referencing node's `subChainRef` → the minted child `config.workflowId`, and dispatches the child as a child run (`core.subWorkflow` / `core.dispatch` child-run); the `from-chain` response carries `{ workflowId, subChainWorkflowIds[], nodeCount }`. Bounded by a cycle check (`sub_chain_cycle`) + a depth cap (`sub_chain_max_depth_exceeded`). A host that does NOT advertise this block MUST refuse a `subChains`-bearing chain at author/instantiate time with `sub_chain_unsupported` (422) — it never silently flattens. `producedVariables` (RFC 0133 §2) needs NO flag: it is pure variable emission under the base `workflowChainPacks.supported`. Conformance scenarios `chain-subchain-fanout.test.ts` + `chain-subchain-unsupported-refused.test.ts` gate on this flag; the server-free `chain-subchain-sibling` / `chain-subchain-cycle-rejected` / `chain-produced-var-roundtrip` legs run unconditionally against the reference library.",
3273
+ "properties": {
3274
+ "supported": {
3275
+ "type": "boolean",
3276
+ "description": "Whether the host implements runtime sub-chain co-registration + child dispatch. `false` (or omission of this block) signals the host does NOT compose sub-chains and MUST refuse a `subChains`-bearing chain with `sub_chain_unsupported`."
3277
+ },
3278
+ "maxDepth": {
3279
+ "type": "integer",
3280
+ "minimum": 1,
3281
+ "default": 8,
3282
+ "description": "RFC 0133 §1.3 (resolves UQ3). RECOMMENDED default 8. The maximum sub-chain nesting depth the host co-expands; exceeding it fails closed with the DISTINCT code `sub_chain_max_depth_exceeded` (the DoS depth backstop, alongside the `sub_chain_cycle` self-composition check — SECURITY `sub-chain-expansion-bounded`)."
3283
+ }
3284
+ },
3285
+ "required": [
3286
+ "supported"
3287
+ ],
3288
+ "additionalProperties": false
3289
+ }
3290
+ },
3291
+ "required": [
3292
+ "supported"
3293
+ ],
3294
+ "additionalProperties": false
3295
+ },
3296
+ "packs": {
3297
+ "type": "object",
3298
+ "description": "RFC 0025 (`Active`). Pack-registry surface advertisement. The baseline `/v1/packs/*` read surface (per `spec/v1/node-packs.md` §\"Registry HTTP API\") is unconditional for hosts that ship a pack catalog and does NOT require a capability flag; this object carries optional sub-blocks (currently the test-mode mirror namespace). Hosts that don't expose any optional pack-registry sub-block MAY omit this block entirely.",
3299
+ "properties": {
3300
+ "testMode": {
3301
+ "type": "object",
3302
+ "description": "RFC 0025 §A. Optional `/v1/packs-test/*` mirror surface that exposes the production publish/get/delete/sig contract against an isolated catalog. Lets the conformance suite (`pack-registry-publish.test.ts`) exercise the documented 19-code publish error catalog without `packs:publish` scope on the real registry. Hosts that advertise `supported: true` MUST honor the §C isolation guarantees and MUST surface the same error envelopes and HTTP statuses as the production `/v1/packs/*` surface.",
3303
+ "properties": {
3304
+ "supported": {
3305
+ "type": "boolean",
3306
+ "description": "Host exposes `/v1/packs-test/*` per RFC 0025 §B. When `true`, the conformance suite drives publish-error-catalog assertions through the test namespace; when `false` or absent, the 26 scenarios in `pack-registry-publish.test.ts` soft-skip cleanly."
3307
+ },
3308
+ "isolated": {
3309
+ "type": "boolean",
3310
+ "description": "RFC 0025 §C point 1. MUST be `true` when `supported` is `true` — guarantees the test catalog is persisted distinctly from the production catalog and that a pack PUT'd via `/v1/packs-test/*` MUST NOT appear in `/v1/packs/*` listings."
3311
+ },
3312
+ "catalogResetEndpoint": {
3313
+ "type": "string",
3314
+ "description": "RFC 0025 §C point 4. Optional URL path (e.g. `/v1/packs-test/reset`) that clears the entire test catalog. When advertised, conformance-suite teardown SHOULD call it; the endpoint MUST be idempotent. Hosts MAY omit; in that case the suite leaves disposable timestamped pack names in place and relies on the next host restart to clear in-memory state.",
3315
+ "pattern": "^/"
3316
+ },
3317
+ "scopes": {
3318
+ "type": "array",
3319
+ "items": {
3320
+ "type": "string",
3321
+ "enum": [
3322
+ "core",
3323
+ "vendor",
3324
+ "community",
3325
+ "private",
3326
+ "local"
3327
+ ]
3328
+ },
3329
+ "uniqueItems": true,
3330
+ "minItems": 1,
3331
+ "description": "RFC 0025 §A. Which namespace scopes the test catalog accepts in pack names. Public test catalogs SHOULD refuse `private` and `local` (matching the production-registry rule for `packs.openwop.dev`); private dev catalogs MAY accept all five. When omitted, the test catalog defaults to the same scope set as the production namespace it mirrors."
3332
+ }
3333
+ },
3334
+ "required": [
3335
+ "supported"
3336
+ ],
3337
+ "additionalProperties": false
3338
+ }
3339
+ },
3340
+ "additionalProperties": false
3341
+ },
3342
+ "mcp": {
3343
+ "type": "object",
3344
+ "description": "RFC 0020 (`Active`). MCP (Model Context Protocol) composition surface. The client half is consumed implicitly via `host.mcp` host-surface; this block adds the optional server half — workflow exposed AS an MCP server with bidirectional sampling/elicitation bridges.",
3345
+ "properties": {
3346
+ "supported": {
3347
+ "type": "boolean",
3348
+ "description": "Host advertises a client-side MCP surface (ctx.mcp.*). See spec/v1/mcp-integration.md."
3349
+ },
3350
+ "serverUrls": {
3351
+ "type": "array",
3352
+ "minItems": 1,
3353
+ "uniqueItems": true,
3354
+ "items": {
3355
+ "type": "string",
3356
+ "minLength": 1
3357
+ },
3358
+ "description": "Where the host's MCP server mount(s) answer JSON-RPC — an absolute URL or a path relative to the discovery origin (S25: a path is joined to the base URL, an absolute URL is used as-is). The v1.0 conformance baseline (`mcp-discoverability.test.ts`, spec/v1/mcp-integration.md §Conformance + interop) REQUIRES a non-empty `serverUrls` whenever `supported: true`, and since suite 1.134.0 every server-side scenario POSTs to `serverUrls[0]`. Required by the scenario and absent from this schema until 2026-08-17 (S27) — the second sibling host emitted it and measured the contradiction."
3359
+ },
3360
+ "serverMount": {
3361
+ "type": "object",
3362
+ "description": "Server-side MCP composition (workflow IS an MCP server). When supported, the host mounts an MCP endpoint and routes inbound tools/call, resources/read, prompts/get into workflows. Inbound MUST be treated as `trustBoundary: 'untrusted'`.",
3363
+ "properties": {
3364
+ "supported": {
3365
+ "type": "boolean"
3366
+ },
3367
+ "transports": {
3368
+ "type": "array",
3369
+ "items": {
3370
+ "type": "string",
3371
+ "enum": [
3372
+ "stdio",
3373
+ "streamable-http"
3374
+ ]
3375
+ }
3376
+ },
3377
+ "samplingBridge": {
3378
+ "type": "boolean",
3379
+ "description": "Inbound sampling/createMessage bridges to the workflow's ctx.callAI."
3380
+ },
3381
+ "elicitationBridge": {
3382
+ "type": "boolean",
3383
+ "description": "Inbound elicitation/create bridges to ctx.suspend."
3384
+ }
3385
+ },
3386
+ "additionalProperties": false
3387
+ },
3388
+ "protocolVersions": {
3389
+ "type": "array",
3390
+ "minItems": 1,
3391
+ "uniqueItems": true,
3392
+ "items": {
3393
+ "type": "string",
3394
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"
3395
+ },
3396
+ "description": "RFC 0153 §A — MCP revisions in MCP's own date form, exactly. The pattern is date-shaped rather than free string because MCP versions ARE dates: accepting `latest` or `2026-7-28` would make two hosts disagree about which revision they share while both validating."
3397
+ },
3398
+ "preferredVersion": {
3399
+ "type": "string",
3400
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$",
3401
+ "description": "RFC 0153 §A — MUST be present in `protocolVersions`."
3402
+ },
3403
+ "profiles": {
3404
+ "type": "array",
3405
+ "uniqueItems": true,
3406
+ "items": {
3407
+ "type": "string",
3408
+ "pattern": "^mcp-[0-9]{4}-[0-9]{2}-[0-9]{2}(-legacy)?$"
3409
+ },
3410
+ "description": "RFC 0153 §A — named composition profiles. Unqualified `supported: true` is deprecated and cannot substantiate a current-MCP claim."
3411
+ },
3412
+ "features": {
3413
+ "type": "array",
3414
+ "uniqueItems": true,
3415
+ "items": {
3416
+ "enum": [
3417
+ "server-discover",
3418
+ "mrtr",
3419
+ "cacheable-lists",
3420
+ "extensions"
3421
+ ]
3422
+ },
3423
+ "description": "RFC 0153 §A — closed feature set for the current revision. Closed rather than open because an unrecognized feature name is indistinguishable from a typo, and a peer that silently ignores one negotiates a capability neither side has."
3424
+ }
3425
+ },
3426
+ "additionalProperties": false
3427
+ },
3428
+ "sandbox": {
3429
+ "type": "object",
3430
+ "description": "RFC 0035 — Sandbox execution contract for pack-loaded typeIds. Hosts that advertise execute pack-loaded code inside an isolation boundary meeting the 8 failure-mode invariants in spec/v1/host-capabilities.md §'Sandbox execution contract'. Absent block = host does NOT sandbox pack-loaded code and MUST refuse to load any pack whose manifest declares `peerDependencies.host.sandbox: required`.",
3431
+ "additionalProperties": false,
3432
+ "required": [
3433
+ "supported",
3434
+ "isolationModel"
3435
+ ],
3436
+ "properties": {
3437
+ "supported": {
3438
+ "type": "boolean",
3439
+ "description": "Host enforces the 8 sandbox failure-mode invariants per spec/v1/host-capabilities.md §'Sandbox execution contract'. When false, host MUST refuse to load packs declaring required sandbox isolation."
3440
+ },
3441
+ "isolationModel": {
3442
+ "type": "string",
3443
+ "anyOf": [
3444
+ {
3445
+ "enum": [
3446
+ "wasm",
3447
+ "process",
3448
+ "container",
3449
+ "vm"
3450
+ ]
3451
+ },
3452
+ {
3453
+ "pattern": "^x-host-[a-z][a-z0-9-]*-[a-z][a-z0-9-]*$"
3454
+ }
3455
+ ],
3456
+ "description": "Categorical isolation model. `wasm` = WebAssembly sandbox with explicit host imports (e.g., Wasmtime, Wasmer). `process` = OS process boundary with restricted syscalls (e.g., gVisor, seccomp, Landlock). `container` = container runtime boundary (e.g., Firecracker microVM). `vm` = full VM. Vendor-specific isolation models advertise `^x-host-<host>-<key>$` per spec/v1/host-extensions.md §'Canonical prefixes'; documentation lives at the host's discovery doc."
3457
+ },
3458
+ "allowedHostCalls": {
3459
+ "type": "array",
3460
+ "items": {
3461
+ "type": "string"
3462
+ },
3463
+ "description": "Whitelist of host-call surfaces sandboxed code MAY invoke. Identifiers from the spec-reserved `host.*` capability set or `^x-host-<host>-<key>$` extension namespace. Empty array = pure compute only (no host I/O). Conformance verifies the sandbox refuses unlisted calls (`sandbox_capability_denied`)."
3464
+ },
3465
+ "memoryLimitBytes": {
3466
+ "type": "integer",
3467
+ "minimum": 1048576,
3468
+ "description": "Per-invocation memory cap (≥ 1 MiB). Host MUST enforce; exceeding fails the node with `sandbox_memory_exceeded`."
3469
+ },
3470
+ "wallClockLimitMs": {
3471
+ "type": "integer",
3472
+ "minimum": 100,
3473
+ "description": "Per-invocation wall-clock cap (≥ 100 ms). Host MUST enforce; exceeding fails the node with `sandbox_timeout`."
3474
+ }
3475
+ }
3476
+ },
3477
+ "compensation": {
3478
+ "type": "object",
3479
+ "description": "RFC 0151 §A — generic compensation (Saga) contract. OPTIONAL; absent means the host offers no generic compensation contract, NOT that it never compensates — a workflow can always model an inverse action as an ordinary node. What this advertises is that the HOST orders, persists, and retries the unwind, so a client can rely on it rather than hand-rolling one. Compensation is a second effect, not an undo: it can fail, can be partially applied, and can itself require approval, which is why the profile is security-tier high (RFC 0147 R9).",
3480
+ "additionalProperties": false,
3481
+ "required": [
3482
+ "supported"
3483
+ ],
3484
+ "properties": {
3485
+ "supported": {
3486
+ "type": "boolean"
3487
+ },
3488
+ "profileVersion": {
3489
+ "type": "string",
3490
+ "pattern": "^[1-9][0-9]*$",
3491
+ "description": "RFC 0151. Participates in the inverse-action identity, so a profile bump cannot silently collide with identities minted under the previous ordering rules."
3492
+ },
3493
+ "orderingModels": {
3494
+ "type": "array",
3495
+ "minItems": 1,
3496
+ "uniqueItems": true,
3497
+ "items": {
3498
+ "enum": [
3499
+ "reverse-completion",
3500
+ "dependency-graph"
3501
+ ]
3502
+ },
3503
+ "description": "RFC 0151 §A. A host that advertises compensation MUST implement `reverse-completion` and MAY additionally implement `dependency-graph`. `dependency-graph` MUST be a DAG and preserve reverse dependency order."
3504
+ },
3505
+ "manualIntervention": {
3506
+ "type": "boolean",
3507
+ "description": "RFC 0151 §C — the host can record `manual-intervention-required` rather than silently abandoning an unwind. Cancelling the parent MUST NOT abandon an active compensation: it continues, pauses for authorized intervention, or records this state."
3508
+ }
3509
+ }
3510
+ },
3511
+ "idempotency": {
3512
+ "type": "object",
3513
+ "description": "RFC 0036 — Multi-region idempotency contract. Optional v1 advertisement. The existing `crossRegion: 'single-region'|'reconciled-records'|'fenced-effects'` categorical claim lives under `capabilities.idempotency.crossRegion` (this description named the pre-RFC-0150-§D vocabulary `'best-effort'|'strict'` until 2026-08-18 — both values were RETIRED by §D and the sibling `crossRegion` enum has been correct throughout, so only this prose was wrong; a reader of it would have believed two non-values were advertisable) per spec/v1/idempotency.md §'Multi-region idempotency (annex)'. The `multiRegion` sub-block here gives a granular advertisement that hosts SHOULD pair with the categorical `crossRegion` claim.",
3514
+ "additionalProperties": true,
3515
+ "properties": {
3516
+ "supported": {
3517
+ "type": "boolean",
3518
+ "description": "Whether the host implements the idempotency contract. Used in `spec/v1/idempotency.md`'s own capability example and throughout the corpus, but UNDECLARED until 2026-08-13 — it validated only because this family carries `additionalProperties: true`, which meant a typo such as `suported` was accepted silently and a consumer reading it saw nothing advertised. Declaring it does not close the family; it makes the field a contract rather than a convention."
3519
+ },
3520
+ "multiRegion": {
3521
+ "type": "object",
3522
+ "additionalProperties": false,
3523
+ "required": [
3524
+ "supported"
3525
+ ],
3526
+ "properties": {
3527
+ "supported": {
3528
+ "type": "boolean",
3529
+ "description": "Host implements cross-region idempotency reconciliation per spec/v1/idempotency.md §'Multi-region reconciliation'. When `true`, an Idempotency-Key write succeeding in region A is read-visible in region B within `replicationLagBoundMs + safetyMargin`."
3530
+ },
3531
+ "replicationLagBoundMs": {
3532
+ "type": "integer",
3533
+ "minimum": 0,
3534
+ "maximum": 60000,
3535
+ "description": "Conservative upper bound on cross-region replication lag (≤ 60s ceiling chosen to keep operator advertisement honest; production cross-region deployments typically run < 10s). Conformance asserts read-visibility after the bound."
3536
+ },
3537
+ "partitionRecoveryStrategy": {
3538
+ "type": "string",
3539
+ "anyOf": [
3540
+ {
3541
+ "enum": [
3542
+ "lexicographic-min-run-id"
3543
+ ]
3544
+ },
3545
+ {
3546
+ "pattern": "^x-host-[a-z][a-z0-9-]*-[a-z][a-z0-9-]*$"
3547
+ }
3548
+ ],
3549
+ "description": "RFC 0150 §D — deterministic resolution rule for conflicting idempotency-key records after a partition heals. `lexicographic-min-run-id` is the rule spec/v1/idempotency.md §'Guarantees under partition' MUSTs: the lower runId wins, deterministic without coordination. Vendor strategies use `^x-host-<host>-<key>$` and MUST still satisfy the reproducible-survivor requirement. The time-ordered `last-writer-wins` / `first-writer-wins` values were REMOVED as a safety-fix: under a partition there is no shared clock, so both regions believe they wrote last, which cannot satisfy the annex MUST that re-running the same conflict input produces the same survivor — and both select a different survivor than the lex-min rule the same document requires. Resolving a RECORD never authorizes an EFFECT; see `crossRegion`."
3550
+ }
3551
+ }
3552
+ },
3553
+ "crossRegion": {
3554
+ "type": "string",
3555
+ "enum": [
3556
+ "single-region",
3557
+ "reconciled-records",
3558
+ "fenced-effects"
3559
+ ],
3560
+ "description": "RFC 0036, revised by RFC 0150 §D — categorical multi-region idempotency posture (the canonical conformance-checked surface per spec/v1/idempotency.md §'Multi-region idempotency (annex)'). The ladder is about EFFECTS, not replication latency. `single-region`: the host runs in one region and makes no cross-region claim (it MAY still implement the convergence resolver, demonstrable via the multi-region simulator seam). `reconciled-records`: cross-region reconciliation converges eventually under the annex's lex-min(runId) rule, and external effects MAY remain at-least-once — converging on which RECORD survives says nothing about how many times an effect was issued. `fenced-effects`: records converge AND every external effect is either fenced by a monotonic token from a linearizable ownership service or issued to a provider that guarantees duplicate suppression. When `reconciled-records` or `fenced-effects`, the host MUST emit the `openwop.idempotency.cross_region_conflicts_total` operator metric. The `multiRegion` sub-block above is the optional granular companion. REMOVED as a safety-fix: `best-effort` (renamed to `reconciled-records`, which states the effect caveat the old name hid) and `strict`, which promised only that read-visibility was bounded by `multiRegion.replicationLagBoundMs` — a LATENCY claim occupying the top slot of a ladder implementers read as effect safety. A host replicating synchronously at 0 ms can still issue duplicate effects from two regions, because knowing what the other region wrote is not being authorized to act. That latency bound already has its own field, so no information is lost."
3561
+ }
3562
+ }
3563
+ },
3564
+ "eventLog": {
3565
+ "type": "object",
3566
+ "description": "RFC 0036 — Event-log multi-engine advertisement. Optional v1.",
3567
+ "additionalProperties": false,
3568
+ "properties": {
3569
+ "crossEngineOrdering": {
3570
+ "type": "object",
3571
+ "additionalProperties": false,
3572
+ "required": [
3573
+ "supported"
3574
+ ],
3575
+ "properties": {
3576
+ "supported": {
3577
+ "type": "boolean",
3578
+ "description": "Host implements append-ordering guarantees across multiple engine instances writing to the same run's event log. When `true`, two engines appending concurrently converge on a total order any reader observes consistently."
3579
+ },
3580
+ "orderingModel": {
3581
+ "type": "string",
3582
+ "enum": [
3583
+ "lamport",
3584
+ "vector-clock",
3585
+ "global-sequencer"
3586
+ ],
3587
+ "description": "Mechanism the host uses to derive the total order. `lamport` = Lamport timestamps on each append. `vector-clock` = per-engine vector counters merged at read. `global-sequencer` = single sequencer assigns monotonic seq numbers (the Postgres reference host's posture)."
3588
+ }
3589
+ }
3590
+ }
3591
+ }
3592
+ },
3593
+ "compliance": {
3594
+ "type": "object",
3595
+ "description": "Privacy / compliance behavior advertised to clients (closes O5). Lets consumers know what masking mode is in effect so a `\"[REDACTED]\"` value in event log payloads is recognizable as a server-side mask vs an upstream null.",
3596
+ "properties": {
3597
+ "defaultMode": {
3598
+ "type": "string",
3599
+ "enum": [
3600
+ "mask",
3601
+ "omit",
3602
+ "hash",
3603
+ "passthrough"
3604
+ ],
3605
+ "description": "Server's default masking mode for fields marked sensitive. `mask` (default): replace with `\"[REDACTED]\"`. `omit`: drop the field entirely. `hash`: replace with `\"sha256:<hex>\"` for audit-only equality. `passthrough`: record as-is (NOT recommended for production). Workflow authors MAY override per-workflow via `metadata.complianceConfig.maskingMode`. See observability.md §Privacy classification."
3606
+ },
3607
+ "supportedClasses": {
3608
+ "type": "array",
3609
+ "items": {
3610
+ "type": "string",
3611
+ "enum": [
3612
+ "public",
3613
+ "pii",
3614
+ "phi",
3615
+ "pci",
3616
+ "regulated"
3617
+ ]
3618
+ },
3619
+ "uniqueItems": true,
3620
+ "description": "Compliance classes this server applies special handling for. A server MAY accept all five classes but only enforce stricter retention on a subset; this field declares the operational reality so workflow authors can choose a server appropriately."
3621
+ }
3622
+ },
3623
+ "additionalProperties": true
3624
+ },
3625
+ "production": {
3626
+ "type": "object",
3627
+ "description": "Production-profile advertisement (see production-profile.md). Optional in v1; absence means the host does not claim the openwop-production profile. When `supported: true`, the host claims every MUST in production-profile.md and conformance scenarios gated on this block MUST run. Landed by RFC 0009.",
3628
+ "required": [
3629
+ "supported"
3630
+ ],
3631
+ "properties": {
3632
+ "supported": {
3633
+ "type": "boolean",
3634
+ "description": "Host claims the openwop-production profile end-to-end (production-profile.md)."
3635
+ },
3636
+ "backpressure": {
3637
+ "type": "object",
3638
+ "description": "Backpressure envelope advertisement (production-profile.md §Backpressure).",
3639
+ "properties": {
3640
+ "supported": {
3641
+ "type": "boolean",
3642
+ "description": "Host returns 503 + Retry-After + canonical envelope under load per production-profile.md §Backpressure."
3643
+ },
3644
+ "inflightCap": {
3645
+ "type": "integer",
3646
+ "minimum": 1,
3647
+ "description": "Optional host-side concurrent-inflight cap the conformance suite can saturate. When advertised, the production-backpressure scenario issues `inflightCap + 1` concurrent long-lived requests to deterministically force a 503. When absent, the scenario soft-skips the saturation step (envelope assertion still runs if a 503 happens to fire)."
3648
+ },
3649
+ "retryAfterSeconds": {
3650
+ "type": "integer",
3651
+ "minimum": 0,
3652
+ "maximum": 86400,
3653
+ "description": "Optional advertised Retry-After value in seconds the host returns on 503. When present, MUST equal both the `Retry-After` header and the `details.retryAfter` body field per production-profile.md. Upper bound 86400 (24h) — values beyond that are operationally indistinguishable from 'permanently denied'. Hosts needing longer holds SHOULD omit `Retry-After` entirely (RFC 0009 Q#2)."
3654
+ }
3655
+ },
3656
+ "additionalProperties": false
3657
+ },
3658
+ "retention": {
3659
+ "type": "object",
3660
+ "description": "Event-retention advertisement (production-profile.md §\"Event retention\").",
3661
+ "properties": {
3662
+ "supported": {
3663
+ "type": "boolean",
3664
+ "description": "Host enforces event-log retention with a documented minimum window."
3665
+ },
3666
+ "minWindowSeconds": {
3667
+ "type": "integer",
3668
+ "minimum": 604800,
3669
+ "description": "Documented minimum retention window in seconds. Per production-profile.md §\"Event retention\", MUST be ≥ 604800 (7 days) for public hosts; development-only hosts MAY advertise a smaller window but MUST NOT claim `supported: true` while doing so."
3670
+ },
3671
+ "testForceExpire": {
3672
+ "type": "boolean",
3673
+ "description": "Host exposes a test-only force-expire hook the conformance suite can call (URL/method supplied via `OPENWOP_TEST_FORCE_EXPIRE_URL` / `OPENWOP_TEST_FORCE_EXPIRE_METHOD` env vars). When `false`, the production-retention-expiry scenario asserts only the 410/404 envelope shape on an operator-supplied already-expired run id (via `OPENWOP_TEST_EXPIRED_RUN_ID`); otherwise it soft-skips. RFC 0009 unresolved question #1 — endpoint normation is deferred."
3674
+ }
3675
+ },
3676
+ "additionalProperties": false
3677
+ },
3678
+ "debugBundle": {
3679
+ "type": "object",
3680
+ "description": "Debug-bundle truncation advertisement (production-profile.md §\"Debug bundle behavior\"). Stricter than the existing `capabilities.debugBundle.supported` advertised per debug-bundle.md — this block adds the production-profile MUSTs (truncation metadata, redaction).",
3681
+ "properties": {
3682
+ "supported": {
3683
+ "type": "boolean",
3684
+ "description": "Host claims production-profile debug-bundle behavior end-to-end."
3685
+ },
3686
+ "truncationMetadata": {
3687
+ "type": "boolean",
3688
+ "description": "When `true`, host surfaces `truncated: true` + non-empty `truncatedReason` per debug-bundle.md §\"Bundle size limits\" when caps are reached."
3689
+ }
3690
+ },
3691
+ "additionalProperties": false
3692
+ }
3693
+ },
3694
+ "additionalProperties": false
3695
+ },
3696
+ "auth": {
3697
+ "type": "object",
3698
+ "description": "Auth-profile advertisement (auth-profiles.md). Optional in v1; absence preserves the baseline bearer-token contract from auth.md. RFC 0010 formalized this block; `additionalProperties: true` permits existing informal usages (`auth.auditLogIntegrity` from the audit-log-integrity profile) to continue alongside the formal sub-blocks defined here.",
3699
+ "properties": {
3700
+ "profiles": {
3701
+ "type": "array",
3702
+ "items": {
3703
+ "type": "string",
3704
+ "minLength": 1
3705
+ },
3706
+ "uniqueItems": true,
3707
+ "description": "Auth profiles the host claims. Canonical ids: `openwop-audit-log-integrity` (auth-profiles.md §Audit-log integrity), `openwop-auth-api-key-rotation`, `openwop-auth-oauth2-client-credentials`, `openwop-auth-oidc-user-bearer`, `openwop-auth-mtls`, `openwop-auth-saml` + `openwop-auth-scim` + `openwop-auth-ldap` (RFC 0050 enterprise identity). Clients SHOULD tolerate unknown profile ids."
3708
+ },
3709
+ "subjectLinking": {
3710
+ "type": "boolean",
3711
+ "description": "RFC 0164: DERIVED and DEPRECATED toward v2. A host that advertises both `openwop-auth-saml` and `openwop-auth-scim` MUST set this true (schema conditional on `profiles[]`); a document advertising both with this absent/false is invalid. Kept through v1.x for clients pinned to it; clients SHOULD derive the guarantee from the profile pair. Original (RFC 0159) semantics follow. RFC 0159 (auth-profiles.md §Subject linking). When true, this host advertises BOTH `openwop-auth-saml` and `openwop-auth-scim` AND honours the cross-profile subject-linking obligation: it links the SAML and SCIM principals of one human on an opaque, IdP-stable subject id (SCIM `externalId` ↔ persistent SAML `NameID`), same-tenant only, and on SCIM deactivation fail-closes the linked SAML identity too (composing RFC 0049 §C) — so a provisioned leaver cannot still SSO in. The link key MUST NOT be a mutable/PII attribute (email, userName). Optional; absent/false means the SAML and SCIM lanes are independent and the combined leaver guarantee is NOT claimed. A host MUST NOT set this true unless both profiles are advertised and the obligation holds."
3712
+ },
3713
+ "subjectLinkKey": {
3714
+ "type": "string",
3715
+ "enum": [
3716
+ "opaque-idp",
3717
+ "configured-immutable"
3718
+ ],
3719
+ "description": "RFC 0163 (auth-profiles.md §Subject linking). The CLASS of opaque, IdP-stable, non-PII identifier this host joins the SAML and SCIM lanes on. A CLOSED enum of ALLOWED classes only, naming classes rather than vendor attributes: `opaque-idp` (SCIM `externalId` ↔ persistent-format SAML `NameID`) or `configured-immutable` (a host-configured attribute asserted by the same IdP on both lanes that is opaque, stable for the account lifetime, never reassigned, non-PII and not user- or operator-influenceable — e.g. a directory object id or an immutable-id claim; the attribute name stays host config, the class goes on the wire). Mutable/PII keys (email, userName, displayName) are INEXPRESSIBLE by construction — a conforming host cannot name one, which is the witness that converts RFC 0159 §A.2/§A.4's negative-existence prohibition into a positive advertisement. REQUIRED when `subjectLinking` is true: expressed BOTH as the auth-profiles.md §Subject linking MUST and as this block's `if`/`then` conditional, so a discovery document claiming linking without a class fails schema validation as well as the conformance scenario. Absent when `subjectLinking` is absent/false."
3720
+ },
3721
+ "rotation": {
3722
+ "type": "object",
3723
+ "description": "API-key rotation advertisement (auth-profiles.md §`openwop-auth-api-key-rotation`).",
3724
+ "properties": {
3725
+ "supported": {
3726
+ "type": "boolean",
3727
+ "description": "Host claims the openwop-auth-api-key-rotation profile (old+new key overlap during a documented grace window)."
3728
+ },
3729
+ "minGraceSeconds": {
3730
+ "type": "integer",
3731
+ "minimum": 0,
3732
+ "description": "Minimum rotation grace window in seconds. auth-profiles.md SHOULDs production-profile hosts to ≥ 86400 (24h). The conformance scenario tolerates any non-negative value but warns in behavior mode when < 86400."
3733
+ }
3734
+ },
3735
+ "additionalProperties": false
3736
+ },
3737
+ "oauth2": {
3738
+ "type": "object",
3739
+ "description": "OAuth2 client-credentials advertisement (auth-profiles.md §`openwop-auth-oauth2-client-credentials`).",
3740
+ "properties": {
3741
+ "supported": {
3742
+ "type": "boolean"
3743
+ },
3744
+ "issuer": {
3745
+ "type": "string",
3746
+ "format": "uri",
3747
+ "description": "Token issuer URL the host trusts. Tokens MUST carry a matching `iss` claim."
3748
+ },
3749
+ "audience": {
3750
+ "type": "string",
3751
+ "description": "Audience the host requires. Tokens MUST carry a matching `aud` claim."
3752
+ },
3753
+ "supportedAlgorithms": {
3754
+ "type": "array",
3755
+ "items": {
3756
+ "type": "string",
3757
+ "minLength": 1
3758
+ },
3759
+ "uniqueItems": true,
3760
+ "description": "JWS signing algorithms the host accepts (canonical: RS256, ES256). Hosts MUST reject tokens signed with algorithms outside this list."
3761
+ }
3762
+ },
3763
+ "additionalProperties": false
3764
+ },
3765
+ "oidc": {
3766
+ "type": "object",
3767
+ "description": "OIDC user-bearer advertisement (auth-profiles.md §`openwop-auth-oidc-user-bearer`).",
3768
+ "properties": {
3769
+ "supported": {
3770
+ "type": "boolean"
3771
+ },
3772
+ "issuers": {
3773
+ "type": "array",
3774
+ "items": {
3775
+ "type": "string",
3776
+ "format": "uri"
3777
+ },
3778
+ "minItems": 1,
3779
+ "uniqueItems": true,
3780
+ "description": "Trusted OIDC issuer URLs. The host accepts tokens whose `iss` claim matches any entry."
3781
+ },
3782
+ "audience": {
3783
+ "type": "string",
3784
+ "description": "Audience identifier the host requires in OIDC tokens."
3785
+ },
3786
+ "supportedScopeMapping": {
3787
+ "type": "string",
3788
+ "enum": [
3789
+ "group-claim",
3790
+ "scope-claim",
3791
+ "host-acl"
3792
+ ],
3793
+ "description": "How the host derives openwop scopes from the OIDC token. `group-claim`: from `groups` claim via host config. `scope-claim`: from `scope` claim directly. `host-acl`: from a host-side mapping table (sub → scope)."
3794
+ },
3795
+ "introspectionIntervalSeconds": {
3796
+ "type": "integer",
3797
+ "minimum": 0,
3798
+ "description": "Maximum interval at which the host SHOULD re-introspect a cached token to detect IdP revocation. auth-profiles.md recommends `min(exp - now, 300)`."
3799
+ }
3800
+ },
3801
+ "additionalProperties": false
3802
+ },
3803
+ "mtls": {
3804
+ "type": "object",
3805
+ "description": "mTLS advertisement (auth-profiles.md §`openwop-auth-mtls`).",
3806
+ "properties": {
3807
+ "supported": {
3808
+ "type": "boolean"
3809
+ },
3810
+ "required": {
3811
+ "type": "boolean",
3812
+ "description": "When `true`, the host rejects bearer-only requests (mTLS required for all authenticated calls). When `false`, mTLS is optional and complements bearer auth."
3813
+ },
3814
+ "subjectMapping": {
3815
+ "type": "string",
3816
+ "enum": [
3817
+ "cn",
3818
+ "san-dns",
3819
+ "san-uri"
3820
+ ],
3821
+ "description": "How the host derives the transport principal from the client certificate. `cn`: subject CN. `san-dns`: subjectAltName DNS entry. `san-uri`: subjectAltName URI entry."
3822
+ }
3823
+ },
3824
+ "additionalProperties": false
3825
+ },
3826
+ "workloadIdentity": {
3827
+ "type": "object",
3828
+ "description": "RFC 0154 §A — workload identity profile `openwop-workload-identity-v1`. A host that advertises this cryptographically verifies the presented workload identity, binds it to the request, and resolves it to an OpenWOP principal BEFORE authorization. Unresolvable identity fails closed. **Identity is not authorization** (RFC 0147 R12): proving which workload called says nothing about what it may do, and treating the two as one is the confused-deputy shape this profile exists to prevent — tenant, audience, and policy MUST still be re-evaluated at every boundary.",
3829
+ "additionalProperties": false,
3830
+ "required": [
3831
+ "supported"
3832
+ ],
3833
+ "properties": {
3834
+ "supported": {
3835
+ "type": "boolean"
3836
+ },
3837
+ "schemes": {
3838
+ "type": "array",
3839
+ "minItems": 1,
3840
+ "uniqueItems": true,
3841
+ "items": {
3842
+ "enum": [
3843
+ "spiffe",
3844
+ "mtls-san",
3845
+ "cloud-subject",
3846
+ "oauth-client"
3847
+ ]
3848
+ },
3849
+ "description": "RFC 0154 §A — closed set of identity schemes the host verifies. Closed because an unrecognized scheme is a verification path nobody implemented, and accepting the NAME without the verification is precisely the failure the profile prevents."
3850
+ },
3851
+ "senderConstraint": {
3852
+ "type": "array",
3853
+ "uniqueItems": true,
3854
+ "items": {
3855
+ "enum": [
3856
+ "mtls",
3857
+ "dpop"
3858
+ ]
3859
+ },
3860
+ "description": "RFC 0154 §A — proof-of-possession mechanisms. Without a sender constraint a bearer credential is replayable by anyone who observes it, so a host advertising workload identity WITHOUT one is asserting who called, not that the caller held the key."
3861
+ },
3862
+ "delegation": {
3863
+ "type": "object",
3864
+ "description": "RFC 0154 §B — delegated actor chains, advertised SEPARATELY from §A identity resolution. A host can verify workload identity without implementing delegation, and forcing one flag to cover both would make a §A-only host choose between advertising a capability it does not have and advertising nothing at all. That is the same overclaim-or-silence bind RFC 0155 §A names for `openwop-core`, and per-section advertisement is how it is avoided: `supported` means §A resolution, `delegation.supported` means §B chain verification, and a conformance run gates each section on its own flag.",
3865
+ "additionalProperties": false,
3866
+ "required": [
3867
+ "supported"
3868
+ ],
3869
+ "properties": {
3870
+ "supported": {
3871
+ "type": "boolean"
3872
+ },
3873
+ "maxChainDepth": {
3874
+ "type": "integer",
3875
+ "minimum": 1,
3876
+ "description": "RFC 0154 §B — bounded chain length. Unbounded delegation is unbounded authority laundering: each hop is another party the host is trusting transitively."
3877
+ }
3878
+ }
3879
+ }
3880
+ }
3881
+ }
3882
+ },
3883
+ "allOf": [
3884
+ {
3885
+ "if": {
3886
+ "properties": {
3887
+ "subjectLinking": {
3888
+ "const": true
3889
+ }
3890
+ },
3891
+ "required": [
3892
+ "subjectLinking"
3893
+ ]
3894
+ },
3895
+ "then": {
3896
+ "required": [
3897
+ "subjectLinkKey"
3898
+ ],
3899
+ "$comment": "RFC 0163 §A.1 / §C: a host that claims subject linking MUST declare the class it joins on. A conditional required on a flag no host emitted when it landed is a tightening of an unemitted shape (COMPATIBILITY.md §2.2), not closure of a server-emitted shape (§2.1)."
3900
+ }
3901
+ },
3902
+ {
3903
+ "if": {
3904
+ "properties": {
3905
+ "profiles": {
3906
+ "allOf": [
3907
+ {
3908
+ "contains": {
3909
+ "const": "openwop-auth-saml"
3910
+ }
3911
+ },
3912
+ {
3913
+ "contains": {
3914
+ "const": "openwop-auth-scim"
3915
+ }
3916
+ }
3917
+ ]
3918
+ }
3919
+ },
3920
+ "required": [
3921
+ "profiles"
3922
+ ]
3923
+ },
3924
+ "then": {
3925
+ "properties": {
3926
+ "subjectLinking": {
3927
+ "const": true
3928
+ }
3929
+ },
3930
+ "required": [
3931
+ "subjectLinking",
3932
+ "subjectLinkKey"
3933
+ ],
3934
+ "$comment": "RFC 0164 §A.3 / §B: advertising BOTH openwop-auth-saml and openwop-auth-scim implies the combined leaver contract, so `subjectLinking` MUST be true (a derived statement of fact, deprecated toward v2) and `subjectLinkKey` MUST name the class. Binds only documents that emit both profile strings; none did when it landed (COMPATIBILITY.md §2.2 / §4 default-on row with an empty harm set)."
3935
+ }
3936
+ }
3937
+ ],
3938
+ "additionalProperties": true
3939
+ },
3940
+ "discovery": {
3941
+ "type": "object",
3942
+ "description": "Discovery advertisement (capabilities-change-detection.md). Optional in v1; absence means the host serves only the public unauthenticated payload at /.well-known/openwop. Landed by RFC 0011.",
3943
+ "properties": {
3944
+ "authScoped": {
3945
+ "type": "object",
3946
+ "description": "Auth-scoped discovery advertisement (capabilities-change-detection.md §\"Scoped capability views\"). Hosts that return a different payload when called with Authorization than when called anonymously declare it here. The authenticated view MUST still satisfy the base capabilities.schema.json shape per the spec annex.",
3947
+ "properties": {
3948
+ "supported": {
3949
+ "type": "boolean",
3950
+ "description": "Host returns an authenticated/tenant-scoped capability view when the discovery endpoint is called with valid bearer credentials."
3951
+ },
3952
+ "mode": {
3953
+ "type": "string",
3954
+ "enum": [
3955
+ "same-endpoint",
3956
+ "extension-endpoint"
3957
+ ],
3958
+ "description": "How the host exposes the auth-scoped view. `same-endpoint`: the canonical /.well-known/openwop returns a narrowed/enriched view when authenticated. `extension-endpoint`: a separate host route carries the scoped view (path advertised via `endpointPath`). Hosts using the third pattern from the spec annex (documentation pointer in the public payload) MAY omit this field."
3959
+ },
3960
+ "endpointPath": {
3961
+ "type": "string",
3962
+ "pattern": "^/",
3963
+ "description": "When `mode: \"extension-endpoint\"` is advertised, the leading-slash relative path the host serves the scoped view from. The conformance scenario hits this path with bearer credentials. Absolute URLs are rejected at the schema level."
3964
+ }
3965
+ },
3966
+ "additionalProperties": false
3967
+ }
3968
+ },
3969
+ "additionalProperties": false
3970
+ },
3971
+ "i18n": {
3972
+ "type": "object",
3973
+ "description": "Locale-negotiation advertisement per spec/v1/i18n.md. Optional in v1; absence means the host serves a single locale always (typically `en`). When `supported: true`, the host honors `Accept-Language` on every protected route and emits `Content-Language` on responses carrying localized text.",
3974
+ "properties": {
3975
+ "supported": {
3976
+ "type": "boolean",
3977
+ "description": "Host honors `Accept-Language` and returns localized human-facing text when negotiable."
3978
+ },
3979
+ "defaultLocale": {
3980
+ "type": "string",
3981
+ "pattern": "^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8}){0,3}$",
3982
+ "description": "BCP 47 language tag the host falls back to when no `Accept-Language` matches `supportedLocales`. Default: `\"en\"` when omitted."
3983
+ },
3984
+ "supportedLocales": {
3985
+ "type": "array",
3986
+ "minItems": 1,
3987
+ "uniqueItems": true,
3988
+ "items": {
3989
+ "type": "string",
3990
+ "pattern": "^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8}){0,3}$"
3991
+ },
3992
+ "description": "BCP 47 tags of locales the host has validated end-to-end for human-facing text. MUST contain `defaultLocale` when both are present."
3993
+ }
3994
+ },
3995
+ "additionalProperties": false
3996
+ },
3997
+ "content": {
3998
+ "type": "object",
3999
+ "description": "RFC 0103 (spec/v1/localized-content.md). Localized authored content (pages → sections) advertisement. Reuses the i18n annex's Accept-Language/Content-Language negotiation; it does NOT redeclare negotiation. Requires `i18n.supported: true`. `baseLocale` MUST equal `capabilities.i18n.defaultLocale`; `({baseLocale} ∪ supportedLocales)` MUST be a subset of `capabilities.i18n.supportedLocales`; `baseLocale` MUST NOT appear in `supportedLocales`. Hosts that omit this block serve no content surface; the conformance scenarios skip cleanly.",
4000
+ "additionalProperties": false,
4001
+ "required": [
4002
+ "supported",
4003
+ "baseLocale",
4004
+ "supportedLocales"
4005
+ ],
4006
+ "properties": {
4007
+ "supported": {
4008
+ "type": "boolean",
4009
+ "description": "Host serves the localized-content surface (`GET /v1/content/pages/{slug}` + tenant-scoped admin CRUD). Requires `i18n.supported: true`."
4010
+ },
4011
+ "baseLocale": {
4012
+ "type": "string",
4013
+ "pattern": "^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8}){0,3}$",
4014
+ "description": "The locale section `data` is authored in. MUST equal `capabilities.i18n.defaultLocale`."
4015
+ },
4016
+ "supportedLocales": {
4017
+ "type": "array",
4018
+ "uniqueItems": true,
4019
+ "items": {
4020
+ "type": "string",
4021
+ "pattern": "^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8}){0,3}$"
4022
+ },
4023
+ "description": "BCP 47 tags the host has authored content translations for. MUST NOT contain `baseLocale`; `({baseLocale} ∪ supportedLocales)` MUST be a subset of `i18n.supportedLocales`."
4024
+ }
4025
+ }
4026
+ },
4027
+ "portability": {
4028
+ "type": "object",
4029
+ "description": "RFC 0098 (`Active`). Export/import of a tenant's reusable estate (agents 0070, packs 0003/0013, prompt templates 0027, connection *refs* 0045/0095, schedules 0052, roster/org-chart 0086/0087). An export bundle carries NO credential values — only refs to be re-bound at the destination (RFC 0046/0079). Import maps the estate onto the destination's RFC 0048 identity, MUST offer a no-write dry-run plan, MUST be idempotent, and is gated by an RFC 0049 scope. A host advertising this serves `/v1/host/sample/{export,import}` (promotable to `/v1/{export,import}`) and emits the content-free `import.applied` event. Hosts that omit this block neither export nor import; the conformance scenarios skip cleanly.",
4030
+ "additionalProperties": false,
4031
+ "properties": {
4032
+ "export": {
4033
+ "type": "boolean",
4034
+ "default": false,
4035
+ "description": "Host can emit an export bundle for the caller's tenant/workspace via `GET /export`."
4036
+ },
4037
+ "import": {
4038
+ "type": "boolean",
4039
+ "default": false,
4040
+ "description": "Host can import an export bundle via `POST /import`. When `true`, `dryRun` MUST also be `true` (a no-write plan preview is mandatory)."
4041
+ },
4042
+ "kinds": {
4043
+ "type": "array",
4044
+ "items": {
4045
+ "type": "string",
4046
+ "enum": [
4047
+ "agent",
4048
+ "pack",
4049
+ "prompt-template",
4050
+ "connection-ref",
4051
+ "schedule",
4052
+ "roster",
4053
+ "org-chart"
4054
+ ]
4055
+ },
4056
+ "uniqueItems": true,
4057
+ "description": "Estate kinds this host can export/import."
4058
+ },
4059
+ "dryRun": {
4060
+ "type": "boolean",
4061
+ "default": true,
4062
+ "description": "Import supports a no-write plan preview (`POST /import?dryRun=true`). MUST be true if `import` is true."
4063
+ }
4064
+ },
4065
+ "if": {
4066
+ "properties": {
4067
+ "import": {
4068
+ "const": true
4069
+ }
4070
+ },
4071
+ "required": [
4072
+ "import"
4073
+ ]
4074
+ },
4075
+ "then": {
4076
+ "properties": {
4077
+ "dryRun": {
4078
+ "const": true
4079
+ }
4080
+ },
4081
+ "required": [
4082
+ "dryRun"
4083
+ ]
4084
+ }
4085
+ },
4086
+ "interrupt": {
4087
+ "type": "object",
4088
+ "additionalProperties": false,
4089
+ "description": "RFC 0104 (`Active`). Interrupt-surface capabilities. Currently advertises portable HITL approver routing on the `kind: \"approval\"` InterruptPayload.",
4090
+ "properties": {
4091
+ "approverRouting": {
4092
+ "type": "object",
4093
+ "additionalProperties": false,
4094
+ "required": [
4095
+ "supported"
4096
+ ],
4097
+ "description": "RFC 0104. When `supported: true`, the host surfaces the OPTIONAL, ADVISORY `approverGroupRefs` / `approverRoleRefs` / `audience` fields on the approval InterruptPayload unchanged, resolves the ref kinds it advertises against its own identity/RBAC, ENFORCES eligibility at resolve time (the refs stay advisory metadata for clients; enforcement is host-side), and SHOULD route notifications to the resolved union. Refs are opaque to the engine and snapshotted at decision time for deterministic replay. Hosts that omit this block (or set `supported: false`) ignore the fields and remain conformant. Advertise only what the host actually resolves (`refKinds`) and honors (`audience`).",
4098
+ "properties": {
4099
+ "supported": {
4100
+ "type": "boolean",
4101
+ "description": "Host honors the RFC 0104 approver-routing fields."
4102
+ },
4103
+ "refKinds": {
4104
+ "type": "array",
4105
+ "items": {
4106
+ "type": "string",
4107
+ "enum": [
4108
+ "group",
4109
+ "role"
4110
+ ]
4111
+ },
4112
+ "uniqueItems": true,
4113
+ "description": "Which approver ref kinds the host actually resolves. `group` ⇒ honors `approverGroupRefs`; `role` ⇒ honors `approverRoleRefs`. Absent ⇒ the host resolves neither (advisory-only passthrough). Capability honesty: advertise only the kinds the host's resolver supports."
4114
+ },
4115
+ "audience": {
4116
+ "type": "boolean",
4117
+ "default": false,
4118
+ "description": "Host honors the `audience` notification-targeting override. Absent/`false` ⇒ the host notifies the resolved eligible union and ignores `audience`."
4119
+ }
4120
+ }
4121
+ }
4122
+ }
4123
+ },
4124
+ "restTransport": {
4125
+ "type": "object",
4126
+ "additionalProperties": false,
4127
+ "description": "RFC 0115 (`Active`). Conditional-GET + Content-Encoding negotiation on run reads (`GET /v1/runs/{runId}`). Optional. Distinct from the file-egress `fileHandling.transport` (ftp/sftp/ssh) sub-capability — this advertises HTTP-layer poll economy on the run-read REST surface.",
4128
+ "properties": {
4129
+ "conditionalRunGet": {
4130
+ "type": "boolean",
4131
+ "description": "RFC 0115. Host emits a strong, event-log-sequence-derived `ETag` on `GET /v1/runs/{runId}` and honors `If-None-Match` with a `304 Not Modified` (empty body) when the validator matches the current state version."
4132
+ },
4133
+ "contentEncodings": {
4134
+ "type": "array",
4135
+ "items": {
4136
+ "type": "string",
4137
+ "enum": [
4138
+ "gzip",
4139
+ "br",
4140
+ "zstd"
4141
+ ]
4142
+ },
4143
+ "description": "RFC 0115. Content-Encoding values the host will negotiate on run reads (advertisement of standard-HTTP behavior). `gzip` is the baseline; `br`/`zstd` are OPTIONAL — the host advertises only the subset it can actually serve. For each advertised value the decoded body MUST be byte-identical to the identity body."
4144
+ }
4145
+ }
4146
+ },
4147
+ "a2uiSurface": {
4148
+ "type": "object",
4149
+ "additionalProperties": false,
4150
+ "description": "RFC 0114 (`Active`). Host-side TRANSPORT features over the recorded `ui.a2ui-surface` envelope (RFC 0102). A transport optimization, NOT an envelope-payload kind — the recorded envelope stays the full surface. Optional.",
4151
+ "properties": {
4152
+ "deltaTransport": {
4153
+ "type": "boolean",
4154
+ "description": "RFC 0114. Host delivers RFC 6902 (`a2ui-surface-delta-frame.schema.json`) delta frames over the run event stream to subscribers that negotiate `?a2uiDelta=1`; the full surface is materialized for everyone else, the event-log read, and replay. The recorded `ui.a2ui-surface` envelope stays full. The consumer re-validates the post-patch surface against the closed `catalogVersion` catalog before render and falls back fail-closed on any apply/validation failure (the host then re-materializes full)."
4155
+ }
4156
+ }
4157
+ }
4158
+ },
4159
+ "additionalProperties": true
4160
+ }