@openwop/openwop-conformance 1.62.0 → 1.65.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.
- package/README.md +2 -2
- package/coverage.md +20 -0
- package/package.json +1 -1
- package/schemas/artifact-type-pack-manifest.schema.json +108 -24
- package/schemas/chat-card-pack-manifest.schema.json +125 -30
- package/schemas/connection-pack-manifest.schema.json +211 -38
- package/schemas/form-content-pack-manifest.schema.json +91 -20
- package/schemas/frontend-plugin-manifest.schema.json +10 -0
- package/schemas/node-pack-manifest.schema.json +311 -68
- package/schemas/prompt-pack-manifest.schema.json +48 -11
- package/schemas/registry-version-manifest.schema.json +5 -0
- package/schemas/workflow-chain-pack-manifest.schema.json +176 -40
- package/src/lib/artifactTypes.ts +52 -0
- package/src/lib/behavior-gate.ts +17 -0
- package/src/scenarios/artifact-type-legacy-ids.test.ts +124 -0
- package/src/scenarios/artifact-type-pack-install.test.ts +17 -6
- package/src/scenarios/artifact-type-store-without-render.test.ts +17 -4
- package/src/scenarios/chat-card-pack-execution.test.ts +17 -5
- package/src/scenarios/pack-manifest-extension-opacity.test.ts +179 -0
- package/src/scenarios/pack-manifest-extensions.test.ts +203 -0
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
"title": "ConnectionPackManifest",
|
|
5
5
|
"description": "RFC 0095. Manifest for a published OpenWOP connection pack — `pack.json` at the pack root with `kind: \"connection\"`. Peer to and disjoint from `node-pack-manifest.schema.json` (RFC 0003), `prompt-pack-manifest.schema.json` (RFC 0028), and `workflow-chain-pack-manifest.schema.json` (RFC 0013) via the `kind` discriminator. A connection pack is a portable PROVIDER definition — the authorize/token/revoke endpoints, the read/write OAuth scope catalog, and how the integration is reached (an MCP server, an OpenAPI surface, or a core integration node). It carries NO secret: the OAuth client credential is a host concern (RFC 0047), supplied out of band. When installed, it makes an RFC 0045 connector's `auth.provider` / an RFC 0047 `host.oauth` provider string resolve against `provider.id` instead of host-locked code. See `spec/v1/connection-packs.md`.",
|
|
6
6
|
"type": "object",
|
|
7
|
-
"required": [
|
|
7
|
+
"required": [
|
|
8
|
+
"name",
|
|
9
|
+
"version",
|
|
10
|
+
"kind",
|
|
11
|
+
"engines",
|
|
12
|
+
"provider"
|
|
13
|
+
],
|
|
8
14
|
"additionalProperties": false,
|
|
9
15
|
"properties": {
|
|
10
16
|
"name": {
|
|
@@ -24,34 +30,77 @@
|
|
|
24
30
|
"const": "connection",
|
|
25
31
|
"description": "Pack kind discriminator. MUST be the literal string `\"connection\"` for this schema."
|
|
26
32
|
},
|
|
27
|
-
"description": {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
33
|
+
"description": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"maxLength": 1024
|
|
36
|
+
},
|
|
37
|
+
"author": {
|
|
38
|
+
"type": "string"
|
|
39
|
+
},
|
|
40
|
+
"license": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "SPDX license identifier (e.g., `Apache-2.0`)."
|
|
43
|
+
},
|
|
44
|
+
"homepage": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"format": "uri"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"format": "uri"
|
|
51
|
+
},
|
|
32
52
|
"keywords": {
|
|
33
53
|
"type": "array",
|
|
34
|
-
"items": {
|
|
54
|
+
"items": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"maxLength": 64
|
|
57
|
+
},
|
|
35
58
|
"maxItems": 50
|
|
36
59
|
},
|
|
37
60
|
"engines": {
|
|
38
61
|
"type": "object",
|
|
39
|
-
"required": [
|
|
62
|
+
"required": [
|
|
63
|
+
"openwop"
|
|
64
|
+
],
|
|
40
65
|
"additionalProperties": false,
|
|
41
66
|
"properties": {
|
|
42
|
-
"openwop": {
|
|
67
|
+
"openwop": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"description": "SemVer range of the openwop spec version this pack targets."
|
|
70
|
+
}
|
|
43
71
|
}
|
|
44
72
|
},
|
|
45
73
|
"provider": {
|
|
46
74
|
"type": "object",
|
|
47
75
|
"description": "The portable provider definition. Exactly one per connection pack.",
|
|
48
|
-
"required": [
|
|
76
|
+
"required": [
|
|
77
|
+
"id",
|
|
78
|
+
"displayName",
|
|
79
|
+
"category",
|
|
80
|
+
"auth",
|
|
81
|
+
"reach"
|
|
82
|
+
],
|
|
49
83
|
"additionalProperties": false,
|
|
50
84
|
"allOf": [
|
|
51
85
|
{
|
|
52
86
|
"$comment": "RFC 0120 UQ1: apiHosts is REQUIRED when reach is openapi (a credentialed REST/OpenAPI egress surface). A pure metadata / mcp / integration provider MAY omit it. The runtime binding-site validator (spec §Manifest item 13) is the complementary check for credentialed-egress paths the manifest cannot see.",
|
|
53
|
-
"if": {
|
|
54
|
-
|
|
87
|
+
"if": {
|
|
88
|
+
"required": [
|
|
89
|
+
"reach"
|
|
90
|
+
],
|
|
91
|
+
"properties": {
|
|
92
|
+
"reach": {
|
|
93
|
+
"required": [
|
|
94
|
+
"openapi"
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"then": {
|
|
100
|
+
"required": [
|
|
101
|
+
"apiHosts"
|
|
102
|
+
]
|
|
103
|
+
}
|
|
55
104
|
}
|
|
56
105
|
],
|
|
57
106
|
"properties": {
|
|
@@ -60,10 +109,30 @@
|
|
|
60
109
|
"pattern": "^[a-z][a-z0-9-]*$",
|
|
61
110
|
"description": "Stable provider id an RFC 0045/0047 `provider` string resolves to, e.g. `github`."
|
|
62
111
|
},
|
|
63
|
-
"displayName": {
|
|
112
|
+
"displayName": {
|
|
113
|
+
"type": "string",
|
|
114
|
+
"minLength": 1
|
|
115
|
+
},
|
|
64
116
|
"category": {
|
|
65
117
|
"type": "string",
|
|
66
|
-
"enum": [
|
|
118
|
+
"enum": [
|
|
119
|
+
"communication",
|
|
120
|
+
"docs",
|
|
121
|
+
"crm",
|
|
122
|
+
"dev",
|
|
123
|
+
"storage",
|
|
124
|
+
"email-calendar",
|
|
125
|
+
"ticketing",
|
|
126
|
+
"data-warehouse",
|
|
127
|
+
"marketing",
|
|
128
|
+
"finance",
|
|
129
|
+
"hr",
|
|
130
|
+
"esignature",
|
|
131
|
+
"support",
|
|
132
|
+
"project-management",
|
|
133
|
+
"payments",
|
|
134
|
+
"other"
|
|
135
|
+
]
|
|
67
136
|
},
|
|
68
137
|
"vendor": {
|
|
69
138
|
"type": "string",
|
|
@@ -72,14 +141,36 @@
|
|
|
72
141
|
},
|
|
73
142
|
"auth": {
|
|
74
143
|
"type": "object",
|
|
75
|
-
"required": [
|
|
144
|
+
"required": [
|
|
145
|
+
"kind"
|
|
146
|
+
],
|
|
76
147
|
"additionalProperties": false,
|
|
77
148
|
"properties": {
|
|
78
|
-
"kind": {
|
|
79
|
-
|
|
149
|
+
"kind": {
|
|
150
|
+
"type": "string",
|
|
151
|
+
"enum": [
|
|
152
|
+
"oauth2",
|
|
153
|
+
"api_key",
|
|
154
|
+
"bearer",
|
|
155
|
+
"basic"
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
"authFlow": {
|
|
159
|
+
"type": "string",
|
|
160
|
+
"enum": [
|
|
161
|
+
"pkce",
|
|
162
|
+
"client_credentials",
|
|
163
|
+
"manual",
|
|
164
|
+
"none"
|
|
165
|
+
]
|
|
166
|
+
},
|
|
80
167
|
"scopeModel": {
|
|
81
168
|
"type": "string",
|
|
82
|
-
"enum": [
|
|
169
|
+
"enum": [
|
|
170
|
+
"groups",
|
|
171
|
+
"coarse",
|
|
172
|
+
"capabilities"
|
|
173
|
+
],
|
|
83
174
|
"default": "groups",
|
|
84
175
|
"description": "`groups` = read/write scope groups (Google/Jira/Graph). `coarse` = a single read-vs-write toggle (Stripe Connect read_only/read_write). `capabilities` = consent is a static developer-portal capability set, not per-request scopes (Notion)."
|
|
85
176
|
},
|
|
@@ -88,9 +179,21 @@
|
|
|
88
179
|
"additionalProperties": false,
|
|
89
180
|
"description": "Fixed, manifest-declared https endpoints (RFC 0095 §B.3). Never derived from runtime user input — not an SSRF surface.",
|
|
90
181
|
"properties": {
|
|
91
|
-
"authorize": {
|
|
92
|
-
|
|
93
|
-
|
|
182
|
+
"authorize": {
|
|
183
|
+
"type": "string",
|
|
184
|
+
"format": "uri",
|
|
185
|
+
"pattern": "^https://"
|
|
186
|
+
},
|
|
187
|
+
"token": {
|
|
188
|
+
"type": "string",
|
|
189
|
+
"format": "uri",
|
|
190
|
+
"pattern": "^https://"
|
|
191
|
+
},
|
|
192
|
+
"revoke": {
|
|
193
|
+
"type": "string",
|
|
194
|
+
"format": "uri",
|
|
195
|
+
"pattern": "^https://"
|
|
196
|
+
}
|
|
94
197
|
}
|
|
95
198
|
},
|
|
96
199
|
"scopes": {
|
|
@@ -98,8 +201,18 @@
|
|
|
98
201
|
"additionalProperties": false,
|
|
99
202
|
"description": "Read/write scope groups. `write` is requested as a SEPARATE re-consent step (RFC 0095 §B.4).",
|
|
100
203
|
"properties": {
|
|
101
|
-
"read": {
|
|
102
|
-
|
|
204
|
+
"read": {
|
|
205
|
+
"type": "array",
|
|
206
|
+
"items": {
|
|
207
|
+
"$ref": "#/$defs/ScopeGroup"
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
"write": {
|
|
211
|
+
"type": "array",
|
|
212
|
+
"items": {
|
|
213
|
+
"$ref": "#/$defs/ScopeGroup"
|
|
214
|
+
}
|
|
215
|
+
}
|
|
103
216
|
}
|
|
104
217
|
},
|
|
105
218
|
"instanceUrlTemplate": {
|
|
@@ -117,64 +230,124 @@
|
|
|
117
230
|
"properties": {
|
|
118
231
|
"mcp": {
|
|
119
232
|
"type": "object",
|
|
120
|
-
"required": [
|
|
233
|
+
"required": [
|
|
234
|
+
"server"
|
|
235
|
+
],
|
|
121
236
|
"additionalProperties": false,
|
|
122
237
|
"properties": {
|
|
123
238
|
"server": {
|
|
124
239
|
"type": "object",
|
|
125
|
-
"required": [
|
|
240
|
+
"required": [
|
|
241
|
+
"url",
|
|
242
|
+
"transport"
|
|
243
|
+
],
|
|
126
244
|
"additionalProperties": false,
|
|
127
245
|
"properties": {
|
|
128
|
-
"url": {
|
|
129
|
-
|
|
246
|
+
"url": {
|
|
247
|
+
"type": "string",
|
|
248
|
+
"format": "uri",
|
|
249
|
+
"pattern": "^https://"
|
|
250
|
+
},
|
|
251
|
+
"transport": {
|
|
252
|
+
"type": "string",
|
|
253
|
+
"enum": [
|
|
254
|
+
"http",
|
|
255
|
+
"sse"
|
|
256
|
+
]
|
|
257
|
+
}
|
|
130
258
|
}
|
|
131
259
|
}
|
|
132
260
|
}
|
|
133
261
|
},
|
|
134
262
|
"openapi": {
|
|
135
263
|
"type": "object",
|
|
136
|
-
"required": [
|
|
264
|
+
"required": [
|
|
265
|
+
"ref"
|
|
266
|
+
],
|
|
137
267
|
"additionalProperties": false,
|
|
138
268
|
"properties": {
|
|
139
|
-
"ref": {
|
|
269
|
+
"ref": {
|
|
270
|
+
"type": "string",
|
|
271
|
+
"description": "URL or in-pack path of the OpenAPI document for core.openwop.http.openapi-call."
|
|
272
|
+
}
|
|
140
273
|
}
|
|
141
274
|
},
|
|
142
275
|
"integration": {
|
|
143
276
|
"type": "object",
|
|
144
|
-
"required": [
|
|
277
|
+
"required": [
|
|
278
|
+
"node"
|
|
279
|
+
],
|
|
145
280
|
"additionalProperties": false,
|
|
146
281
|
"properties": {
|
|
147
|
-
"node": {
|
|
282
|
+
"node": {
|
|
283
|
+
"type": "string",
|
|
284
|
+
"description": "A core.openwop.integration.* node typeId."
|
|
285
|
+
}
|
|
148
286
|
}
|
|
149
287
|
}
|
|
150
288
|
}
|
|
151
289
|
},
|
|
152
290
|
"consumerNodes": {
|
|
153
291
|
"type": "array",
|
|
154
|
-
"items": {
|
|
292
|
+
"items": {
|
|
293
|
+
"type": "string"
|
|
294
|
+
},
|
|
155
295
|
"description": "The core node typeIds that consume this provider's credential (e.g. core.openwop.mcp.invoke-tool)."
|
|
156
296
|
},
|
|
157
|
-
"docsUrl": {
|
|
297
|
+
"docsUrl": {
|
|
298
|
+
"type": "string",
|
|
299
|
+
"format": "uri"
|
|
300
|
+
},
|
|
158
301
|
"apiHosts": {
|
|
159
302
|
"type": "array",
|
|
160
303
|
"description": "RFC 0120. API host(s) a host MAY send this provider's resolved credential to for connector egress (RFC 0045). Each entry is a bare registrable hostname — lowercase ASCII, >=2 labels, TLD label beginning with a letter; no scheme, port, path, wildcard, or IP literal. Matched by dot-anchored suffix containment (request host == entry OR ends with '.'+entry); eTLD+1 is the floor and a pack MAY list a tighter exact host (e.g. googleads.googleapis.com). Omitted => no credential-bearing connector egress is reachable (fails closed). REQUIRED when reach is openapi (see the provider-level allOf).",
|
|
161
|
-
"items": {
|
|
304
|
+
"items": {
|
|
305
|
+
"type": "string",
|
|
306
|
+
"format": "hostname",
|
|
307
|
+
"pattern": "^(?!-)[a-z0-9-]{1,63}(\\.[a-z0-9-]{1,63})*\\.[a-z][a-z0-9-]*$"
|
|
308
|
+
},
|
|
162
309
|
"uniqueItems": true,
|
|
163
310
|
"minItems": 1
|
|
164
311
|
}
|
|
312
|
+
},
|
|
313
|
+
"patternProperties": {
|
|
314
|
+
"^(x-|vendor\\.)": {
|
|
315
|
+
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md` §\"Vendor-prefixed namespaces\", which requires a client receiving an unknown vendor-prefixed field to treat it as opaque — a field that `additionalProperties: false` previously made impossible to carry. SECURITY: an extension value is PACK-AUTHORED, therefore untrusted; \"ignore\" means ignore. See `node-packs.md` §\"Vendor extensions on pack manifests\" and the `pack-manifest-extension-opaque` invariant."
|
|
316
|
+
}
|
|
165
317
|
}
|
|
166
318
|
}
|
|
167
319
|
},
|
|
168
320
|
"$defs": {
|
|
169
321
|
"ScopeGroup": {
|
|
170
322
|
"type": "object",
|
|
171
|
-
"required": [
|
|
323
|
+
"required": [
|
|
324
|
+
"key",
|
|
325
|
+
"label",
|
|
326
|
+
"scopes"
|
|
327
|
+
],
|
|
172
328
|
"additionalProperties": false,
|
|
173
329
|
"properties": {
|
|
174
|
-
"key": {
|
|
175
|
-
|
|
176
|
-
|
|
330
|
+
"key": {
|
|
331
|
+
"type": "string",
|
|
332
|
+
"pattern": "^[a-z][a-z0-9._-]*$"
|
|
333
|
+
},
|
|
334
|
+
"label": {
|
|
335
|
+
"type": "string",
|
|
336
|
+
"minLength": 1
|
|
337
|
+
},
|
|
338
|
+
"scopes": {
|
|
339
|
+
"type": "array",
|
|
340
|
+
"items": {
|
|
341
|
+
"type": "string"
|
|
342
|
+
},
|
|
343
|
+
"description": "The provider's raw scope strings, e.g. `https://www.googleapis.com/auth/drive.readonly`."
|
|
344
|
+
}
|
|
177
345
|
}
|
|
178
346
|
}
|
|
347
|
+
},
|
|
348
|
+
"patternProperties": {
|
|
349
|
+
"^(x-|vendor\\.)": {
|
|
350
|
+
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md` §\"Vendor-prefixed namespaces\", which requires a client receiving an unknown vendor-prefixed field to treat it as opaque — a field that `additionalProperties: false` previously made impossible to carry. SECURITY: an extension value is PACK-AUTHORED, therefore untrusted; \"ignore\" means ignore. See `node-packs.md` §\"Vendor extensions on pack manifests\" and the `pack-manifest-extension-opaque` invariant."
|
|
351
|
+
}
|
|
179
352
|
}
|
|
180
353
|
}
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
"title": "FormContentPackManifest",
|
|
5
5
|
"description": "Manifest for a published OpenWOP form-content pack — `pack.json` at the pack root with `kind: \"form-content\"`. Peer to the node / workflow-chain / prompt / artifact-type / card pack manifests; disjoint via the `kind` discriminator. See `spec/v1/form-content-packs.md` for the canonical contract and RFC 0137 for the rationale.\n\nA form-content pack distributes **form templates**: a named, versioned set of typed input fields a host instantiates into an ordinary, fully editable form through its normal create path. It is purely declarative — no `runtime`, no executor bytecode, no entry point. The pack contributes the *shape* of a form; it does not define a submission surface, a handler, or any behavior the host would not otherwise accept.\n\n**A template carries NO submission routing, and MUST NOT gain any.** There is deliberately no `intakeBinding` / destination / webhook / list-id property on `FormTemplate`, and `additionalProperties: false` rejects one. Where a tenant's form submissions go is a decision about the OPERATOR's data; a third-party pack author does not get to make it by shipping a manifest. Enforced as the `form-content-template-no-submission-routing` SECURITY invariant — see `form-content-packs.md` §\"No submission routing\".\n\n**The wire vocabulary governs the WIRE, not host storage.** A host MAY store fields under any internal representation it likes and translate at the pack boundary; the spec constrains what crosses the wire, not what sits in a host's database (`positioning.md`).\n\n**Field types are the RFC 0071 portable subset, not a second vocabulary.** `fields[].type` reuses the closed portable subset defined in `chat-card-packs.md` §\"Input fields — a closed portable subset\" (RFC 0071 Phase 2, gap G9 resolved 2026-05-27) verbatim, including its `vendor.<org>.<kind>` / `x-<kind>` extension escape hatch. Two declarative pack kinds that both collect typed user input MUST agree on what a field type means.",
|
|
6
6
|
"type": "object",
|
|
7
|
-
"required": [
|
|
7
|
+
"required": [
|
|
8
|
+
"name",
|
|
9
|
+
"version",
|
|
10
|
+
"kind",
|
|
11
|
+
"engines",
|
|
12
|
+
"templates"
|
|
13
|
+
],
|
|
8
14
|
"additionalProperties": false,
|
|
9
15
|
"properties": {
|
|
10
16
|
"kind": {
|
|
@@ -24,47 +30,83 @@
|
|
|
24
30
|
"description": "Pack-level SemVer 2.0.0.",
|
|
25
31
|
"pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$"
|
|
26
32
|
},
|
|
27
|
-
"description": {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
33
|
+
"description": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"maxLength": 1024
|
|
36
|
+
},
|
|
37
|
+
"author": {
|
|
38
|
+
"type": "string"
|
|
39
|
+
},
|
|
40
|
+
"license": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "SPDX license identifier (e.g., `Apache-2.0`, `MIT`)."
|
|
43
|
+
},
|
|
44
|
+
"homepage": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"format": "uri"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"format": "uri"
|
|
51
|
+
},
|
|
32
52
|
"keywords": {
|
|
33
53
|
"type": "array",
|
|
34
|
-
"items": {
|
|
54
|
+
"items": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"maxLength": 64
|
|
57
|
+
},
|
|
35
58
|
"maxItems": 50
|
|
36
59
|
},
|
|
37
60
|
"engines": {
|
|
38
61
|
"type": "object",
|
|
39
|
-
"required": [
|
|
62
|
+
"required": [
|
|
63
|
+
"openwop"
|
|
64
|
+
],
|
|
40
65
|
"properties": {
|
|
41
|
-
"openwop": {
|
|
66
|
+
"openwop": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"description": "Semver range — which openwop protocol versions this pack works against."
|
|
69
|
+
}
|
|
42
70
|
},
|
|
43
71
|
"additionalProperties": true
|
|
44
72
|
},
|
|
45
73
|
"dependencies": {
|
|
46
74
|
"type": "object",
|
|
47
|
-
"additionalProperties": {
|
|
75
|
+
"additionalProperties": {
|
|
76
|
+
"type": "string"
|
|
77
|
+
},
|
|
48
78
|
"description": "Other packs this pack depends on. Map of pack name → semver range."
|
|
49
79
|
},
|
|
50
80
|
"peerDependencies": {
|
|
51
81
|
"type": "object",
|
|
52
|
-
"additionalProperties": {
|
|
82
|
+
"additionalProperties": {
|
|
83
|
+
"type": "string"
|
|
84
|
+
},
|
|
53
85
|
"description": "Engine-supplied capabilities the pack consumes (e.g., `{ \"host.forms.contentPacks\": \"supported\" }`). Resolved against the host's advertised capabilities at register time."
|
|
54
86
|
},
|
|
55
87
|
"templates": {
|
|
56
88
|
"type": "array",
|
|
57
89
|
"minItems": 1,
|
|
58
90
|
"maxItems": 100,
|
|
59
|
-
"items": {
|
|
91
|
+
"items": {
|
|
92
|
+
"$ref": "#/$defs/FormTemplate"
|
|
93
|
+
},
|
|
60
94
|
"description": "Form template definitions this pack contributes. Each MUST have a unique `templateId` within the pack — duplicates MUST be refused at registry `PUT` / install (`form-content-packs.md` §\"Unique identifiers\"); JSON Schema cannot express uniqueness-by-property, so this is a normative prose requirement, as it is for every peer pack kind. `maxItems` is an OUTER resource bound against a render bomb, not product policy — a host MAY enforce a tighter limit locally."
|
|
61
95
|
},
|
|
62
|
-
"signing": {
|
|
96
|
+
"signing": {
|
|
97
|
+
"$ref": "#/$defs/Signing"
|
|
98
|
+
}
|
|
63
99
|
},
|
|
64
100
|
"$defs": {
|
|
65
101
|
"FormTemplate": {
|
|
66
102
|
"type": "object",
|
|
67
|
-
"required": [
|
|
103
|
+
"required": [
|
|
104
|
+
"templateId",
|
|
105
|
+
"version",
|
|
106
|
+
"label",
|
|
107
|
+
"title",
|
|
108
|
+
"fields"
|
|
109
|
+
],
|
|
68
110
|
"additionalProperties": false,
|
|
69
111
|
"properties": {
|
|
70
112
|
"templateId": {
|
|
@@ -105,14 +147,25 @@
|
|
|
105
147
|
"type": "array",
|
|
106
148
|
"minItems": 1,
|
|
107
149
|
"maxItems": 200,
|
|
108
|
-
"items": {
|
|
150
|
+
"items": {
|
|
151
|
+
"$ref": "#/$defs/FormField"
|
|
152
|
+
},
|
|
109
153
|
"description": "Typed fields the template contributes, in presentation order. Each MUST have a unique `id` within the template — duplicates MUST be refused (`form-content-packs.md` §\"Unique identifiers\"). Two fields sharing an `id` silently overwrite one another in the submission value bag, losing data with no error, so this is a data-integrity requirement rather than a style rule. `maxItems` is an OUTER resource bound; a host MAY enforce a tighter limit locally."
|
|
110
154
|
}
|
|
155
|
+
},
|
|
156
|
+
"patternProperties": {
|
|
157
|
+
"^(x-|vendor\\.)": {
|
|
158
|
+
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md` §\"Vendor-prefixed namespaces\", which requires a client receiving an unknown vendor-prefixed field to treat it as opaque — a field that `additionalProperties: false` previously made impossible to carry. SECURITY: an extension value is PACK-AUTHORED, therefore untrusted; \"ignore\" means ignore. See `node-packs.md` §\"Vendor extensions on pack manifests\" and the `pack-manifest-extension-opaque` invariant."
|
|
159
|
+
}
|
|
111
160
|
}
|
|
112
161
|
},
|
|
113
162
|
"FormField": {
|
|
114
163
|
"type": "object",
|
|
115
|
-
"required": [
|
|
164
|
+
"required": [
|
|
165
|
+
"id",
|
|
166
|
+
"type",
|
|
167
|
+
"label"
|
|
168
|
+
],
|
|
116
169
|
"additionalProperties": false,
|
|
117
170
|
"description": "One typed field. `id` (not `key`) matches `chat-card-pack-manifest.schema.json#/$defs/InputField` so a host renders both declarative kinds through the same machinery.",
|
|
118
171
|
"properties": {
|
|
@@ -152,7 +205,10 @@
|
|
|
152
205
|
},
|
|
153
206
|
"options": {
|
|
154
207
|
"type": "array",
|
|
155
|
-
"items": {
|
|
208
|
+
"items": {
|
|
209
|
+
"type": "string",
|
|
210
|
+
"maxLength": 200
|
|
211
|
+
},
|
|
156
212
|
"maxItems": 250,
|
|
157
213
|
"description": "Choices for `type: \"select\"` / `\"multiselect\"`. UNTRUSTED pack-authored content — see `form-content-packs.md` §\"Trust boundary\". Mirrors `chat-card-pack-manifest.schema.json` `InputField.options`."
|
|
158
214
|
}
|
|
@@ -163,10 +219,25 @@
|
|
|
163
219
|
"description": "Optional signing metadata. See node-packs.md §signing.",
|
|
164
220
|
"additionalProperties": false,
|
|
165
221
|
"properties": {
|
|
166
|
-
"publicKeyRef": {
|
|
167
|
-
|
|
168
|
-
|
|
222
|
+
"publicKeyRef": {
|
|
223
|
+
"type": "string"
|
|
224
|
+
},
|
|
225
|
+
"signatureRef": {
|
|
226
|
+
"type": "string"
|
|
227
|
+
},
|
|
228
|
+
"method": {
|
|
229
|
+
"type": "string",
|
|
230
|
+
"enum": [
|
|
231
|
+
"manual",
|
|
232
|
+
"sigstore"
|
|
233
|
+
]
|
|
234
|
+
}
|
|
169
235
|
}
|
|
170
236
|
}
|
|
237
|
+
},
|
|
238
|
+
"patternProperties": {
|
|
239
|
+
"^(x-|vendor\\.)": {
|
|
240
|
+
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md` §\"Vendor-prefixed namespaces\", which requires a client receiving an unknown vendor-prefixed field to treat it as opaque — a field that `additionalProperties: false` previously made impossible to carry. SECURITY: an extension value is PACK-AUTHORED, therefore untrusted; \"ignore\" means ignore. See `node-packs.md` §\"Vendor extensions on pack manifests\" and the `pack-manifest-extension-opaque` invariant."
|
|
241
|
+
}
|
|
171
242
|
}
|
|
172
243
|
}
|
|
@@ -51,6 +51,11 @@
|
|
|
51
51
|
"items": { "$ref": "#/$defs/uiPlugin" }
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
|
+
"patternProperties": {
|
|
55
|
+
"^(x-|vendor\\.)": {
|
|
56
|
+
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md` \u00a7\"Vendor-prefixed namespaces\", which requires a client receiving an unknown vendor-prefixed field to treat it as opaque \u2014 a field that `additionalProperties: false` previously made impossible to carry. SECURITY: an extension value is PACK-AUTHORED, therefore untrusted; \"ignore\" means ignore. See `node-packs.md` \u00a7\"Vendor extensions on pack manifests\" and the `pack-manifest-extension-opaque` invariant."
|
|
57
|
+
}
|
|
58
|
+
},
|
|
54
59
|
"$defs": {
|
|
55
60
|
"uiPlugin": {
|
|
56
61
|
"type": "object",
|
|
@@ -93,6 +98,11 @@
|
|
|
93
98
|
"items": { "type": "string", "format": "uri" },
|
|
94
99
|
"uniqueItems": true
|
|
95
100
|
}
|
|
101
|
+
},
|
|
102
|
+
"patternProperties": {
|
|
103
|
+
"^(x-|vendor\\.)": {
|
|
104
|
+
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md` \u00a7\"Vendor-prefixed namespaces\", which requires a client receiving an unknown vendor-prefixed field to treat it as opaque \u2014 a field that `additionalProperties: false` previously made impossible to carry. SECURITY: an extension value is PACK-AUTHORED, therefore untrusted; \"ignore\" means ignore. See `node-packs.md` \u00a7\"Vendor extensions on pack manifests\" and the `pack-manifest-extension-opaque` invariant."
|
|
105
|
+
}
|
|
96
106
|
}
|
|
97
107
|
}
|
|
98
108
|
}
|