@openwop/openwop-conformance 1.58.0 → 1.64.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.
@@ -4,10 +4,35 @@
4
4
  "title": "NodePackManifest",
5
5
  "description": "Manifest for a published OpenWOP node pack — `pack.json` at the pack root. Language-neutral. See node-packs.md for the canonical contract.",
6
6
  "type": "object",
7
- "required": ["name", "version", "engines", "runtime"],
7
+ "required": [
8
+ "name",
9
+ "version",
10
+ "engines",
11
+ "runtime"
12
+ ],
8
13
  "anyOf": [
9
- { "properties": { "nodes": { "type": "array", "minItems": 1 } }, "required": ["nodes"] },
10
- { "properties": { "agents": { "type": "array", "minItems": 1 } }, "required": ["agents"] }
14
+ {
15
+ "properties": {
16
+ "nodes": {
17
+ "type": "array",
18
+ "minItems": 1
19
+ }
20
+ },
21
+ "required": [
22
+ "nodes"
23
+ ]
24
+ },
25
+ {
26
+ "properties": {
27
+ "agents": {
28
+ "type": "array",
29
+ "minItems": 1
30
+ }
31
+ },
32
+ "required": [
33
+ "agents"
34
+ ]
35
+ }
11
36
  ],
12
37
  "properties": {
13
38
  "kind": {
@@ -27,19 +52,38 @@
27
52
  "description": "Pack version per Semantic Versioning 2.0.0.",
28
53
  "pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$"
29
54
  },
30
- "description": { "type": "string", "maxLength": 1024 },
31
- "author": { "type": "string" },
32
- "license": { "type": "string", "description": "SPDX license identifier (e.g., `Apache-2.0`, `MIT`)." },
33
- "homepage": { "type": "string", "format": "uri" },
34
- "repository": { "type": "string", "format": "uri" },
55
+ "description": {
56
+ "type": "string",
57
+ "maxLength": 1024
58
+ },
59
+ "author": {
60
+ "type": "string"
61
+ },
62
+ "license": {
63
+ "type": "string",
64
+ "description": "SPDX license identifier (e.g., `Apache-2.0`, `MIT`)."
65
+ },
66
+ "homepage": {
67
+ "type": "string",
68
+ "format": "uri"
69
+ },
70
+ "repository": {
71
+ "type": "string",
72
+ "format": "uri"
73
+ },
35
74
  "keywords": {
36
75
  "type": "array",
37
- "items": { "type": "string", "maxLength": 64 },
76
+ "items": {
77
+ "type": "string",
78
+ "maxLength": 64
79
+ },
38
80
  "maxItems": 50
39
81
  },
40
82
  "engines": {
41
83
  "type": "object",
42
- "required": ["openwop"],
84
+ "required": [
85
+ "openwop"
86
+ ],
43
87
  "properties": {
44
88
  "openwop": {
45
89
  "type": "string",
@@ -50,12 +94,16 @@
50
94
  },
51
95
  "dependencies": {
52
96
  "type": "object",
53
- "additionalProperties": { "type": "string" },
97
+ "additionalProperties": {
98
+ "type": "string"
99
+ },
54
100
  "description": "Other node packs this pack depends on. Map of pack name → semver range. Engine resolves transitively at workflow-register time."
55
101
  },
56
102
  "peerDependencies": {
57
103
  "type": "object",
58
- "additionalProperties": { "type": "string" },
104
+ "additionalProperties": {
105
+ "type": "string"
106
+ },
59
107
  "description": "Engine-supplied capabilities the pack consumes (e.g., a particular AI provider extension). Resolved against `Capabilities` at register time. Each entry is REQUIRED by default — an unmet entry fails install with `pack_peer_dependency_missing` — unless marked optional in `peerDependenciesMeta` (RFC 0072 §C)."
60
108
  },
61
109
  "peerDependenciesMeta": {
@@ -75,23 +123,38 @@
75
123
  },
76
124
  "nodes": {
77
125
  "type": "array",
78
- "items": { "$ref": "#/$defs/PackNode" },
126
+ "items": {
127
+ "$ref": "#/$defs/PackNode"
128
+ },
79
129
  "description": "Node typeIds the pack contributes. Each MUST have a unique `typeId` within the pack. A pack MAY omit `nodes` entirely if it ships only `agents[]` — the top-level `anyOf` constraint requires at least one of `nodes` or `agents` to be non-empty."
80
130
  },
81
131
  "agents": {
82
132
  "type": "array",
83
- "items": { "$ref": "agent-manifest.schema.json" },
133
+ "items": {
134
+ "$ref": "agent-manifest.schema.json"
135
+ },
84
136
  "description": "Optional agent manifests shipped alongside this pack. Each entry is an AgentManifest (see agent-manifest.schema.json + RFC 0003 §`agents[]` extension). Pure-agent packs MUST set `runtime.language: 'remote'` — agents are interpreted by the host, not bundled as executable artifacts. Mixed packs (nodes + agents) declare the runtime that loads the node implementations; agents remain host-interpreted."
85
137
  },
86
- "runtime": { "$ref": "#/$defs/Runtime" },
87
- "signing": { "$ref": "#/$defs/Signing" },
88
- "connector": { "$ref": "#/$defs/Connector" }
138
+ "runtime": {
139
+ "$ref": "#/$defs/Runtime"
140
+ },
141
+ "signing": {
142
+ "$ref": "#/$defs/Signing"
143
+ },
144
+ "connector": {
145
+ "$ref": "#/$defs/Connector"
146
+ }
89
147
  },
90
148
  "additionalProperties": false,
91
149
  "$defs": {
92
150
  "PackNode": {
93
151
  "type": "object",
94
- "required": ["typeId", "version", "category", "role"],
152
+ "required": [
153
+ "typeId",
154
+ "version",
155
+ "category",
156
+ "role"
157
+ ],
95
158
  "properties": {
96
159
  "typeId": {
97
160
  "type": "string",
@@ -104,11 +167,23 @@
104
167
  "type": "string",
105
168
  "description": "Per-node semver. MAY differ from the pack's overall version — useful when a single pack ships multiple nodes that evolve independently."
106
169
  },
107
- "label": { "type": "string", "minLength": 1 },
108
- "description": { "type": "string" },
170
+ "label": {
171
+ "type": "string",
172
+ "minLength": 1
173
+ },
174
+ "description": {
175
+ "type": "string"
176
+ },
109
177
  "category": {
110
178
  "type": "string",
111
- "enum": ["chat", "control", "data", "canvas", "coordination", "integration"],
179
+ "enum": [
180
+ "chat",
181
+ "control",
182
+ "data",
183
+ "canvas",
184
+ "coordination",
185
+ "integration"
186
+ ],
112
187
  "description": "Engine-side classification used for scheduling and UI grouping."
113
188
  },
114
189
  "role": {
@@ -119,7 +194,12 @@
119
194
  "type": "array",
120
195
  "items": {
121
196
  "type": "string",
122
- "enum": ["streamable", "cacheable", "side-effectful", "mcp-exportable"]
197
+ "enum": [
198
+ "streamable",
199
+ "cacheable",
200
+ "side-effectful",
201
+ "mcp-exportable"
202
+ ]
123
203
  },
124
204
  "uniqueItems": true,
125
205
  "description": "Orthogonal capability traits. The engine uses these to decide caching, MCP exposure, idempotency enforcement."
@@ -158,28 +238,47 @@
158
238
  "artifact": {
159
239
  "type": "object",
160
240
  "properties": {
161
- "typeId": { "type": "string" },
162
- "syncOn": { "type": "string", "enum": ["completion", "approval", "manual"] },
163
- "supportsCheckpoint": { "type": "boolean" }
241
+ "typeId": {
242
+ "type": "string"
243
+ },
244
+ "syncOn": {
245
+ "type": "string",
246
+ "enum": [
247
+ "completion",
248
+ "approval",
249
+ "manual"
250
+ ]
251
+ },
252
+ "supportsCheckpoint": {
253
+ "type": "boolean"
254
+ }
164
255
  },
165
256
  "additionalProperties": false
166
257
  },
167
258
  "mcp": {
168
259
  "type": "object",
169
260
  "properties": {
170
- "exposeAsTool": { "type": "boolean" },
171
- "toolName": { "type": "string" }
261
+ "exposeAsTool": {
262
+ "type": "boolean"
263
+ },
264
+ "toolName": {
265
+ "type": "string"
266
+ }
172
267
  },
173
268
  "additionalProperties": false
174
269
  },
175
270
  "requiresSecrets": {
176
271
  "type": "array",
177
- "items": { "$ref": "#/$defs/SecretRequirement" },
272
+ "items": {
273
+ "$ref": "#/$defs/SecretRequirement"
274
+ },
178
275
  "description": "Secrets the node needs to execute. Resolved by the host's secret-resolution adapter at dispatch time. Hosts that don't advertise `Capabilities.secrets.supported` MUST refuse to dispatch a node with non-empty `requiresSecrets` and return `credential_unavailable`."
179
276
  },
180
277
  "requiredCredentials": {
181
278
  "type": "array",
182
- "items": { "$ref": "#/$defs/CredentialRequirement" },
279
+ "items": {
280
+ "$ref": "#/$defs/CredentialRequirement"
281
+ },
183
282
  "description": "RFC 0046. Credentials the node needs, resolved by the host's `host.credentials` resolver at dispatch time (distinct from `requiresSecrets`, which targets the informal BYOK annex). Hosts that don't advertise `Capabilities.credentials.supported` MUST refuse to dispatch a node with non-empty `requiredCredentials` and return `credential_unavailable` (peerDependency `credentials: 'supported'`)."
184
283
  },
185
284
  "auth": {
@@ -199,7 +298,10 @@
199
298
  "fallbackModel": {
200
299
  "type": "object",
201
300
  "additionalProperties": false,
202
- "required": ["provider", "model"],
301
+ "required": [
302
+ "provider",
303
+ "model"
304
+ ],
203
305
  "description": "RFC 0031 §B. Substitute model coordinates the host MAY use if the active model lacks the declared `requiredModelCapabilities`. When the host substitutes, it MUST emit `model.capability.substituted` per RFC 0031 §D. Absent means no substitution permitted; the host MUST refuse to dispatch with `capability_not_provided` when capabilities are unmet. The fallback is single-shot — if the fallback itself fails capability checks or authentication, the host emits `model.capability.insufficient` with `fallbackAttempted: true` and refuses (no recursive fallback chains per RFC 0031 §\"Unresolved questions\" #3).",
204
306
  "properties": {
205
307
  "provider": {
@@ -215,11 +317,19 @@
215
317
  }
216
318
  }
217
319
  },
218
- "additionalProperties": false
320
+ "additionalProperties": false,
321
+ "patternProperties": {
322
+ "^(x-|vendor\\.)": {
323
+ "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."
324
+ }
325
+ }
219
326
  },
220
327
  "SecretRequirement": {
221
328
  "type": "object",
222
- "required": ["id", "kind"],
329
+ "required": [
330
+ "id",
331
+ "kind"
332
+ ],
223
333
  "description": "Declared secret requirement. The engine surfaces this to the host's `SecretResolver` which returns an opaque `ResolvedSecret` reference; the executor passes the reference to provider adapters that dereference internally. Raw key material NEVER reaches the protocol surface (events, logs, traces, etc.).",
224
334
  "properties": {
225
335
  "id": {
@@ -229,7 +339,12 @@
229
339
  },
230
340
  "kind": {
231
341
  "type": "string",
232
- "enum": ["ai-provider", "api-key", "oauth-token", "custom"],
342
+ "enum": [
343
+ "ai-provider",
344
+ "api-key",
345
+ "oauth-token",
346
+ "custom"
347
+ ],
233
348
  "description": "Coarse classification driving host-side resolution policy. `ai-provider` triggers the BYOK / aiProviders flow; `api-key` is generic; `oauth-token` integrates with the host's OAuth refresh flow; `custom` is host-extensible."
234
349
  },
235
350
  "provider": {
@@ -238,7 +353,11 @@
238
353
  },
239
354
  "scope": {
240
355
  "type": "string",
241
- "enum": ["tenant", "user", "run"],
356
+ "enum": [
357
+ "tenant",
358
+ "user",
359
+ "run"
360
+ ],
242
361
  "description": "Where the host should look up the secret. MUST match a scope in `Capabilities.secrets.scopes`. Defaults to `tenant` when omitted."
243
362
  }
244
363
  },
@@ -246,7 +365,9 @@
246
365
  },
247
366
  "CredentialRequirement": {
248
367
  "type": "object",
249
- "required": ["key"],
368
+ "required": [
369
+ "key"
370
+ ],
250
371
  "description": "RFC 0046. A credential the node needs, resolved by the host's `host.credentials` resolver into the node sandbox at dispatch time. The node declares only the requirement; raw key material NEVER reaches the protocol surface (events, logs, traces, replay) per the `credential-payload-redaction` invariant.",
251
372
  "properties": {
252
373
  "key": {
@@ -256,7 +377,11 @@
256
377
  },
257
378
  "scope": {
258
379
  "type": "string",
259
- "enum": ["user", "workspace", "tenant"],
380
+ "enum": [
381
+ "user",
382
+ "workspace",
383
+ "tenant"
384
+ ],
260
385
  "description": "Resolution scope. MUST match a scope in `Capabilities.credentials.scopes`. Defaults to the host's default scope when omitted."
261
386
  },
262
387
  "displayName": {
@@ -268,12 +393,17 @@
268
393
  },
269
394
  "NodeAuth": {
270
395
  "type": "object",
271
- "required": ["type", "provider"],
396
+ "required": [
397
+ "type",
398
+ "provider"
399
+ ],
272
400
  "description": "RFC 0047. A node's third-party authentication declaration. The node declares only which provider + scopes it needs; the host performs the OAuth dance and resolves the token in-sandbox. Raw token material NEVER reaches the protocol surface (`credential-payload-redaction` invariant).",
273
401
  "properties": {
274
402
  "type": {
275
403
  "type": "string",
276
- "enum": ["oauth2"],
404
+ "enum": [
405
+ "oauth2"
406
+ ],
277
407
  "description": "Authentication mechanism. `oauth2` routes through the `host.oauth` flow."
278
408
  },
279
409
  "provider": {
@@ -283,7 +413,9 @@
283
413
  },
284
414
  "scopes": {
285
415
  "type": "array",
286
- "items": { "type": "string" },
416
+ "items": {
417
+ "type": "string"
418
+ },
287
419
  "description": "OAuth scopes the node requires; each MUST be in the provider's advertised `scopesSupported`."
288
420
  }
289
421
  },
@@ -292,14 +424,37 @@
292
424
  "ConnectorAuth": {
293
425
  "description": "RFC 0045. The auth declaration shared by a connector's actions. Either an RFC 0047 OAuth2 declaration or an RFC 0046 stored-credential reference.",
294
426
  "oneOf": [
295
- { "$ref": "#/$defs/NodeAuth" },
427
+ {
428
+ "$ref": "#/$defs/NodeAuth"
429
+ },
296
430
  {
297
431
  "type": "object",
298
- "required": ["type", "key"],
432
+ "required": [
433
+ "type",
434
+ "key"
435
+ ],
299
436
  "properties": {
300
- "type": { "type": "string", "enum": ["credential"], "description": "Static stored-credential auth via the RFC 0046 host.credentials resolver." },
301
- "key": { "type": "string", "minLength": 1, "description": "CredentialRequirement key the host resolves into the node sandbox." },
302
- "scope": { "type": "string", "enum": ["user", "workspace", "tenant"], "description": "Resolution scope; MUST match a scope in `capabilities.credentials.scopes`." }
437
+ "type": {
438
+ "type": "string",
439
+ "enum": [
440
+ "credential"
441
+ ],
442
+ "description": "Static stored-credential auth via the RFC 0046 host.credentials resolver."
443
+ },
444
+ "key": {
445
+ "type": "string",
446
+ "minLength": 1,
447
+ "description": "CredentialRequirement key the host resolves into the node sandbox."
448
+ },
449
+ "scope": {
450
+ "type": "string",
451
+ "enum": [
452
+ "user",
453
+ "workspace",
454
+ "tenant"
455
+ ],
456
+ "description": "Resolution scope; MUST match a scope in `capabilities.credentials.scopes`."
457
+ }
303
458
  },
304
459
  "additionalProperties": false
305
460
  }
@@ -307,39 +462,77 @@
307
462
  },
308
463
  "Connector": {
309
464
  "type": "object",
310
- "required": ["id", "displayName"],
465
+ "required": [
466
+ "id",
467
+ "displayName"
468
+ ],
311
469
  "description": "RFC 0045. Declares this pack a named connector — a typed integration exposing actions (and reusing the existing trigger model). Optional; packs without it remain plain node packs. Actions are normal side-effectful nodes from this pack's `nodes[]` annotated with scheduler hints; the connector block adds metadata, not a new execution kind.",
312
470
  "properties": {
313
- "id": { "type": "string", "pattern": "^[a-z][a-z0-9.-]*$", "description": "Stable connector id, e.g. `salesforce`." },
314
- "displayName": { "type": "string", "minLength": 1 },
315
- "auth": { "$ref": "#/$defs/ConnectorAuth", "description": "RFC 0047/0046 auth declaration shared by the connector's actions." },
471
+ "id": {
472
+ "type": "string",
473
+ "pattern": "^[a-z][a-z0-9.-]*$",
474
+ "description": "Stable connector id, e.g. `salesforce`."
475
+ },
476
+ "displayName": {
477
+ "type": "string",
478
+ "minLength": 1
479
+ },
480
+ "auth": {
481
+ "$ref": "#/$defs/ConnectorAuth",
482
+ "description": "RFC 0047/0046 auth declaration shared by the connector's actions."
483
+ },
316
484
  "actions": {
317
485
  "type": "array",
318
486
  "description": "Typed actions the connector exposes. Each `typeId` MUST resolve to a node typeId defined in this pack's `nodes[]` (validation error `connector_action_unresolved` otherwise).",
319
487
  "items": {
320
488
  "type": "object",
321
- "required": ["typeId", "displayName"],
489
+ "required": [
490
+ "typeId",
491
+ "displayName"
492
+ ],
322
493
  "properties": {
323
- "typeId": { "type": "string", "minLength": 1, "description": "MUST match a `nodes[].typeId` in this manifest." },
324
- "displayName": { "type": "string", "minLength": 1 },
325
- "idempotent": { "type": "boolean", "description": "Action is safe to auto-retry without an idempotency key. Absent/false ⇒ the host MUST NOT auto-retry without one (composes with idempotency.md)." },
494
+ "typeId": {
495
+ "type": "string",
496
+ "minLength": 1,
497
+ "description": "MUST match a `nodes[].typeId` in this manifest."
498
+ },
499
+ "displayName": {
500
+ "type": "string",
501
+ "minLength": 1
502
+ },
503
+ "idempotent": {
504
+ "type": "boolean",
505
+ "description": "Action is safe to auto-retry without an idempotency key. Absent/false ⇒ the host MUST NOT auto-retry without one (composes with idempotency.md)."
506
+ },
326
507
  "rateLimit": {
327
508
  "type": "object",
328
509
  "properties": {
329
- "requests": { "type": "integer", "minimum": 1 },
330
- "perSeconds": { "type": "integer", "minimum": 1 }
510
+ "requests": {
511
+ "type": "integer",
512
+ "minimum": 1
513
+ },
514
+ "perSeconds": {
515
+ "type": "integer",
516
+ "minimum": 1
517
+ }
331
518
  },
332
519
  "additionalProperties": false,
333
520
  "description": "Advertised rate-limit hint the host scheduler SHOULD honor. Does not change the node's wire shape."
334
521
  },
335
- "paginated": { "type": "boolean", "description": "Action returns paginated results." }
522
+ "paginated": {
523
+ "type": "boolean",
524
+ "description": "Action returns paginated results."
525
+ }
336
526
  },
337
527
  "additionalProperties": false
338
528
  }
339
529
  },
340
530
  "triggers": {
341
531
  "type": "array",
342
- "items": { "type": "string", "minLength": 1 },
532
+ "items": {
533
+ "type": "string",
534
+ "minLength": 1
535
+ },
343
536
  "description": "typeIds of triggers (from the existing trigger model) this connector exposes. Each MUST resolve to a node/trigger typeId in this pack."
344
537
  }
345
538
  },
@@ -347,11 +540,21 @@
347
540
  },
348
541
  "Runtime": {
349
542
  "type": "object",
350
- "required": ["language", "entry"],
543
+ "required": [
544
+ "language",
545
+ "entry"
546
+ ],
351
547
  "properties": {
352
548
  "language": {
353
549
  "type": "string",
354
- "enum": ["javascript", "python", "go", "wasm", "wasm-component", "remote"],
550
+ "enum": [
551
+ "javascript",
552
+ "python",
553
+ "go",
554
+ "wasm",
555
+ "wasm-component",
556
+ "remote"
557
+ ],
355
558
  "description": "How the engine loads the pack. See node-packs.md §runtime formats. `wasm` is the core-module WASM ABI (RFC 0008). `wasm-component` is the WASM Component Model variant (WIT-defined interfaces); hosts that advertise `capabilities.nodePackRuntimes.wasmComponent.supported: true` load it via wasmtime / wasmer Component Model runtimes. Both share the openwop ABI envelope; the wasm-component variant uses WIT interfaces instead of hand-rolled imports/exports."
356
559
  },
357
560
  "entry": {
@@ -360,7 +563,15 @@
360
563
  },
361
564
  "format": {
362
565
  "type": "string",
363
- "enum": ["esm", "cjs", "wheel", "binary", "shared-library", "wasm", "wasm-component"],
566
+ "enum": [
567
+ "esm",
568
+ "cjs",
569
+ "wheel",
570
+ "binary",
571
+ "shared-library",
572
+ "wasm",
573
+ "wasm-component"
574
+ ],
364
575
  "description": "Runtime-specific format hint. `esm`/`cjs` for JS, `wheel` for Python, `binary`/`shared-library` for Go, `wasm` for core-module WASM (RFC 0008), `wasm-component` for WASM Component Model preview 3+ (WIT-defined interfaces, additive RFC 0008.1)."
365
576
  },
366
577
  "minRuntimeVersion": {
@@ -373,14 +584,38 @@
373
584
  "description": "RFC 0076. Abstract platform primitives the pack's runtime code exercises, for install-time sandbox gating. Runtime-agnostic (not language builtin names). Absent or [] ⇒ no elevated platform needs. A sandbox host MUST evaluate this at install time and refuse (`pack_runtime_requirement_unmet`) any primitive it will not grant; see node-packs.md §\"Runtime platform requirements\".",
374
585
  "items": {
375
586
  "oneOf": [
376
- { "const": "net.dns", "description": "Resolves hostnames (e.g., SSRF pre-flight)." },
377
- { "const": "net.outbound", "description": "Opens outbound network connections / fetch." },
378
- { "const": "crypto", "description": "Primitives beyond the standard hashing the host already provides." },
379
- { "const": "subprocess", "description": "Spawns a child process (composes with the RFC 0069 exec-class contract when the host advertises it)." },
380
- { "const": "fs.read", "description": "Reads the local filesystem." },
381
- { "const": "fs.write", "description": "Writes the local filesystem." },
382
- { "const": "env.read", "description": "Reads the process environment (may expose deployment secrets if the host does not scrub it)." },
383
- { "const": "clock", "description": "Reads wall-clock time as a behavioral input — gated for REPLAY determinism, not access control. A pack that branches on the clock is non-deterministic on replay (replay.md)." }
587
+ {
588
+ "const": "net.dns",
589
+ "description": "Resolves hostnames (e.g., SSRF pre-flight)."
590
+ },
591
+ {
592
+ "const": "net.outbound",
593
+ "description": "Opens outbound network connections / fetch."
594
+ },
595
+ {
596
+ "const": "crypto",
597
+ "description": "Primitives beyond the standard hashing the host already provides."
598
+ },
599
+ {
600
+ "const": "subprocess",
601
+ "description": "Spawns a child process (composes with the RFC 0069 exec-class contract when the host advertises it)."
602
+ },
603
+ {
604
+ "const": "fs.read",
605
+ "description": "Reads the local filesystem."
606
+ },
607
+ {
608
+ "const": "fs.write",
609
+ "description": "Writes the local filesystem."
610
+ },
611
+ {
612
+ "const": "env.read",
613
+ "description": "Reads the process environment (may expose deployment secrets if the host does not scrub it)."
614
+ },
615
+ {
616
+ "const": "clock",
617
+ "description": "Reads wall-clock time as a behavioral input — gated for REPLAY determinism, not access control. A pack that branches on the clock is non-deterministic on replay (replay.md)."
618
+ }
384
619
  ]
385
620
  }
386
621
  }
@@ -401,11 +636,19 @@
401
636
  },
402
637
  "method": {
403
638
  "type": "string",
404
- "enum": ["manual", "sigstore"],
639
+ "enum": [
640
+ "manual",
641
+ "sigstore"
642
+ ],
405
643
  "description": "Signing method. `manual` uses publicKeyRef + signatureRef; `sigstore` uses a Sigstore bundle at `pack.json.sigstore`."
406
644
  }
407
645
  },
408
646
  "additionalProperties": false
409
647
  }
648
+ },
649
+ "patternProperties": {
650
+ "^(x-|vendor\\.)": {
651
+ "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."
652
+ }
410
653
  }
411
654
  }