@openwop/openwop-conformance 1.6.0 → 1.10.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 (169) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +2 -2
  3. package/api/asyncapi.yaml +74 -1
  4. package/api/openapi.yaml +316 -0
  5. package/coverage.md +16 -0
  6. package/fixtures/conformance-run-duration-breach.json +33 -0
  7. package/fixtures.md +19 -0
  8. package/package.json +1 -1
  9. package/schemas/README.md +12 -0
  10. package/schemas/agent-inventory-response.schema.json +90 -0
  11. package/schemas/ai-envelope.schema.json +28 -0
  12. package/schemas/annotation-create.schema.json +37 -0
  13. package/schemas/annotation.schema.json +56 -0
  14. package/schemas/artifact-type-pack-manifest.schema.json +160 -0
  15. package/schemas/capabilities.schema.json +195 -4
  16. package/schemas/chat-card-pack-manifest.schema.json +158 -0
  17. package/schemas/envelopes/media.audio.schema.json +38 -0
  18. package/schemas/envelopes/media.file.schema.json +37 -0
  19. package/schemas/envelopes/media.image.schema.json +33 -0
  20. package/schemas/heartbeat-evaluated.schema.json +14 -0
  21. package/schemas/heartbeat-state-changed.schema.json +14 -0
  22. package/schemas/node-pack-manifest.schema.json +16 -1
  23. package/schemas/run-event-payloads.schema.json +96 -5
  24. package/schemas/run-event.schema.json +4 -0
  25. package/schemas/workflow-definition.schema.json +5 -0
  26. package/schemas/workspace-file-create.schema.json +20 -0
  27. package/schemas/workspace-file.schema.json +39 -0
  28. package/src/lib/agentLoop.ts +44 -0
  29. package/src/lib/agentRuntime.ts +45 -0
  30. package/src/lib/artifactTypes.ts +96 -0
  31. package/src/lib/cardPacks.ts +52 -0
  32. package/src/lib/discovery-capabilities.ts +50 -0
  33. package/src/lib/distillation.ts +38 -0
  34. package/src/lib/feedback.ts +31 -0
  35. package/src/lib/heartbeat.ts +31 -0
  36. package/src/lib/memoryAttribution.ts +48 -0
  37. package/src/lib/subRunAttestation.ts +35 -0
  38. package/src/lib/toolHooks.ts +33 -0
  39. package/src/scenarios/agent-loop-iteration-monotonic.test.ts +33 -0
  40. package/src/scenarios/agent-loop-stateful-resume.test.ts +28 -0
  41. package/src/scenarios/agent-loop-version5-shape.test.ts +41 -0
  42. package/src/scenarios/agent-loop-workspace-snapshot.test.ts +33 -0
  43. package/src/scenarios/agent-manifest-runtime.test.ts +85 -0
  44. package/src/scenarios/ai-envelope-shape.test.ts +14 -18
  45. package/src/scenarios/aiEnvelope.capBreached.test.ts +2 -1
  46. package/src/scenarios/aiEnvelope.schemaDrift.test.ts +2 -1
  47. package/src/scenarios/aiEnvelope.universalKinds.test.ts +2 -1
  48. package/src/scenarios/approval-gate-flow.test.ts +4 -6
  49. package/src/scenarios/artifact-schema-compile-bounded.test.ts +126 -0
  50. package/src/scenarios/artifact-type-pack-install.test.ts +78 -0
  51. package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +140 -0
  52. package/src/scenarios/artifact-type-store-without-render.test.ts +54 -0
  53. package/src/scenarios/audit-log-integrity.test.ts +3 -2
  54. package/src/scenarios/auth-api-key-rotation.test.ts +2 -1
  55. package/src/scenarios/auth-mtls.test.ts +2 -1
  56. package/src/scenarios/auth-oauth2-client-credentials.test.ts +2 -1
  57. package/src/scenarios/auth-oidc-user-bearer.test.ts +2 -1
  58. package/src/scenarios/auth-saml-profile.test.ts +2 -1
  59. package/src/scenarios/auth-scim-profile.test.ts +2 -1
  60. package/src/scenarios/authorization-fail-closed.test.ts +2 -1
  61. package/src/scenarios/authorization-roles-shape.test.ts +2 -1
  62. package/src/scenarios/byok-auth-modes.test.ts +141 -0
  63. package/src/scenarios/chat-card-pack-execution.test.ts +56 -0
  64. package/src/scenarios/chat-card-pack-manifest-validation.test.ts +128 -0
  65. package/src/scenarios/commitment-fired.test.ts +83 -0
  66. package/src/scenarios/credential-payload-redaction.test.ts +2 -1
  67. package/src/scenarios/credentials-capability-shape.test.ts +2 -1
  68. package/src/scenarios/cross-engine-append-ordering.test.ts +2 -1
  69. package/src/scenarios/cross-host-ancestry-endpoint.test.ts +3 -2
  70. package/src/scenarios/cross-host-causation-shape.test.ts +3 -2
  71. package/src/scenarios/deadletter-capability-shape.test.ts +2 -1
  72. package/src/scenarios/deadletter-retry-exhaustion.test.ts +2 -1
  73. package/src/scenarios/distillation-index-roundtrip.test.ts +35 -0
  74. package/src/scenarios/distillation-secret-carryforward.test.ts +35 -0
  75. package/src/scenarios/distillation-shape.test.ts +41 -0
  76. package/src/scenarios/distillation-stable-archive.test.ts +37 -0
  77. package/src/scenarios/distillation-token-budget.test.ts +45 -0
  78. package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +4 -3
  79. package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +5 -4
  80. package/src/scenarios/envelope-reasoning-shape.test.ts +3 -2
  81. package/src/scenarios/envelope-refusal-shape.test.ts +3 -2
  82. package/src/scenarios/envelope-rendering-hint.test.ts +95 -0
  83. package/src/scenarios/envelope-retry-attempted.test.ts +2 -1
  84. package/src/scenarios/envelope-tier-one-subset-static.test.ts +3 -2
  85. package/src/scenarios/exec-not-protocol-tier.test.ts +137 -0
  86. package/src/scenarios/experimental-tier-shape.test.ts +5 -4
  87. package/src/scenarios/feedback-capability-shape.test.ts +35 -0
  88. package/src/scenarios/feedback-correction-redaction.test.ts +35 -0
  89. package/src/scenarios/feedback-cross-tenant-isolation.test.ts +37 -0
  90. package/src/scenarios/feedback-fork-not-copied.test.ts +40 -0
  91. package/src/scenarios/feedback-on-terminal-run.test.ts +32 -0
  92. package/src/scenarios/feedback-record-and-list.test.ts +32 -0
  93. package/src/scenarios/feedback-unsupported-501.test.ts +32 -0
  94. package/src/scenarios/fs-path-traversal.test.ts +2 -1
  95. package/src/scenarios/heartbeat-capability-shape.test.ts +35 -0
  96. package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +28 -0
  97. package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +43 -0
  98. package/src/scenarios/heartbeat-runtime-bound.test.ts +30 -0
  99. package/src/scenarios/http-client-ssrf.test.ts +10 -13
  100. package/src/scenarios/mcp-toolcall-redaction.test.ts +3 -2
  101. package/src/scenarios/media-url-inline-cap.test.ts +167 -0
  102. package/src/scenarios/memory-attribution-emits-on-write.test.ts +54 -0
  103. package/src/scenarios/memory-attribution-no-content.test.ts +45 -0
  104. package/src/scenarios/memory-attribution-replay-stable.test.ts +60 -0
  105. package/src/scenarios/memory-attribution-shape.test.ts +28 -0
  106. package/src/scenarios/memory-attribution-tenant-scoped.test.ts +44 -0
  107. package/src/scenarios/memory-compaction-event-emitted.test.ts +2 -1
  108. package/src/scenarios/memory-compaction-provenance-tag.test.ts +2 -1
  109. package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +2 -1
  110. package/src/scenarios/memory-consolidation-idempotent.test.ts +77 -0
  111. package/src/scenarios/memory-consolidation-shape.test.ts +90 -0
  112. package/src/scenarios/model-capability-substituted.test.ts +2 -1
  113. package/src/scenarios/multi-agent-confidence-escalation.test.ts +5 -4
  114. package/src/scenarios/multi-agent-handoff-state-machine.test.ts +6 -5
  115. package/src/scenarios/multi-agent-memory-lifecycle.test.ts +4 -3
  116. package/src/scenarios/multi-region-idempotency.test.ts +10 -10
  117. package/src/scenarios/oauth-capability-shape.test.ts +2 -1
  118. package/src/scenarios/oauth-connector-redaction.test.ts +2 -1
  119. package/src/scenarios/pause-resume.test.ts +3 -3
  120. package/src/scenarios/production-backpressure.test.ts +2 -2
  121. package/src/scenarios/production-retention-expiry.test.ts +2 -2
  122. package/src/scenarios/prompt-all-four-kinds-events.test.ts +2 -1
  123. package/src/scenarios/prompt-composed-secret-redaction.test.ts +2 -1
  124. package/src/scenarios/prompt-composed-trust-marker.test.ts +2 -1
  125. package/src/scenarios/prompt-end-to-end-events.test.ts +2 -1
  126. package/src/scenarios/prompt-list-and-fetch.test.ts +2 -1
  127. package/src/scenarios/prompt-mutable-lifecycle.test.ts +2 -1
  128. package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +2 -1
  129. package/src/scenarios/prompt-pack-install.test.ts +2 -1
  130. package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +2 -1
  131. package/src/scenarios/prompt-render-deterministic.test.ts +2 -1
  132. package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +2 -1
  133. package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +2 -1
  134. package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +2 -1
  135. package/src/scenarios/prompt-template-shape.test.ts +2 -1
  136. package/src/scenarios/provider-usage.test.ts +2 -1
  137. package/src/scenarios/redaction.test.ts +4 -1
  138. package/src/scenarios/replay-divergence-at-refusal.test.ts +4 -3
  139. package/src/scenarios/replay-fork-arbitrary.test.ts +3 -1
  140. package/src/scenarios/replay-llm-cache-key-portable.test.ts +2 -1
  141. package/src/scenarios/replayDeterminism.test.ts +3 -1
  142. package/src/scenarios/run-execution-bounds-shape.test.ts +133 -0
  143. package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
  144. package/src/scenarios/sandbox-mvp-behavior.test.ts +2 -1
  145. package/src/scenarios/sandbox-no-host-fs-escape.test.ts +2 -1
  146. package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
  147. package/src/scenarios/scheduling-capability-shape.test.ts +2 -1
  148. package/src/scenarios/scheduling-cron-fires-once.test.ts +2 -1
  149. package/src/scenarios/secret-leakage-otel-attribute.test.ts +7 -6
  150. package/src/scenarios/spec-corpus-validity.test.ts +4 -1
  151. package/src/scenarios/subrun-approval-fail-closed.test.ts +33 -0
  152. package/src/scenarios/subrun-approval-gate.test.ts +35 -0
  153. package/src/scenarios/subrun-attestation-shape.test.ts +30 -0
  154. package/src/scenarios/subrun-checksum-stable.test.ts +43 -0
  155. package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +39 -0
  156. package/src/scenarios/tool-hooks-content-free.test.ts +40 -0
  157. package/src/scenarios/tool-hooks-rate-limit.test.ts +32 -0
  158. package/src/scenarios/tool-hooks-secret-redaction.test.ts +34 -0
  159. package/src/scenarios/tool-hooks-shape.test.ts +34 -0
  160. package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -10
  161. package/src/scenarios/wasm-pack-invoke-completed.test.ts +2 -2
  162. package/src/scenarios/wasm-pack-invoke-suspended.test.ts +2 -2
  163. package/src/scenarios/wasm-pack-load.test.ts +2 -2
  164. package/src/scenarios/wasm-pack-memory-cap.test.ts +3 -6
  165. package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -2
  166. package/src/scenarios/workflow-primary-output-annotation.test.ts +142 -0
  167. package/src/scenarios/workspace-behavior.test.ts +134 -0
  168. package/src/scenarios/workspace-capability-shape.test.ts +73 -0
  169. package/src/scenarios/workspace-cross-tenant-isolation.test.ts +84 -0
@@ -44,6 +44,16 @@
44
44
  "type": "integer",
45
45
  "minimum": 1,
46
46
  "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."
47
+ },
48
+ "maxRunDurationMs": {
49
+ "type": "integer",
50
+ "minimum": 1000,
51
+ "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."
52
+ },
53
+ "maxLoopIterations": {
54
+ "type": "integer",
55
+ "minimum": 1,
56
+ "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."
47
57
  }
48
58
  },
49
59
  "additionalProperties": false,
@@ -414,6 +424,30 @@
414
424
  },
415
425
  "additionalProperties": false
416
426
  },
427
+ "feedback": {
428
+ "type": "object",
429
+ "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.",
430
+ "required": ["supported"],
431
+ "properties": {
432
+ "supported": {
433
+ "type": "boolean",
434
+ "description": "Host implements the RFC 0056 annotation side-store + endpoints + `run.annotated` notification."
435
+ },
436
+ "targets": {
437
+ "type": "array",
438
+ "items": { "type": "string", "enum": ["run", "event", "node"] },
439
+ "uniqueItems": true,
440
+ "description": "Which annotation-target granularities the host accepts. Absent = `run` only."
441
+ },
442
+ "signals": {
443
+ "type": "array",
444
+ "items": { "type": "string", "enum": ["rating", "correction", "label", "flag"] },
445
+ "uniqueItems": true,
446
+ "description": "Which signal kinds the host accepts. Absent = all four."
447
+ }
448
+ },
449
+ "additionalProperties": false
450
+ },
417
451
  "oauth": {
418
452
  "type": "object",
419
453
  "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).",
@@ -509,8 +543,8 @@
509
543
  "version": {
510
544
  "type": "integer",
511
545
  "minimum": 1,
512
- "maximum": 4,
513
- "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, follow-up RFC). 4 = Phase 4 (replay determinism under nondeterministic models, follow-up RFC). A host advertising `version: N` MUST implement all phases 1..N additively."
546
+ "maximum": 5,
547
+ "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). A host advertising `version: N` MUST implement all phases 1..N additively."
514
548
  },
515
549
  "confidenceEscalationFloor": {
516
550
  "type": "number",
@@ -576,6 +610,15 @@
576
610
  "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`."
577
611
  }
578
612
  }
613
+ },
614
+ "statefulResume": {
615
+ "type": "boolean",
616
+ "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`."
617
+ },
618
+ "transcriptWindow": {
619
+ "type": "integer",
620
+ "minimum": 1,
621
+ "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."
579
622
  }
580
623
  }
581
624
  }
@@ -598,7 +641,7 @@
598
641
  "pattern": "^([a-z][a-z0-9-]*|x-host-[a-z][a-z0-9-]*-[a-z][a-z0-9-]*)$"
599
642
  },
600
643
  "uniqueItems": true,
601
- "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: `structured-output`, `discriminator-enum`, `long-context`, `reasoning` (model-native thinking-tokens), `function-calling`. Host-private extensions MUST prefix with `x-host-<host>-`."
644
+ "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>-`."
602
645
  },
603
646
  "substitutionSupported": {
604
647
  "type": "boolean",
@@ -625,7 +668,7 @@
625
668
  "type": "array",
626
669
  "items": { "type": "string", "minLength": 1 },
627
670
  "uniqueItems": true,
628
- "description": "Provider ids the host's AI-proxy can route to. Conventional ids: `anthropic`, `openai`, `gemini`, `mistral`, `cohere`, `vertex`, `bedrock`. Hosts MAY add vendor-prefixed extensions."
671
+ "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."
629
672
  },
630
673
  "byok": {
631
674
  "type": "array",
@@ -633,6 +676,19 @@
633
676
  "uniqueItems": true,
634
677
  "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."
635
678
  },
679
+ "authModes": {
680
+ "type": "object",
681
+ "description": "RFC 0067 (`Draft`). 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`. Consumers MUST ignore an auth mode they don't recognize rather than reject the discovery doc.",
682
+ "additionalProperties": {
683
+ "type": "array",
684
+ "minItems": 1,
685
+ "uniqueItems": true,
686
+ "items": {
687
+ "type": "string",
688
+ "enum": ["apiKey", "oauth-pkce", "oauth-device", "none"]
689
+ }
690
+ }
691
+ },
636
692
  "policies": {
637
693
  "type": "object",
638
694
  "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`.",
@@ -657,6 +713,12 @@
657
713
  }
658
714
  },
659
715
  "additionalProperties": false
716
+ },
717
+ "maxInlineMediaBytes": {
718
+ "type": "integer",
719
+ "minimum": 0,
720
+ "default": 262144,
721
+ "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."
660
722
  }
661
723
  },
662
724
  "additionalProperties": false
@@ -738,6 +800,41 @@
738
800
  "uniqueItems": true,
739
801
  "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."
740
802
  },
803
+ "memoryConsolidation": {
804
+ "type": "object",
805
+ "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.",
806
+ "additionalProperties": false,
807
+ "required": ["supported"],
808
+ "properties": {
809
+ "supported": {
810
+ "type": "boolean",
811
+ "description": "REQUIRED when the block is present. When `true`, the host performs background consolidation over long-term memory and emits `agent.memory.consolidated`."
812
+ },
813
+ "schedule": {
814
+ "type": "string",
815
+ "enum": ["host-managed", "scheduled", "on-demand"],
816
+ "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."
817
+ }
818
+ }
819
+ },
820
+ "commitments": {
821
+ "type": "object",
822
+ "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.",
823
+ "additionalProperties": false,
824
+ "required": ["supported"],
825
+ "properties": {
826
+ "supported": {
827
+ "type": "boolean",
828
+ "description": "REQUIRED when the block is present. When `true`, the host infers standing commitments from memory and emits `commitment.fired` when one fires."
829
+ },
830
+ "fireConditions": {
831
+ "type": "array",
832
+ "items": { "type": "string", "enum": ["time", "predicate"] },
833
+ "uniqueItems": true,
834
+ "description": "Which fire-condition kinds the host supports. `time` composes RFC 0052 scheduling; `predicate` composes RFC 0060 heartbeat. Absent ⇒ `['time']`."
835
+ }
836
+ }
837
+ },
741
838
  "orchestrator": {
742
839
  "type": "boolean",
743
840
  "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' }`)."
@@ -746,6 +843,29 @@
746
843
  "type": "boolean",
747
844
  "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`."
748
845
  },
846
+ "manifestRuntime": {
847
+ "type": "object",
848
+ "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).",
849
+ "additionalProperties": false,
850
+ "required": ["supported"],
851
+ "properties": {
852
+ "supported": {
853
+ "type": "boolean",
854
+ "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)."
855
+ },
856
+ "handoffValidation": {
857
+ "type": "boolean",
858
+ "default": false,
859
+ "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."
860
+ },
861
+ "installScope": {
862
+ "type": "string",
863
+ "enum": ["host", "tenant"],
864
+ "default": "host",
865
+ "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)."
866
+ }
867
+ }
868
+ },
749
869
  "dispatchMapping": {
750
870
  "type": "boolean",
751
871
  "default": false,
@@ -772,6 +892,11 @@
772
892
  }
773
893
  },
774
894
  "additionalProperties": false
895
+ },
896
+ "subRunAttestation": {
897
+ "type": "boolean",
898
+ "default": false,
899
+ "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)."
775
900
  }
776
901
  },
777
902
  "additionalProperties": true
@@ -821,6 +946,36 @@
821
946
  "additionalProperties": false,
822
947
  "if": { "properties": { "supported": { "const": true } }, "required": ["supported"] },
823
948
  "then": { "required": ["supported", "trigger"] }
949
+ },
950
+ "distillation": {
951
+ "type": "object",
952
+ "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.",
953
+ "required": ["supported"],
954
+ "additionalProperties": false,
955
+ "properties": {
956
+ "supported": { "type": "boolean", "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." },
957
+ "maxTokenBudget": { "type": "integer", "minimum": 1, "description": "Largest per-run distillation token budget the host honors. A supplied `distillation.tokenBudget` is clamped to this; absent ⇒ the host defaults to this." },
958
+ "scheduled": { "type": "boolean", "description": "When `true`, host can initiate distillation on a schedule (requires `capabilities.scheduling`, RFC 0052). Distillation MAY also run on-demand without scheduling." },
959
+ "indexEmitted": { "type": "boolean", "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`." },
960
+ "tokenizerName": { "type": "string", "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." },
961
+ "archiveRetention": { "type": "string", "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." }
962
+ }
963
+ },
964
+ "attribution": {
965
+ "type": "object",
966
+ "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`.",
967
+ "required": ["supported"],
968
+ "properties": {
969
+ "supported": {
970
+ "const": true,
971
+ "description": "REQUIRED when the sub-block is present. The block is omitted entirely by hosts that do not support write attribution."
972
+ },
973
+ "emitsWriteEvents": {
974
+ "type": "boolean",
975
+ "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."
976
+ }
977
+ },
978
+ "additionalProperties": false
824
979
  }
825
980
  },
826
981
  "additionalProperties": true
@@ -952,6 +1107,29 @@
952
1107
  },
953
1108
  "additionalProperties": false
954
1109
  },
1110
+ "heartbeat": {
1111
+ "type": "object",
1112
+ "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`).",
1113
+ "required": ["supported"],
1114
+ "properties": {
1115
+ "supported": { "type": "boolean" },
1116
+ "minIntervalSec": { "type": "integer", "minimum": 1, "description": "Smallest interval the host honors; requests below it clamp up." },
1117
+ "maxRuntimeMs": { "type": "integer", "minimum": 1, "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' }`." }
1118
+ },
1119
+ "additionalProperties": false
1120
+ },
1121
+ "toolHooks": {
1122
+ "type": "object",
1123
+ "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.",
1124
+ "required": ["supported"],
1125
+ "additionalProperties": false,
1126
+ "properties": {
1127
+ "supported": { "type": "boolean" },
1128
+ "prePostEvents": { "type": "boolean", "description": "Host populates `argsHash`/`principal`/`transport` on `agent.toolCalled` + `status`/`durationMs` on `agent.toolReturned` for every external tool call." },
1129
+ "perToolAuthorization": { "type": "boolean", "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." },
1130
+ "perToolRateLimit": { "type": "boolean", "description": "Host applies a per-`(principal, tool)` token-bucket rate limit; exhaustion yields `agent.toolReturned { status: 'rate_limited' }` + a `rate_limited` (429) error." }
1131
+ }
1132
+ },
955
1133
  "deadLetter": {
956
1134
  "type": "object",
957
1135
  "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*.",
@@ -962,6 +1140,19 @@
962
1140
  },
963
1141
  "additionalProperties": false
964
1142
  },
1143
+ "workspace": {
1144
+ "type": "object",
1145
+ "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).",
1146
+ "required": ["supported"],
1147
+ "properties": {
1148
+ "supported": { "type": "boolean", "description": "Host implements the RFC 0059 workspace file store + endpoints + `workspace.updated` event." },
1149
+ "versioned": { "type": "boolean", "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`." },
1150
+ "maxFileBytes": { "type": "integer", "minimum": 1, "description": "Per-file byte ceiling; writes beyond it return `workspace_too_large`." },
1151
+ "maxFiles": { "type": "integer", "minimum": 1, "description": "Per-workspace file-count ceiling." },
1152
+ "maxVersions": { "type": "integer", "minimum": 1, "description": "When `versioned: true`, the number of historical versions a host advertises it will retain (history best-effort beyond the mandatory latest)." }
1153
+ },
1154
+ "additionalProperties": false
1155
+ },
965
1156
  "sql": {
966
1157
  "type": "object",
967
1158
  "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).",
@@ -0,0 +1,158 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/chat-card-pack-manifest.schema.json",
4
+ "title": "ChatCardPackManifest",
5
+ "description": "Manifest for a published OpenWOP AI chat card pack — `pack.json` at the pack root with `kind: \"card\"`. Peer to the node / workflow-chain / prompt / artifact-type pack manifests; disjoint via the `kind` discriminator. See `spec/v1/chat-card-packs.md` for the canonical contract and RFC 0071 Phase 2 for the rationale.\n\nA chat card pack distributes AI step cards: each card binds a prompt template (with typed input slots) to a typed output artifact. When a host advertises `host.chat.cardPacks: supported` and a registered card is invoked, the host substitutes inputs into the prompt, routes the call through `ctx.aiEnvelope.generate`, and (when `outputArtifactType` is set) validates the result against the referenced artifact-type pack's schema before emitting `artifact.created`.",
6
+ "type": "object",
7
+ "required": ["name", "version", "kind", "engines", "cards"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "kind": {
11
+ "type": "string",
12
+ "const": "card",
13
+ "description": "Pack kind discriminator. MUST be the literal string `\"card\"`."
14
+ },
15
+ "name": {
16
+ "type": "string",
17
+ "description": "Reverse-DNS pack name per `node-packs.md` §Naming. Reserved scopes are identical (`core.*` / `vendor.<org>.*` / `community.<author>.*` / `private.<host>.*`).",
18
+ "pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$",
19
+ "minLength": 1,
20
+ "maxLength": 256
21
+ },
22
+ "version": {
23
+ "type": "string",
24
+ "description": "Pack-level SemVer 2.0.0.",
25
+ "pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$"
26
+ },
27
+ "description": { "type": "string", "maxLength": 1024 },
28
+ "author": { "type": "string" },
29
+ "license": { "type": "string", "description": "SPDX license identifier (e.g., `Apache-2.0`, `MIT`)." },
30
+ "homepage": { "type": "string", "format": "uri" },
31
+ "repository": { "type": "string", "format": "uri" },
32
+ "keywords": {
33
+ "type": "array",
34
+ "items": { "type": "string", "maxLength": 64 },
35
+ "maxItems": 50
36
+ },
37
+ "engines": {
38
+ "type": "object",
39
+ "required": ["openwop"],
40
+ "properties": {
41
+ "openwop": { "type": "string", "description": "Semver range — which openwop protocol versions this pack works against." }
42
+ },
43
+ "additionalProperties": true
44
+ },
45
+ "dependencies": {
46
+ "type": "object",
47
+ "additionalProperties": { "type": "string" },
48
+ "description": "Other packs this pack depends on (e.g., the artifact-type pack declaring this card's `outputArtifactType`). Map of pack name → semver range."
49
+ },
50
+ "peerDependencies": {
51
+ "type": "object",
52
+ "additionalProperties": { "type": "string" },
53
+ "description": "Engine-supplied capabilities the pack consumes (e.g., `{ \"host.aiEnvelope\": \"supported\", \"host.chat.cards\": \"supported\" }`). Resolved against the host's advertised capabilities at register time."
54
+ },
55
+ "cards": {
56
+ "type": "array",
57
+ "minItems": 1,
58
+ "items": { "$ref": "#/$defs/Card" },
59
+ "description": "AI chat card definitions this pack contributes. Each MUST have a unique `cardTypeId` within the pack."
60
+ },
61
+ "signing": { "$ref": "#/$defs/Signing" }
62
+ },
63
+ "$defs": {
64
+ "Card": {
65
+ "type": "object",
66
+ "required": ["cardTypeId", "prompt"],
67
+ "additionalProperties": false,
68
+ "properties": {
69
+ "cardTypeId": {
70
+ "type": "string",
71
+ "description": "Reverse-DNS card identifier. Same pattern and reserved scopes as a pack `name`. The value `WorkflowNode.cardType` / `ctx.chat.emitCard` MAY reference. Third parties MUST NOT publish under `core.*`.",
72
+ "pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$",
73
+ "minLength": 1,
74
+ "maxLength": 256
75
+ },
76
+ "schemaVersion": {
77
+ "type": "integer",
78
+ "minimum": 0,
79
+ "description": "Non-negative integer card-schema version (the envelope/artifact integer-version axis). Absent ⇒ treated as 0."
80
+ },
81
+ "prompt": { "$ref": "#/$defs/PromptSpec" },
82
+ "inputs": {
83
+ "type": "array",
84
+ "items": { "$ref": "#/$defs/InputField" },
85
+ "description": "Typed input fields the card collects. The portable subset only; host-specific widget kinds extend via `vendor.*`/`x-` prefixed `type` values."
86
+ },
87
+ "outputArtifactType": {
88
+ "type": "string",
89
+ "description": "A registered `artifactTypeId` (RFC 0071 Phase 1) the card produces. When present, the host MUST validate the LLM output against that artifact type's schema and emit `artifact.created`. Registered-only by design: unlike a bare node `WorkflowNode.artifactType` (where the unregistered free-string tier is permanently valid), a card's product is always a contract-bound artifact, so this MUST be a reverse-DNS registered id. Omit the field for a prompt-only card that produces no durable artifact.",
90
+ "pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$"
91
+ },
92
+ "outputSchemaRef": {
93
+ "type": "string",
94
+ "minLength": 1,
95
+ "description": "Path inside the pack tarball to the JSON Schema (Draft 2020-12) the LLM output MUST conform to. SHOULD be consistent with the referenced artifact type's schema. MUST set top-level `additionalProperties: false`."
96
+ },
97
+ "requiredModelCapabilities": {
98
+ "type": "array",
99
+ "items": {
100
+ "type": "string",
101
+ "pattern": "^([a-z][a-z0-9-]*|x-host-[a-z][a-z0-9-]*-[a-z][a-z0-9-]*)$"
102
+ },
103
+ "uniqueItems": true,
104
+ "maxItems": 32,
105
+ "description": "Model capabilities the card requires the active model to advertise. Reuses the `requiredModelCapabilities` registry in `host-capabilities.md` (spec-reserved: `structured-output`, `discriminator-enum`, `long-context`, `reasoning`, `function-calling`; host extensions `x-host-<host>-`)."
106
+ }
107
+ }
108
+ },
109
+ "PromptSpec": {
110
+ "type": "object",
111
+ "required": ["template", "placeholderMapping"],
112
+ "additionalProperties": false,
113
+ "description": "The prompt the card composes. The host substitutes mapped input values into `template`/`systemPrompt` before dispatch; segments derived from card inputs are `untrusted` (RFC 0071 Trust boundary).",
114
+ "properties": {
115
+ "template": { "type": "string", "minLength": 1, "description": "Prompt body with `{{placeholder}}` slots." },
116
+ "systemPrompt": { "type": "string", "description": "Optional system prompt." },
117
+ "placeholderMapping": {
118
+ "type": "object",
119
+ "additionalProperties": { "type": "string" },
120
+ "description": "Map of `{{placeholder}}` name → input path (e.g. `\"spec\": \"inputs.spec\"`)."
121
+ },
122
+ "temperature": { "type": "number", "minimum": 0, "maximum": 2 },
123
+ "maxTokens": { "type": "integer", "minimum": 1 }
124
+ }
125
+ },
126
+ "InputField": {
127
+ "type": "object",
128
+ "required": ["id", "type"],
129
+ "additionalProperties": false,
130
+ "properties": {
131
+ "id": { "type": "string", "minLength": 1, "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$" },
132
+ "type": {
133
+ "type": "string",
134
+ "description": "Closed portable subset OR a `vendor.<org>.<kind>` / `x-<kind>` host extension other hosts ignore. The portable subset (G9, resolved 2026-05-27 against MyndHyve's `CardFieldType`): `text`, `longtext`, `number`, `boolean`, `select`, `multiselect`, `file`, `artifact-ref`. MyndHyve maps `textarea`→`longtext` and `toggle`→`boolean`; its product-specific kinds (`canvas-reference`, `collection-reference`, `color`) are host extensions (`vendor.myndhyve.*`), not portable.",
135
+ "pattern": "^(text|longtext|number|boolean|select|multiselect|file|artifact-ref|vendor\\.[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*|x-[a-z][a-z0-9-]*)$"
136
+ },
137
+ "label": { "type": "string" },
138
+ "required": { "type": "boolean" },
139
+ "default": {},
140
+ "options": {
141
+ "type": "array",
142
+ "items": { "type": "string" },
143
+ "description": "Choices for `type: \"select\"`."
144
+ }
145
+ }
146
+ },
147
+ "Signing": {
148
+ "type": "object",
149
+ "description": "Optional signing metadata. See node-packs.md §signing.",
150
+ "additionalProperties": false,
151
+ "properties": {
152
+ "publicKeyRef": { "type": "string" },
153
+ "signatureRef": { "type": "string" },
154
+ "method": { "type": "string", "enum": ["manual", "sigstore"] }
155
+ }
156
+ }
157
+ }
158
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/envelopes/media.audio.schema.json",
4
+ "title": "MediaAudioPayload",
5
+ "description": "Payload for the OPTIONAL `media.audio` AI Envelope kind (RFC 0055 §C). Carries a host-served URL reference (preferred) or, below the host's `aiProviders.maxInlineMediaBytes` cap, inline base64 audio data. Pairs with the `meta.rendering.display: \"audio\"` hint. Advertised, opt-in kind — not a MUST-recognize universal kind.",
6
+ "type": "object",
7
+ "required": ["bytes"],
8
+ "properties": {
9
+ "url": {
10
+ "type": "string",
11
+ "format": "uri",
12
+ "description": "Tenant-scoped, non-guessable host-served asset URL (RFC 0055 §C rule 1). SHOULD be used above `aiProviders.maxInlineMediaBytes`. Enforced by the `media-asset-url-tenant-scoped` SECURITY invariant."
13
+ },
14
+ "base64": {
15
+ "type": "string",
16
+ "description": "Inline base64-encoded audio, permitted only at or below `aiProviders.maxInlineMediaBytes` (RFC 0055 §C rule 2). Above the cap the host MUST use `url`."
17
+ },
18
+ "bytes": {
19
+ "type": "integer",
20
+ "minimum": 0,
21
+ "description": "Byte size of the asset."
22
+ },
23
+ "mimeType": {
24
+ "type": "string",
25
+ "description": "IANA media type (e.g. `audio/mpeg`, `audio/wav`, `audio/ogg`). SHOULD match `meta.rendering.mimeType`."
26
+ },
27
+ "durationSeconds": {
28
+ "type": "number",
29
+ "minimum": 0,
30
+ "description": "Optional clip duration in seconds, for a consumer's player UI."
31
+ },
32
+ "alt": {
33
+ "type": "string",
34
+ "description": "Text alternative / label for accessibility (RFC 0055 §B); mirrors `meta.rendering.alt` when the producer also sets the rendering hint."
35
+ }
36
+ },
37
+ "additionalProperties": false
38
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/envelopes/media.file.schema.json",
4
+ "title": "MediaFilePayload",
5
+ "description": "Payload for the OPTIONAL `media.file` AI Envelope kind (RFC 0055 §C). A downloadable, non-inline-rendered asset (document, archive, dataset). Carries a host-served URL reference (preferred) or, below the host's `aiProviders.maxInlineMediaBytes` cap, inline base64. Pairs with the `meta.rendering.display: \"file\"` hint. Advertised, opt-in kind — not a MUST-recognize universal kind.",
6
+ "type": "object",
7
+ "required": ["bytes"],
8
+ "properties": {
9
+ "url": {
10
+ "type": "string",
11
+ "format": "uri",
12
+ "description": "Tenant-scoped, non-guessable host-served asset URL (RFC 0055 §C rule 1). SHOULD be used above `aiProviders.maxInlineMediaBytes`. Enforced by the `media-asset-url-tenant-scoped` SECURITY invariant."
13
+ },
14
+ "base64": {
15
+ "type": "string",
16
+ "description": "Inline base64-encoded file bytes, permitted only at or below `aiProviders.maxInlineMediaBytes` (RFC 0055 §C rule 2). Above the cap the host MUST use `url`."
17
+ },
18
+ "bytes": {
19
+ "type": "integer",
20
+ "minimum": 0,
21
+ "description": "Byte size of the asset."
22
+ },
23
+ "mimeType": {
24
+ "type": "string",
25
+ "description": "IANA media type (e.g. `application/pdf`, `text/csv`, `application/zip`). SHOULD match `meta.rendering.mimeType`."
26
+ },
27
+ "name": {
28
+ "type": "string",
29
+ "description": "Optional suggested filename for download (e.g. `q3-report.pdf`)."
30
+ },
31
+ "alt": {
32
+ "type": "string",
33
+ "description": "Text alternative / label for accessibility (RFC 0055 §B); mirrors `meta.rendering.alt` when the producer also sets the rendering hint."
34
+ }
35
+ },
36
+ "additionalProperties": false
37
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/envelopes/media.image.schema.json",
4
+ "title": "MediaImagePayload",
5
+ "description": "Payload for the OPTIONAL `media.image` AI Envelope kind (RFC 0055 §C). Carries a host-served URL reference (preferred) or, below the host's `aiProviders.maxInlineMediaBytes` cap, inline base64 image data. Pairs with the `meta.rendering.display: \"image\"` hint. This is an advertised, opt-in kind — not one of the four MUST-recognize universal kinds; a host that emits no `media.*` kind is unaffected.",
6
+ "type": "object",
7
+ "required": ["bytes"],
8
+ "properties": {
9
+ "url": {
10
+ "type": "string",
11
+ "format": "uri",
12
+ "description": "Tenant-scoped, non-guessable host-served asset URL (RFC 0055 §C rule 1 — signed-token / capability-token discipline). SHOULD be used when the asset exceeds `capabilities.aiProviders.maxInlineMediaBytes`. Enforced by the `media-asset-url-tenant-scoped` SECURITY invariant."
13
+ },
14
+ "base64": {
15
+ "type": "string",
16
+ "description": "Inline base64-encoded image, permitted only when the asset is at or below `aiProviders.maxInlineMediaBytes` (default 256 KiB, RFC 0055 §C rule 2). Above the cap the host MUST use `url`. Mutually exclusive with `url` in practice — a single envelope carries one or the other."
17
+ },
18
+ "bytes": {
19
+ "type": "integer",
20
+ "minimum": 0,
21
+ "description": "Byte size of the asset. Required so a consumer can decide whether to inline-render or defer-fetch, and so the cap rule is checkable."
22
+ },
23
+ "mimeType": {
24
+ "type": "string",
25
+ "description": "IANA media type (e.g. `image/png`, `image/jpeg`, `image/webp`). SHOULD match `meta.rendering.mimeType`."
26
+ },
27
+ "alt": {
28
+ "type": "string",
29
+ "description": "Text alternative for accessibility (RFC 0055 §B). SHOULD be present so a consumer can describe the image to assistive technology; mirrors `meta.rendering.alt` when the producer also sets the rendering hint."
30
+ }
31
+ },
32
+ "additionalProperties": false
33
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/heartbeat-evaluated.schema.json",
4
+ "title": "HeartbeatEvaluated",
5
+ "description": "RFC 0060. Emitted every tick by a host advertising `capabilities.heartbeat.supported: true` — the observability record of a heartbeat predicate evaluation. Heartbeat-scoped (NOT a replayable run-event-log entry). Carries no external state or secret material; `changed` gates whether a `heartbeat.stateChanged` + enqueued run follow.",
6
+ "type": "object",
7
+ "required": ["heartbeatId", "status", "changed"],
8
+ "properties": {
9
+ "heartbeatId": { "type": "string", "minLength": 1, "description": "Host-assigned identifier for the heartbeat whose tick this records." },
10
+ "status": { "type": "string", "enum": ["ok", "timeout", "error"], "description": "`ok` — predicate evaluated within `maxRuntimeMs`. `timeout` — terminated at the budget (RFC 0060 §B.2). `error` — the predicate threw." },
11
+ "changed": { "type": "boolean", "description": "Whether the predicate's state transitioned vs. the prior tick. `true` is the only path that may emit `heartbeat.stateChanged` and enqueue a run." }
12
+ },
13
+ "additionalProperties": false
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/heartbeat-state-changed.schema.json",
4
+ "title": "HeartbeatStateChanged",
5
+ "description": "RFC 0060. Emitted ONLY on a predicate-state transition — an unchanged tick MUST NOT emit it (this is what prevents notification spam). Heartbeat-scoped (NOT a replayable run-event-log entry). `from`/`to` are opaque host-persisted predicate-state tokens; they MUST NOT carry secret or external-content material beyond the minimal state needed to detect a transition.",
6
+ "type": "object",
7
+ "required": ["heartbeatId", "from", "to"],
8
+ "properties": {
9
+ "heartbeatId": { "type": "string", "minLength": 1, "description": "Host-assigned identifier for the heartbeat that transitioned." },
10
+ "from": { "type": "object", "additionalProperties": true, "description": "Prior tick's predicate state (opaque, host-defined)." },
11
+ "to": { "type": "object", "additionalProperties": true, "description": "Current tick's predicate state — the transition target (opaque, host-defined)." }
12
+ },
13
+ "additionalProperties": false
14
+ }