@openwop/openwop-conformance 1.0.0 → 1.1.1

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 (86) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +31 -6
  3. package/api/grpc/openwop.proto +251 -0
  4. package/api/openapi.yaml +109 -3
  5. package/coverage.md +48 -9
  6. package/fixtures/conformance-configurable-schema.json +39 -0
  7. package/fixtures/conformance-subworkflow-parent.json +1 -1
  8. package/fixtures/conformance-wasm-pack-memory-cap-breach.json +23 -0
  9. package/fixtures/openwop-smoke-byok-roundtrip.json +25 -0
  10. package/fixtures.md +21 -0
  11. package/package.json +3 -1
  12. package/schemas/README.md +4 -0
  13. package/schemas/audit-verify-result.schema.json +90 -0
  14. package/schemas/capabilities.schema.json +342 -1
  15. package/schemas/node-pack-manifest.schema.json +4 -4
  16. package/schemas/pack-lockfile.schema.json +92 -0
  17. package/schemas/registry-version-manifest.schema.json +145 -0
  18. package/schemas/run-event-payloads.schema.json +20 -4
  19. package/schemas/run-event.schema.json +2 -1
  20. package/schemas/security-advisory.schema.json +109 -0
  21. package/src/lib/a2a-fake-peer.ts +143 -56
  22. package/src/lib/behavior-gate.ts +107 -0
  23. package/src/lib/env.ts +37 -0
  24. package/src/lib/grpc-framing.test.ts +96 -0
  25. package/src/lib/grpc-framing.ts +76 -0
  26. package/src/lib/oidc-issuer.test.ts +328 -0
  27. package/src/lib/oidc-issuer.ts +241 -0
  28. package/src/lib/otel-collector-grpc.test.ts +191 -0
  29. package/src/lib/otel-collector.test.ts +303 -0
  30. package/src/lib/otel-collector.ts +318 -14
  31. package/src/lib/otlp-protobuf.test.ts +461 -0
  32. package/src/lib/otlp-protobuf.ts +529 -0
  33. package/src/scenarios/a2a-task-roundtrip.test.ts +147 -28
  34. package/src/scenarios/agentConfidenceEscalation.test.ts +1 -0
  35. package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +1 -0
  36. package/src/scenarios/agentMemoryRedactionContract.test.ts +1 -0
  37. package/src/scenarios/agentMemoryRoundTrip.test.ts +1 -0
  38. package/src/scenarios/agentMemoryTtlExpiry.test.ts +1 -0
  39. package/src/scenarios/agentMessageReducer.test.ts +1 -0
  40. package/src/scenarios/agentMetadata.test.ts +1 -0
  41. package/src/scenarios/agentPackExport.test.ts +1 -0
  42. package/src/scenarios/agentPackInstall.test.ts +1 -0
  43. package/src/scenarios/agentPackProvenance.test.ts +1 -0
  44. package/src/scenarios/audit-log-integrity.test.ts +3 -6
  45. package/src/scenarios/auth-api-key-rotation.test.ts +182 -0
  46. package/src/scenarios/auth-mtls.test.ts +274 -0
  47. package/src/scenarios/auth-oauth2-client-credentials.test.ts +259 -0
  48. package/src/scenarios/auth-oidc-user-bearer.test.ts +361 -0
  49. package/src/scenarios/bulk-cancel.test.ts +111 -0
  50. package/src/scenarios/configurable-schema.test.ts +48 -0
  51. package/src/scenarios/conversationCapabilityNegotiation.test.ts +1 -0
  52. package/src/scenarios/conversationLifecycle.test.ts +1 -0
  53. package/src/scenarios/conversationReplayDeterminism.test.ts +1 -0
  54. package/src/scenarios/conversationVsLegacySuspend.test.ts +1 -0
  55. package/src/scenarios/debug-bundle-truncation.test.ts +95 -0
  56. package/src/scenarios/discovery.test.ts +183 -0
  57. package/src/scenarios/http-client-ssrf.test.ts +71 -0
  58. package/src/scenarios/idempotency.test.ts +6 -0
  59. package/src/scenarios/idempotencyRetry.test.ts +3 -0
  60. package/src/scenarios/mcp-tool-roundtrip.test.ts +205 -34
  61. package/src/scenarios/mcp-toolcall-redaction.test.ts +66 -0
  62. package/src/scenarios/memory-compaction-event-emitted.test.ts +121 -0
  63. package/src/scenarios/memory-compaction-provenance-tag.test.ts +116 -0
  64. package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +127 -0
  65. package/src/scenarios/metric-emission.test.ts +113 -0
  66. package/src/scenarios/multi-region-idempotency.test.ts +39 -4
  67. package/src/scenarios/orchestratorConservativePath.test.ts +1 -0
  68. package/src/scenarios/orchestratorDispatch.test.ts +1 -0
  69. package/src/scenarios/orchestratorTermination.test.ts +1 -0
  70. package/src/scenarios/otel-emission-grpc.test.ts +98 -0
  71. package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +139 -0
  72. package/src/scenarios/pause-resume.test.ts +119 -0
  73. package/src/scenarios/production-backpressure.test.ts +342 -0
  74. package/src/scenarios/production-retention-expiry.test.ts +164 -0
  75. package/src/scenarios/registry-public.test.ts +222 -0
  76. package/src/scenarios/replay-llm-cache-key.test.ts +35 -0
  77. package/src/scenarios/replay-retention-expiry.test.ts +178 -0
  78. package/src/scenarios/restart-during-run.test.ts +177 -0
  79. package/src/scenarios/spec-corpus-validity.test.ts +59 -26
  80. package/src/scenarios/staleClaim.test.ts +3 -0
  81. package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +67 -10
  82. package/src/scenarios/wasm-pack-memory-cap.test.ts +64 -9
  83. package/src/scenarios/webhook-negative.test.ts +90 -0
  84. package/src/scenarios/webhook-signed-delivery.test.ts +178 -0
  85. package/src/setup.ts +25 -1
  86. package/vitest.config.ts +5 -1
@@ -0,0 +1,92 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/pack-lockfile.schema.json",
4
+ "title": "PackLockfile",
5
+ "description": "Reproducible-build lockfile pinning resolved versions of every pack a workspace depends on. Per spec/v1/node-packs.md §\"Dependency resolution + lockfile\". A workspace records its lockfile alongside its workflow definitions; on subsequent installs the registry resolver MUST honor the lockfile's exact versions rather than re-running semver resolution. This guarantees that a workflow that ran against `vendor.openwop.rust-hello@1.0.0` will still run against `1.0.0` even after `1.0.1` is published.",
6
+ "type": "object",
7
+ "required": ["lockfileVersion", "generatedAt", "packs"],
8
+ "properties": {
9
+ "lockfileVersion": {
10
+ "type": "integer",
11
+ "minimum": 1,
12
+ "description": "Lockfile schema version. Bumped when the lockfile structure changes incompatibly. v1.0 ships at lockfileVersion 1."
13
+ },
14
+ "generatedAt": {
15
+ "type": "string",
16
+ "format": "date-time",
17
+ "description": "ISO 8601 timestamp the lockfile was written. Used for audit + cache-bust diagnostics; resolvers MUST NOT use this for resolution decisions."
18
+ },
19
+ "registry": {
20
+ "type": "string",
21
+ "format": "uri",
22
+ "description": "Optional source-of-truth registry URL the resolved versions were fetched from (e.g., `https://packs.openwop.dev`). Multi-registry workspaces include this for audit. Single-registry workspaces MAY omit."
23
+ },
24
+ "packs": {
25
+ "type": "array",
26
+ "description": "Resolved pack records, one per pack referenced (transitively) by the workspace's workflow definitions. Order is informational only; resolvers MUST NOT rely on order. Empty arrays are allowed (a workspace with no packs).",
27
+ "items": { "$ref": "#/$defs/ResolvedPack" }
28
+ }
29
+ },
30
+ "additionalProperties": false,
31
+ "$defs": {
32
+ "ResolvedPack": {
33
+ "type": "object",
34
+ "required": ["name", "version", "integrity"],
35
+ "properties": {
36
+ "name": {
37
+ "type": "string",
38
+ "minLength": 3,
39
+ "maxLength": 256,
40
+ "pattern": "^(core|vendor|community|private|local)\\.[a-z0-9][a-z0-9.-]*$",
41
+ "description": "Pack name per node-packs.md §\"Manifest format\" `name`. Reverse-DNS-style; namespace prefix is one of the canonical tiers."
42
+ },
43
+ "version": {
44
+ "type": "string",
45
+ "minLength": 1,
46
+ "maxLength": 64,
47
+ "description": "Resolved exact version (no ranges). Semver MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]. The resolver MUST pin to this exact version on subsequent installs."
48
+ },
49
+ "resolved": {
50
+ "type": "string",
51
+ "format": "uri",
52
+ "description": "Optional canonical URL the tarball was fetched from. Audit aid; resolvers MUST verify the `integrity` field even if this URL is present."
53
+ },
54
+ "integrity": {
55
+ "type": "string",
56
+ "pattern": "^sha256-[A-Za-z0-9+/]{43}=$",
57
+ "description": "Subresource-integrity-style hash of the tarball: literal `sha256-` followed by 43-char base64-encoded SHA-256 digest with trailing `=` padding. Resolvers MUST verify the downloaded tarball's SHA-256 matches before extraction. Hash mismatch MUST fail the install with `pack_integrity_mismatch`."
58
+ },
59
+ "signature": {
60
+ "type": "object",
61
+ "description": "Optional pack signature material (Ed25519 per node-packs.md §Signing). When present, resolvers MUST verify the signature against the publisher's advertised public key.",
62
+ "required": ["algorithm", "publicKey", "value"],
63
+ "properties": {
64
+ "algorithm": { "type": "string", "enum": ["ed25519"] },
65
+ "publicKey": { "type": "string", "minLength": 1, "description": "Base64-encoded Ed25519 public key bytes." },
66
+ "value": { "type": "string", "minLength": 1, "description": "Base64-encoded Ed25519 signature over the tarball." }
67
+ },
68
+ "additionalProperties": false
69
+ },
70
+ "dependencies": {
71
+ "type": "object",
72
+ "additionalProperties": {
73
+ "type": "string",
74
+ "minLength": 1,
75
+ "maxLength": 64
76
+ },
77
+ "description": "Pinned versions for this pack's `dependencies` (per its manifest). Each key is a pack name; each value is the resolved exact version. Pinned recursively — a transitively-installed pack appears once in the top-level `packs[]` array AND as a value in each parent's `dependencies` map."
78
+ },
79
+ "peerDependencies": {
80
+ "type": "object",
81
+ "additionalProperties": {
82
+ "type": "string",
83
+ "minLength": 1,
84
+ "maxLength": 64
85
+ },
86
+ "description": "Echo of this pack's manifest `peerDependencies` for audit. Resolvers MUST verify that the host's `/.well-known/openwop` advertises every key listed here (per host-capabilities.md §\"Capability negotiation\"). Lockfile records the host-capability values the workspace committed to at lockfile-write time; mismatches at install time MAY warn (host advertises richer set) but MUST fail if a peer-dep is missing."
87
+ }
88
+ },
89
+ "additionalProperties": false
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,145 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/registry-version-manifest.schema.json",
4
+ "title": "RegistryVersionManifest",
5
+ "description": "Registry-augmented version manifest served at `/v1/packs/{name}/-/{version}.json`. Extends the bare pack manifest (`node-pack-manifest.schema.json`) with registry-side metadata (integrity hash, publishedAt timestamp, signed-blob URLs, lifecycle flags). This is the shape clients receive when they GET a specific pack version; the bare manifest is what publishers commit inside the tarball.\n\n**Why two schemas?** The bare manifest is the authoring contract — what a vendor writes in their pack source tree. The registry-augmented version is the SERVING contract — what `packs.openwop.dev` produces after `build-index.mjs` computes integrity hashes + adds URL templates. Validating both surfaces independently catches drift between authored content and served metadata.",
6
+ "type": "object",
7
+ "required": ["name", "version", "engines", "runtime", "integrity"],
8
+ "anyOf": [
9
+ { "properties": { "nodes": { "type": "array", "minItems": 1 } }, "required": ["nodes"] },
10
+ { "properties": { "agents": { "type": "array", "minItems": 1 } }, "required": ["agents"] }
11
+ ],
12
+ "properties": {
13
+ "name": {
14
+ "type": "string",
15
+ "description": "Reverse-DNS pack name. Mirrors `node-pack-manifest.schema.json` §name.",
16
+ "pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$",
17
+ "minLength": 1,
18
+ "maxLength": 256
19
+ },
20
+ "version": {
21
+ "type": "string",
22
+ "description": "Pack version per SemVer 2.0.0. Must match the URL path the manifest is served at.",
23
+ "pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$"
24
+ },
25
+ "description": { "type": "string", "maxLength": 1024 },
26
+ "author": { "type": "string" },
27
+ "license": { "type": "string" },
28
+ "homepage": { "type": "string", "format": "uri" },
29
+ "repository": { "type": "string", "format": "uri" },
30
+ "keywords": {
31
+ "type": "array",
32
+ "items": { "type": "string", "maxLength": 64 },
33
+ "maxItems": 50
34
+ },
35
+ "engines": {
36
+ "type": "object",
37
+ "required": ["openwop"],
38
+ "properties": {
39
+ "openwop": { "type": "string", "minLength": 1 }
40
+ },
41
+ "additionalProperties": { "type": "string" }
42
+ },
43
+ "runtime": {
44
+ "type": "object",
45
+ "required": ["language"],
46
+ "properties": {
47
+ "language": {
48
+ "type": "string",
49
+ "enum": ["javascript", "typescript", "wasm", "remote"],
50
+ "description": "Pack runtime. `javascript`/`typescript` for in-process Node executors; `wasm` for sandboxed WASM (RFC 0008 ABI); `remote` for agent-only packs that dispatch via remote LLM providers + don't ship local executor bytecode."
51
+ },
52
+ "entry": { "type": "string" },
53
+ "abiVersion": { "type": "string" }
54
+ },
55
+ "additionalProperties": true
56
+ },
57
+ "peerDependencies": {
58
+ "type": "object",
59
+ "description": "Host capabilities the pack consumes. Each key is a host-capability identifier (e.g., `host.canvas`); each value declares the support level the pack expects.",
60
+ "additionalProperties": { "type": "string" }
61
+ },
62
+ "dependencies": {
63
+ "type": "object",
64
+ "description": "Pack dependencies (other packs). Wire-locked at install time per `pack-lockfile.schema.json`.",
65
+ "additionalProperties": { "type": "string" }
66
+ },
67
+ "signing": {
68
+ "type": "object",
69
+ "description": "Embedded signing metadata. The pack's signing key MUST be one of the keys registered in `.well-known/openwop-registry.json` `signingKeys[]` AND MUST be authorized for the pack's namespace via `permittedNamespaces`.\n\nTwo identifier forms in the wild:\n - `keyId`: canonical (newer packs). Matches an entry in registry's `signingKeys[].keyId`.\n - `publicKeyRef`: legacy alias of `keyId` (used by `build-pack-tarball.mjs --key-id <id>`). Equivalent semantics.\n\nAt least one of `keyId` / `publicKeyRef` MUST be present; if both, they MUST be equal. The schema's `oneOf` block enforces this.",
70
+ "required": ["method"],
71
+ "oneOf": [
72
+ { "required": ["keyId"] },
73
+ { "required": ["publicKeyRef"] }
74
+ ],
75
+ "properties": {
76
+ "method": {
77
+ "type": "string",
78
+ "enum": ["manual", "ed25519", "sigstore"],
79
+ "description": "Signing method. `manual` and `ed25519` both denote a detached Ed25519 signature over canonical-JSON pack.json (synonymous in practice; both forms appear in published packs). `sigstore` reserved for the SLSA-friendly OIDC path."
80
+ },
81
+ "keyId": { "type": "string", "minLength": 1 },
82
+ "publicKeyRef": { "type": "string", "minLength": 1 },
83
+ "publicKeyUrl": { "type": "string", "format": "uri-reference" },
84
+ "signatureRef": { "type": "string" }
85
+ },
86
+ "additionalProperties": true
87
+ },
88
+ "nodes": {
89
+ "type": "array",
90
+ "items": {
91
+ "type": "object",
92
+ "required": ["typeId", "version", "category", "role"],
93
+ "additionalProperties": true
94
+ }
95
+ },
96
+ "agents": {
97
+ "type": "array",
98
+ "items": { "type": "object", "additionalProperties": true }
99
+ },
100
+ "integrity": {
101
+ "type": "string",
102
+ "description": "SRI-style integrity hash of the SIGNED tarball (`sha256-<base64(sha256)>`). Computed by `registry/scripts/build-index.mjs` from the on-disk `.tgz`. Consumers MUST verify the fetched tarball's hash matches this field before unpacking.",
103
+ "pattern": "^sha256-[A-Za-z0-9+/=]+$"
104
+ },
105
+ "publishedAt": {
106
+ "type": "string",
107
+ "description": "ISO-8601 UTC timestamp of first publish. Immutable per spec — set at first publish, never re-issued on subsequent index rebuilds.",
108
+ "format": "date-time"
109
+ },
110
+ "tarballUrl": {
111
+ "type": "string",
112
+ "description": "Registry-relative URL for the signed tarball. Always `/v1/packs/{name}/-/{version}.tgz`.",
113
+ "pattern": "^/v1/packs/[^/]+/-/[^/]+\\.tgz$"
114
+ },
115
+ "signatureUrl": {
116
+ "type": "string",
117
+ "description": "Registry-relative URL for the detached Ed25519 signature. Always `/v1/packs/{name}/-/{version}.sig`.",
118
+ "pattern": "^/v1/packs/[^/]+/-/[^/]+\\.sig$"
119
+ },
120
+ "deprecated": {
121
+ "type": "boolean",
122
+ "description": "Advisory: this version is deprecated. Consumers MAY refuse to install but registry continues to serve."
123
+ },
124
+ "yanked": {
125
+ "type": "boolean",
126
+ "description": "Hard refusal: this version was published in error. Registry MUST refuse to serve the tarball; consumers MUST refuse to dispatch nodes from yanked versions."
127
+ },
128
+ "yankedReason": {
129
+ "type": "string",
130
+ "maxLength": 512,
131
+ "description": "Optional one-line rationale shown by clients when a yanked install is attempted. Conventionally references the advisory id (`OPENWOP-YYYY-NNNN`) when one was issued."
132
+ },
133
+ "deprecationReason": {
134
+ "type": "string",
135
+ "maxLength": 512,
136
+ "description": "Optional one-line rationale shown by clients when an install of a deprecated version is attempted. Typically names the replacement version + migration link."
137
+ },
138
+ "advisoryUrl": {
139
+ "type": "string",
140
+ "format": "uri",
141
+ "description": "Optional URL with deprecation / yank rationale + remediation."
142
+ }
143
+ },
144
+ "additionalProperties": false
145
+ }
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://openwop.dev/spec/v1/run-event-payloads.schema.json",
4
4
  "title": "RunEventPayloads",
5
- "description": "Per-RunEventType payload schemas. The base RunEventDoc shape (run-event.schema.json) leaves `payload` permissive for forward-compat. This schema defines the canonical payload contract for each known RunEventType. Consumers MAY pin strict payload validation via `$defs.<typeId>` and `ajv.validate(schema.$defs[event.type], event.payload)`. Unknown event types MUST be tolerated (no $defs match → fold best-effort).\n\n47 variants from `run-event.schema.json#$defs.RunEventType` are covered, grouped into ~20 shape families with shared $defs. Naming convention: camelCase keys mirror dotted RunEventType names (e.g., `run.started` → `runStarted`).",
5
+ "description": "Per-RunEventType payload schemas. The base RunEventDoc shape (run-event.schema.json) leaves `payload` permissive for forward-compat. This schema defines the canonical payload contract for each known RunEventType. Consumers MAY pin strict payload validation via `$defs.<typeId>` and `ajv.validate(schema.$defs[event.type], event.payload)`. Unknown event types MUST be tolerated (no $defs match → fold best-effort).\n\n48 variants from `run-event.schema.json#$defs.RunEventType` are covered, grouped into ~20 shape families with shared $defs. Naming convention: camelCase keys mirror dotted RunEventType names (e.g., `run.started` → `runStarted`).",
6
6
  "type": "object",
7
7
  "$defs": {
8
8
  "_typeIndex": {
@@ -56,7 +56,8 @@
56
56
  "runOrchestrator.decided": { "$ref": "#/$defs/runOrchestratorDecided" },
57
57
  "conversation.opened": { "$ref": "#/$defs/conversationOpened" },
58
58
  "conversation.exchanged": { "$ref": "#/$defs/conversationExchanged" },
59
- "conversation.closed": { "$ref": "#/$defs/conversationClosed" }
59
+ "conversation.closed": { "$ref": "#/$defs/conversationClosed" },
60
+ "memory.compacted": { "$ref": "#/$defs/memoryCompacted" }
60
61
  }
61
62
  },
62
63
 
@@ -501,10 +502,10 @@
501
502
 
502
503
  "capBreached": {
503
504
  "type": "object",
504
- "description": "Emitted when a CapabilityLimit is exceeded (clarificationRounds / schemaRounds / envelopesPerTurn / maxNodeExecutions).",
505
+ "description": "Emitted when a CapabilityLimit is exceeded. Protocol-level limits use the four engine kinds (clarificationRounds / schemaRounds / envelopesPerTurn / maxNodeExecutions). RFC 0008 §K WASM-runtime caps use the wasm-* kinds (memory ceiling, fuel exhaustion, execution-time wall-clock).",
505
506
  "required": ["kind", "limit", "observed"],
506
507
  "properties": {
507
- "kind": { "type": "string", "enum": ["clarification", "schema", "envelopes", "node-executions"] },
508
+ "kind": { "type": "string", "enum": ["clarification", "schema", "envelopes", "node-executions", "wasm-memory", "wasm-fuel", "wasm-execution-time"] },
508
509
  "limit": { "type": "integer", "minimum": 0 },
509
510
  "observed": { "type": "integer", "minimum": 0 },
510
511
  "nodeId": { "type": "string" }
@@ -658,6 +659,21 @@
658
659
  "turnCount": { "type": "integer", "minimum": 0, "description": "Total turns completed in this conversation (number of `conversation.exchanged` events emitted)." }
659
660
  },
660
661
  "additionalProperties": true
662
+ },
663
+
664
+ "memoryCompacted": {
665
+ "type": "object",
666
+ "description": "RFC 0012 (Memory Compaction Profile). Emitted by a host advertising `capabilities.memory.compaction.supported: true` each time a compaction run completes, regardless of trigger. `outputId` MUST be readable via `MemoryAdapter.get(memoryRef, outputId)` until normal TTL eviction. SR-1 carry-forward (RFC 0012 §D) applies: the host MUST route compacted entry content through the same BYOK redaction harness applied to a fresh `put`.",
667
+ "required": ["memoryRef", "outputId", "sourceCount", "trigger", "byteSize"],
668
+ "properties": {
669
+ "memoryRef": { "type": "string", "minLength": 1, "description": "Opaque MemoryAdapter handle per RFC 0004 §C. Bounds compaction to a single memory scope." },
670
+ "outputId": { "type": "string", "minLength": 1, "description": "Id of the new MemoryEntry created by compaction." },
671
+ "sourceIds": { "type": "array", "items": { "type": "string", "minLength": 1 }, "description": "Ids of entries collapsed into outputId. Hosts MAY omit when sourceCount > 100; the `compacted-from:<runId>` tag convention (RFC 0012 §C) becomes the authoritative provenance signal. When present, MUST be exhaustive within the array (no 'and N more' semantics)." },
672
+ "sourceCount": { "type": "integer", "minimum": 1, "description": "Total source entries collapsed, including any not enumerated in `sourceIds`." },
673
+ "trigger": { "type": "string", "enum": ["host-managed", "client-requested", "both"], "description": "Matches `capabilities.memory.compaction.trigger`; indicates which trigger path drove this run. v1.x normates only `host-managed`." },
674
+ "byteSize": { "type": "integer", "minimum": 0, "description": "Byte size of the resulting MemoryEntry.content." }
675
+ },
676
+ "additionalProperties": true
661
677
  }
662
678
  }
663
679
  }
@@ -109,7 +109,8 @@
109
109
  "runOrchestrator.decided",
110
110
  "conversation.opened",
111
111
  "conversation.exchanged",
112
- "conversation.closed"
112
+ "conversation.closed",
113
+ "memory.compacted"
113
114
  ]
114
115
  }
115
116
  }
@@ -0,0 +1,109 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/security-advisory.schema.json",
4
+ "title": "SecurityAdvisory",
5
+ "description": "Registry-owned security advisory for a published OpenWOP pack version. Sourced from registry/security/advisories.json. Every entry MUST be reviewed + merged via maintainer PR; the CI check-advisories gate refuses to deploy if any active advisory targets a pack version that is not already yanked.",
6
+ "type": "object",
7
+ "required": ["id", "publishedAt", "severity", "summary", "affected"],
8
+ "properties": {
9
+ "id": {
10
+ "type": "string",
11
+ "description": "Stable advisory identifier. Format: `OPENWOP-YYYY-NNNN` (sequential, assigned by maintainers at PR merge time). MAY also include an aliased CVE ID via `cveIds[]`.",
12
+ "pattern": "^OPENWOP-[0-9]{4}-[0-9]{4}$"
13
+ },
14
+ "cveIds": {
15
+ "type": "array",
16
+ "description": "Aliased CVE identifiers if a CVE was reserved through Mitre or a CNA. Listed in cve.mitre.org format.",
17
+ "items": {
18
+ "type": "string",
19
+ "pattern": "^CVE-[0-9]{4}-[0-9]+$"
20
+ },
21
+ "uniqueItems": true
22
+ },
23
+ "publishedAt": {
24
+ "type": "string",
25
+ "format": "date",
26
+ "description": "Date the advisory was first published (ISO 8601 yyyy-mm-dd). Frozen at merge time; do not update on revision."
27
+ },
28
+ "updatedAt": {
29
+ "type": "string",
30
+ "format": "date",
31
+ "description": "Date of last advisory update. Bump when severity, affected, or fixedIn changes."
32
+ },
33
+ "severity": {
34
+ "type": "string",
35
+ "enum": ["critical", "high", "medium", "low"],
36
+ "description": "Severity per the rubric in docs/runbooks/INCIDENT-RESPONSE.md. critical=S0 (yank immediately), high=S1, medium=S2, low=S3."
37
+ },
38
+ "cvssV4Score": {
39
+ "type": "number",
40
+ "minimum": 0,
41
+ "maximum": 10,
42
+ "description": "Optional CVSS 4.0 base score. Informational; severity field is the authoritative classification."
43
+ },
44
+ "summary": {
45
+ "type": "string",
46
+ "description": "One-line human-readable summary of the vulnerability. Surfaces in tooling output.",
47
+ "minLength": 1,
48
+ "maxLength": 200
49
+ },
50
+ "details": {
51
+ "type": "string",
52
+ "description": "Long-form description: vulnerability mechanism, attack vector, impact, recommended consumer action. May reference external URLs via advisoryUrl."
53
+ },
54
+ "advisoryUrl": {
55
+ "type": "string",
56
+ "format": "uri",
57
+ "description": "External advisory link (e.g., GitHub Security Advisory, vendor security page, CVE detail page)."
58
+ },
59
+ "affected": {
60
+ "type": "array",
61
+ "description": "Pack versions impacted by this advisory. Each entry pins a pack name + a SemVer range. The check-advisories CI gate cross-references this list against every published version and fails if any non-yanked version matches.",
62
+ "minItems": 1,
63
+ "items": {
64
+ "type": "object",
65
+ "required": ["packName", "versions"],
66
+ "properties": {
67
+ "packName": {
68
+ "type": "string",
69
+ "description": "Pack name (e.g., `vendor.myndhyve.ai`). MUST exist under registry/v1/packs/.",
70
+ "pattern": "^[a-z][a-z0-9-]*(\\.[a-z][a-z0-9-]*)+$"
71
+ },
72
+ "versions": {
73
+ "type": "string",
74
+ "description": "SemVer range using node-semver syntax (`<1.0.0`, `>=1.0.0 <1.0.3`, `1.0.0 || 1.0.1`). Matched against published version numbers via the check-advisories gate."
75
+ },
76
+ "fixedIn": {
77
+ "type": "string",
78
+ "description": "Earliest version that contains the fix. Consumers MUST upgrade to >= this version. Absent if no fix is available yet (defense-only).",
79
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$"
80
+ },
81
+ "fixedInRef": {
82
+ "type": "string",
83
+ "description": "PR or commit reference where the fix landed (e.g., `openwop/openwop#42`). Helps consumers audit the patch."
84
+ }
85
+ },
86
+ "additionalProperties": false
87
+ }
88
+ },
89
+ "credits": {
90
+ "type": "array",
91
+ "description": "Acknowledged reporters or researchers.",
92
+ "items": {
93
+ "type": "object",
94
+ "required": ["name"],
95
+ "properties": {
96
+ "name": { "type": "string", "minLength": 1 },
97
+ "url": { "type": "string", "format": "uri" },
98
+ "role": {
99
+ "type": "string",
100
+ "enum": ["finder", "reporter", "analyst", "coordinator", "remediation-developer"],
101
+ "description": "Per CSAF role vocabulary. Defaults to `finder` when not specified."
102
+ }
103
+ },
104
+ "additionalProperties": false
105
+ }
106
+ }
107
+ },
108
+ "additionalProperties": false
109
+ }