@openwop/openwop-conformance 1.37.0 → 1.44.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 (30) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +2 -2
  3. package/api/openapi.yaml +62 -5
  4. package/fixtures/conformance-agent-memory-injection-budget.json +44 -0
  5. package/fixtures/conformance-context-budget-multiturn.json +50 -0
  6. package/fixtures.md +2 -0
  7. package/package.json +1 -1
  8. package/schemas/README.md +5 -0
  9. package/schemas/a2ui-surface-delta-frame.schema.json +48 -0
  10. package/schemas/capabilities.schema.json +155 -1
  11. package/schemas/channel-presence-payload.schema.json +41 -0
  12. package/schemas/compact-tool-descriptor.schema.json +51 -0
  13. package/schemas/conversation-turn.schema.json +10 -0
  14. package/schemas/frontend-plugin-manifest.schema.json +93 -0
  15. package/schemas/memory-list-options.schema.json +16 -0
  16. package/schemas/run-event-payloads.schema.json +25 -2
  17. package/schemas/run-event.schema.json +2 -0
  18. package/schemas/ui-plugin-message.schema.json +90 -0
  19. package/src/lib/toolCatalog.ts +89 -0
  20. package/src/scenarios/a2ui-surface-delta-transport.test.ts +600 -0
  21. package/src/scenarios/channel-presence-behavioral.test.ts +83 -0
  22. package/src/scenarios/channel-presence-shape.test.ts +93 -0
  23. package/src/scenarios/context-budget-transcript-bound.test.ts +253 -0
  24. package/src/scenarios/context-summarization-replay.test.ts +155 -0
  25. package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +120 -0
  26. package/src/scenarios/frontend-plugin-packs.test.ts +230 -0
  27. package/src/scenarios/memory-injection-budget.test.ts +188 -0
  28. package/src/scenarios/prompt-prefix-cache.test.ts +200 -0
  29. package/src/scenarios/run-transport-economy.test.ts +236 -0
  30. package/src/scenarios/tool-catalog-compact-projection.test.ts +149 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "1.37.0",
3
+ "version": "1.44.0",
4
4
  "description": "Production-ready black-box conformance suite for OpenWOP v1.0 compliant servers.",
5
5
  "repository": {
6
6
  "type": "git",
package/schemas/README.md CHANGED
@@ -32,6 +32,7 @@
32
32
  | `envelopes/media.audio.schema.json` | `ai-envelope.md` §"Media reference payloads" | RFC 0055 §C — optional `media.audio` payload; URL ref or inline base64 + optional `durationSeconds`. |
33
33
  | `envelopes/media.file.schema.json` | `ai-envelope.md` §"Media reference payloads" | RFC 0055 §C — optional `media.file` payload; downloadable asset by URL ref or inline base64 + optional `name`. |
34
34
  | `envelopes/ui.a2ui-surface.schema.json` | `ai-envelope.md` §"A2UI surfaces" | RFC 0102 — optional, advertised `ui.a2ui-surface` payload; closed A2UI component tree (`anyOf` + single-string-enum discriminator) + host-enumerated `catalogVersion`. Core `ui.*` content-primitive family beside `media.*`. |
35
+ | `a2ui-surface-delta-frame.schema.json` | `ai-envelope.md` §"Delta transport" + `stream-modes.md` | RFC 0114 (`Active`) — host-side TRANSPORT frame (`{ surfaceRef, catalogVersion, patch[] }`) carrying an RFC 6902 (JSON-Patch) delta over a recorded `ui.a2ui-surface` envelope; stream-only (`?a2uiDelta=1`), NOT a recorded shape (the envelope stays full). Op enum excludes `test`. Consumer re-validates the post-patch surface against the closed catalog fail-closed. |
35
36
  | `annotation.schema.json` | `RFCS/0056` + `observability.md` | RFC 0056 (`Draft`) — a non-blocking human/agent quality signal (rating / correction / label / flag) attached to a run, event, or node. A side-resource (not a replayable run-event-log entry); response of `POST/GET /v1/runs/{runId}/annotations` + payload of the `run.annotated` SSE notification. |
36
37
  | `annotation-create.schema.json` | `RFCS/0056` | RFC 0056 (`Draft`) — request body for `POST /v1/runs/{runId}/annotations` (host assigns `annotationId`/`createdAt`/`actor`; binds `target.runId` to the path). |
37
38
  | `heartbeat-evaluated.schema.json` | `RFCS/0060` + `host-capabilities.md` | RFC 0060 (`Active`) — payload of the heartbeat-scoped `heartbeat.evaluated` AsyncAPI event (`{ heartbeatId, status, changed }`); emitted every tick by a host advertising `capabilities.heartbeat`. Not a run-event-log entry. |
@@ -39,8 +40,11 @@
39
40
  | `audit-verify-result.schema.json` | `auth-profiles.md` §`openwop-audit-log-integrity` | Response payload from `GET /v1/audit/verify` — chain-validity verdict + checkpoints + anomalies |
40
41
  | `capabilities.schema.json` | `capabilities.md` | `/.well-known/openwop` response — protocolVersion + supportedEnvelopes + schemaVersions + limits + optional v1 discovery surface |
41
42
  | `channel-written-payload.schema.json` | `channels-and-reducers.md` §Channel write event | Payload of the `channel.written` RunEvent — write input + reducer name |
43
+ | `channel-presence-payload.schema.json` | RFC 0110 | Payload of the OPTIONAL `channel.presence` RunEvent — ephemeral channel presence (online + typing), membership-gated, non-persisted |
42
44
  | `chat-card-pack-manifest.schema.json` | `chat-card-packs.md` + RFC 0071 | DRAFT — manifest for `kind: "card"` registry packs (RFC 0071 Phase 2). Peer to the node/workflow-chain/prompt/artifact-type pack manifests; disjoint via the `kind` discriminator. Distributes AI chat cards: a prompt template + typed input subset bound to a typed `outputArtifactType`. |
43
45
  | `connection-pack-manifest.schema.json` | `connection-packs.md` + RFC 0095 | DRAFT — manifest for `kind: "connection"` registry packs (RFC 0095). Peer to the node/workflow-chain/prompt/artifact-type/chat-card pack manifests; disjoint via the `kind` discriminator. Distributes a portable provider definition — auth endpoints, read/write scope groups, exactly-one reach (`mcp`/`openapi`/`integration`) — that the RFC 0045/0047 `provider` string resolves against. Carries NO credential material (`connection-pack-no-credential-material`). |
46
+ | `frontend-plugin-manifest.schema.json` | `frontend-plugin-packs.md` + RFC 0117 | DRAFT — manifest for `kind: "frontend-plugin"` registry packs (RFC 0117). Peer to the other pack manifests; disjoint via the `kind` discriminator. Distributes a signed, SANDBOXED UI extension — one or more `uiPlugins[]` (a `pluginId`, a `surface`, an opaque `entry` bundle, a closed `hostApi` allowlist). A backend `runtime` member is FORBIDDEN (`not: {}`). Loaded only in a cross-origin sandbox (`frontend-plugin-isolation`). |
47
+ | `ui-plugin-message.schema.json` | `frontend-plugin-packs.md` + RFC 0117 | DRAFT — the `ui-plugin/1` `postMessage` host-RPC envelope (RFC 0117) between a sandboxed front-end plugin and its host: a plugin→host `request` (closed `method` allowlist), a host→plugin `response` (`ok`+`result` or `ok:false`+`error`), or a host→plugin `event`. Carries the optimistic-concurrency `version` token and the `artifact_conflict` + `currentVersion` shape (`frontend-plugin-rpc-allowlist` / `frontend-plugin-no-byok`). |
44
48
  | `conformance-certification-bundle.schema.json` | `conformance-certification.md` + RFC 0089 | DRAFT — machine-readable attestation binding a host's claimed profiles to the reproducible run that substantiates them (suite version + per-scenario pass list + host identity/commit + captured discovery document). Out-of-band; a consumer re-derives each claim via the §B binding rule. |
45
49
  | `conversation-event.schema.json` | `channels-and-reducers.md` + conversation RFC | Multi-turn conversation event shape for orchestrator-driven HITL flows |
46
50
  | `conversation-turn.schema.json` | `channels-and-reducers.md` + conversation RFC | Conversation turn shape for user/agent/system messages |
@@ -74,6 +78,7 @@
74
78
  | `a2a-task-state.schema.json` | `a2a-integration.md` §"Async / durable Tasks" (RFC 0100) | The durable, persisted projection of an A2A `Task` an OpenWOP host keeps per backing run when `a2a.durableTasks: true` — `taskId == runId`, lowercase-hyphen `state`, `interruptKind`, optional SSRF-guarded `PushConfig`. Content-free of run inputs/outputs/artifacts (SR-1 / `a2a-push-egress-ssrf`). |
75
79
  | `budget-policy.schema.json` | `budget-policy.md` (RFC 0084) | The reserved `budget` run-options shape — `maxTokens`/`maxCostUsd`/`maxToolCalls`/`maxRetries`/`modelAllow[]`/`modelDeny[]`/`thresholdPercent`/`onExhaustion`. Enforceable per-run spend governance; wall-time/iterations delegated to RFC 0058 (`additionalProperties:false`). Content-free events; no pricing on the wire (`budget-no-pricing-leak`). |
76
80
  | `tool-descriptor.schema.json` | `tool-catalog.md` (RFC 0078) | Portable read-only description of one tool unifying the five tool surfaces (node-pack/workflow/mcp/connector/host-extension) — stable `toolId`, source, I/O schemas, auth/egress/approval requirements, replay policy, and `safetyTier` (`exec` ⇒ `host-extension`, RFC 0069). Returned by `GET /v1/tools`; secret-free (SR-1). |
81
+ | `compact-tool-descriptor.schema.json` | `tool-catalog.md` §compact (RFC 0112) | Lossy model-facing projection of `ToolDescriptor` returned by `GET /v1/tools?view=compact` (`{ tools: [] }` envelope) when the host advertises `toolCatalog.compactView`. Keeps `toolId`/`source`/`safetyTier` (+ optional `title`/`description`/`inputSchema`), drops the heavy fields, and bounds any `inputSchema` to a self-contained structural subset (top-level object; no `$ref`/`oneOf`/`allOf`/`anyOf`/`not`/`patternProperties`/`dependentSchemas`). Secret-free (SR-1); `exec` ⇒ `host-extension`. |
77
82
  | `suspend-request.schema.json` | `interrupt.md` | `InterruptPayload` with 8 `kind` discriminators (approval, clarification, external-event, custom, conversation.start, conversation.exchange, conversation.close, low-confidence) |
78
83
  | `workflow-chain-pack-manifest.schema.json` | `workflow-chain-packs.md` + RFC 0013 | Manifest for workflow-chain packs (`kind: "workflow-chain"`) — pre-configured DAG fragments expanded inline at workflow-author time. Peer to `node-pack-manifest.schema.json`; disjoint via the `kind` discriminator. |
79
84
  | `workflow-definition.schema.json` | `channels-and-reducers.md` + `node-packs.md` | DAG of nodes + edges + triggers + variables + channels |
@@ -0,0 +1,48 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/a2ui-surface-delta-frame.schema.json",
4
+ "title": "A2uiSurfaceDeltaFrame",
5
+ "description": "RFC 0114. A HOST-SIDE TRANSPORT frame carrying an RFC 6902 (JSON-Patch) delta over a recorded `ui.a2ui-surface` envelope (RFC 0102). Delivered ONLY over the run event stream (`GET /v1/runs/{runId}/events`) to a subscriber that negotiated `?a2uiDelta=1`; every other consumer (the event-log read, replay, `:fork`, any non-negotiating subscriber) receives the materialized FULL surface. This is NOT a recorded-envelope shape: the canonical `ui.a2ui-surface.schema.json` envelope is UNCHANGED and always full. The consumer applies the `patch` to the surface last delivered under `surfaceRef`, then re-validates the result against the closed `catalogVersion` catalog before render (the same fail-closed validation a full surface receives, RFC 0102 §1); on ANY apply/validation failure it falls back to store-without-render and the host re-materializes the full surface.",
6
+ "type": "object",
7
+ "required": ["surfaceRef", "catalogVersion", "patch"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "surfaceRef": {
11
+ "type": "string",
12
+ "description": "REQUIRED — the recorded `ui.a2ui-surface` envelope id this delta patches. The consumer applies `patch` to the surface last delivered under this ref.",
13
+ "minLength": 1
14
+ },
15
+ "catalogVersion": {
16
+ "type": "string",
17
+ "description": "REQUIRED — the A2UI catalog version. MUST equal the referenced full surface's `catalogVersion`; a catalog-version change MUST start from a fresh full surface, never a delta.",
18
+ "minLength": 1
19
+ },
20
+ "patch": {
21
+ "type": "array",
22
+ "minItems": 1,
23
+ "description": "REQUIRED — a non-empty RFC 6902 (JSON-Patch) document applied over the surface last delivered under `surfaceRef`. The `test` op is EXCLUDED (a fire-and-forget transport frame cannot act on a failed conditional); `move`/`copy` are permitted but OPTIONAL to support.",
24
+ "items": {
25
+ "type": "object",
26
+ "required": ["op", "path"],
27
+ "additionalProperties": false,
28
+ "properties": {
29
+ "op": {
30
+ "enum": ["add", "remove", "replace", "move", "copy"],
31
+ "description": "RFC 6902 operation. `test` is deliberately excluded."
32
+ },
33
+ "path": {
34
+ "type": "string",
35
+ "description": "RFC 6901 JSON-Pointer into the target surface."
36
+ },
37
+ "from": {
38
+ "type": "string",
39
+ "description": "RFC 6901 JSON-Pointer source for `move`/`copy`."
40
+ },
41
+ "value": {
42
+ "description": "The value for `add`/`replace`. Walked by the SR-1 redaction harness exactly like a full-surface value (RFC 0114)."
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
@@ -583,6 +583,30 @@
583
583
  }
584
584
  }
585
585
  },
586
+ "conversationTurnModelProvenance": {
587
+ "type": "object",
588
+ "description": "RFC 0109 — Conversation-turn model provenance. When advertised with `supported: true`, the host stamps the OPTIONAL `agent.model` object (`{ provider, model }`) on `role: 'agent'` conversation turns (`conversation-turn.schema.json`), recording which model produced the turn. The stamp is NON-SECRET + NON-PII (`additionalProperties: false` on `agent.model` forbids any credential/endpoint/prompt — the SR-1 guard) and is read VERBATIM on `:fork` (never re-resolved, so a forked transcript preserves the original provenance). Absent block ⇒ no advertisement: the host omits `agent.model` and treats it as an opaque, unenforced field. Advertising `supported: true` without stamping is a dishonest wire claim (`OPENWOP_REQUIRE_BEHAVIOR=true` fails the gated scenario). Additive over RFC 0005 — extends the conversation primitive, does not replace it.",
589
+ "required": ["supported"],
590
+ "additionalProperties": false,
591
+ "properties": {
592
+ "supported": {
593
+ "type": "boolean",
594
+ "description": "RFC 0109. Host stamps `agent.model` ({ provider, model }) on `role: 'agent'` conversation turns. When `false` or absent, the RFC 0109 conformance scenario soft-skips and the host emits no model provenance."
595
+ }
596
+ }
597
+ },
598
+ "channelPresence": {
599
+ "type": "object",
600
+ "description": "RFC 0110 — Channel presence (online + typing). When advertised with `supported: true`, the host emits the OPTIONAL `channel.presence` RunEvent (`channel-presence-payload.schema.json`) for a `type:'channel'` conversation, carrying the currently-present member subject refs + optional per-member typing. Presence is EPHEMERAL live state: the host MUST NOT persist it to the replayable event log / transcript and it MUST NOT affect replay or `:fork` (the load-bearing distinction from the persisted `conversation.exchanged` turn). Membership-gated: every ref MUST be a current participant and the event MUST NOT be delivered to a non-member (DEFAULT-DENY, CTI-1). NON-PII (opaque RFC 0041 subject refs only). Absent block ⇒ no advertisement: the host emits no presence. Advertising `supported: true` without emitting is a dishonest wire claim (`OPENWOP_REQUIRE_BEHAVIOR=true` fails the gated scenario). Additive over RFC 0005.",
601
+ "required": ["supported"],
602
+ "additionalProperties": false,
603
+ "properties": {
604
+ "supported": {
605
+ "type": "boolean",
606
+ "description": "RFC 0110. Host emits the ephemeral `channel.presence` RunEvent for channel conversations. When `false` or absent, the RFC 0110 conformance scenario soft-skips and the host emits no presence."
607
+ }
608
+ }
609
+ },
586
610
  "multiAgent": {
587
611
  "type": "object",
588
612
  "description": "RFC 0037 — Multi-agent execution model + handoff state machine. Hosts that advertise implement the supervisor→dispatch→harvest loop + the 4-state handoff state machine + the `core.workflowChain.event` emission contract per spec/v1/multi-agent-execution.md. Absent block = host implements RFCs 0006/0007/0022 individually with implementation flexibility on integration semantics; conformance scenarios gating on this flag soft-skip on absence.",
@@ -705,6 +729,47 @@
705
729
  "type": "integer",
706
730
  "minimum": 1,
707
731
  "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."
732
+ },
733
+ "contextBudget": {
734
+ "type": "object",
735
+ "description": "RFC 0111 (`Active`, `version >= 5`). Opt-in, token-denominated bound on the orchestrator transcript the host feeds each iteration, plus a declared summarization contract for turns evicted beyond that budget. SCOPE: governs the RFC 0061 per-iteration ORCHESTRATOR-LOOP transcript (`multi-agent-execution.md` §\"Per-iteration state inputs\" input 3 — the same transcript `transcriptWindow` bounds), NOT a general chat-conversation history. A host whose orchestrator loop does not run real model turns (e.g. a mock supervisor) MUST NOT advertise this block, exactly as it MUST NOT dishonestly advertise `transcriptWindow`. Budget-only advertisement (`transcriptTokenBudget` + `tokenCounter` WITHOUT `summarization.supported`) is valid for a host that HAS a real orchestrator loop but does not summarize. Purely additive: a host that omits this block behaves exactly as today, including `transcriptWindow`'s `absent ⇒ unbounded` default (NOT flipped by this RFC). Complements (does not replace) the event-count `transcriptWindow`; when both bound a turn the host MUST honor whichever is tighter. Summarization here is a NONDETERMINISTIC host output governed exactly like an RFC 0041 envelope: each substitution MUST be recorded as a `context.summarized` event whose `summaryRef` artifact replay reuses (never re-summarizes) per `multi-agent-execution.md` §\"Context economy (RFC 0111)\".",
736
+ "additionalProperties": false,
737
+ "properties": {
738
+ "transcriptTokenBudget": {
739
+ "type": "integer",
740
+ "minimum": 1,
741
+ "description": "RFC 0111. Max tokens of transcript the host feeds any single orchestrator turn, measured in the unit named by `tokenCounter`. Advertise-and-honor; complements (does not replace) `transcriptWindow`. When both are present the host MUST honor whichever bound is tighter for a given turn. Absent ⇒ no token bound on the transcript (only the event-count `transcriptWindow`, if any, applies)."
742
+ },
743
+ "tokenCounter": {
744
+ "type": "string",
745
+ "enum": ["o200k_base", "cl100k_base", "chars", "host-defined"],
746
+ "description": "RFC 0111. The unit `transcriptTokenBudget` is denominated in, so the bound is interpretable across hosts. REQUIRED when `transcriptTokenBudget` is present (enforced via the `if/then` clause). `o200k_base`/`cl100k_base` are tokenizer encodings; `chars` counts UTF-8/Unicode characters (a tokenizer-free unit a client can reason about directly); `host-defined` is an opaque host unit. Same enum as RFC 0113 `memory.injectionBudget.tokenCounter` — transcript (0111) and memory (0113) budgets denominate in one consistent vocabulary; no shared `$ref` (decoupled)."
747
+ },
748
+ "summarization": {
749
+ "type": "object",
750
+ "additionalProperties": false,
751
+ "required": ["supported"],
752
+ "description": "RFC 0111. Declared contract for turns evicted beyond `transcriptTokenBudget`. When `supported: true`, the host MAY replace older in-window turns with a host-produced summary; it MUST keep the most recent `keepLastTurns` turns verbatim and MUST NOT summarize the active (most recent) turn. Each substitution MUST be recorded as a `context.summarized` event and is replay-governed under RFC 0041 (replay reuses the recorded `summaryRef`, never re-summarizes).",
753
+ "properties": {
754
+ "supported": {
755
+ "type": "boolean",
756
+ "description": "REQUIRED when the sub-block is present. When `true`, the host implements the RFC 0111 declared-summarization contract; conformance scenario `context-summarization-replay` gates on this flag and soft-skips when absent/false."
757
+ },
758
+ "strategy": {
759
+ "type": "string",
760
+ "enum": ["sliding-window", "recursive", "map-reduce"],
761
+ "description": "RFC 0111. Informational descriptor of the host's summarization strategy. `sliding-window` keeps a recent verbatim tail and summarizes the prefix; `recursive` folds prior summaries into new ones; `map-reduce` summarizes chunks then combines. Does not change the replay-determinism contract — all strategies record `context.summarized` and reuse `summaryRef` on replay."
762
+ },
763
+ "keepLastTurns": {
764
+ "type": "integer",
765
+ "minimum": 0,
766
+ "description": "RFC 0111. Number of most-recent turns kept verbatim at the head of the window; older in-window turns MAY be replaced by a summary. The active (most recent) turn MUST NOT be summarized regardless of this value. Absent ⇒ host-defined verbatim floor."
767
+ }
768
+ }
769
+ }
770
+ },
771
+ "if": { "required": ["transcriptTokenBudget"] },
772
+ "then": { "required": ["transcriptTokenBudget", "tokenCounter"] }
708
773
  }
709
774
  }
710
775
  }
@@ -856,6 +921,24 @@
856
921
  "turnDetection": ["transcription"],
857
922
  "bargeIn": ["transcription"]
858
923
  }
924
+ },
925
+ "promptPrefixCache": {
926
+ "type": "object",
927
+ "additionalProperties": false,
928
+ "description": "RFC 0116. Host honors the AI-envelope `generate` request's optional `cachePrefixId` as a provider-cache routing hint — tenant-namespaced (SECURITY invariant `prompt-prefix-cache-cross-tenant-isolation`), secret-free, and replay-invariant (a cache hit/miss MUST NOT change the recorded envelope or `provider.usage.inputTokens`/`outputTokens`). Absent ⇒ a host MUST ignore `cachePrefixId` (no error, no behavior change). PROVIDER-SCOPED: prefix caching is provider-specific (e.g. Anthropic ephemeral), so this is NOT a universal claim — see `providers`.",
929
+ "properties": {
930
+ "supported": {
931
+ "type": "boolean",
932
+ "description": "Whether the host honors `cachePrefixId` as a provider-cache routing hint."
933
+ },
934
+ "providers": {
935
+ "type": "array",
936
+ "items": { "type": "string", "minLength": 1 },
937
+ "uniqueItems": true,
938
+ "description": "RFC 0116. The subset of `aiProviders.supported[]` for which the host honors `cachePrefixId` (prefix caching is provider-specific). A request whose routed provider is NOT in this list MUST have `cachePrefixId` ignored. Absent ⇒ host-defined per-provider routing; NOT a universal claim across providers."
939
+ }
940
+ },
941
+ "required": ["supported"]
859
942
  }
860
943
  },
861
944
  "additionalProperties": false,
@@ -1341,6 +1424,22 @@
1341
1424
  "ttl": { "type": "boolean", "description": "When `true`, memory entries expire per `expiresAt` (the `ttlSupported` semantics surfaced as a named retention dimension)." },
1342
1425
  "forget": { "type": "boolean", "description": "When `true`, the host supports a tenant-scoped delete-by-subject forget operation (composes the CTI-1 cross-tenant invariant — a forget MUST NOT cross tenant boundaries)." }
1343
1426
  }
1427
+ },
1428
+ "injectionBudget": {
1429
+ "type": "object",
1430
+ "description": "RFC 0113 (`Active`). The host honors `MemoryListOptions.tokenBudget` — a token-denominated bound on a single injection read (the live read that feeds a turn), distinct from RFC 0062 distillation's background-compaction budget. Advertising it commits the host to return a token-bounded prefix of the ranked entry list (over-budget single entry omitted, never truncated mid-entry) over the SR-1-redacted, CTI-1-single-tenant result set. Relevance ranking is NOT advertised here — `rank:'relevance'` delegates to the existing `memory.search` semantic mode (RFC 0080), so there is exactly one relevance surface in the corpus. Hosts that omit this block do not honor `tokenBudget` (a supplied `tokenBudget` is ignored, today's `limit`/`tag` behavior).",
1431
+ "additionalProperties": false,
1432
+ "required": ["supported"],
1433
+ "properties": {
1434
+ "supported": { "type": "boolean", "description": "REQUIRED when the sub-block is present. When `true`, the host honors `MemoryListOptions.tokenBudget` per `agent-memory.md` §\"Injection budget\"." },
1435
+ "tokenCounter": {
1436
+ "type": "string",
1437
+ "enum": ["o200k_base", "cl100k_base", "chars", "host-defined"],
1438
+ "description": "The unit `tokenBudget` is denominated in. REQUIRED when `injectionBudget.supported` (enforced via the `if/then` clause). `o200k_base`/`cl100k_base` are tokenizer encodings; `chars` counts UTF-8/Unicode characters of the entry `content` (a tokenizer-free unit a client can reason about directly — preferred over opaque `host-defined`); `host-defined` is an opaque host unit. The over-budget-single-entry-omitted rule applies regardless of unit. RFC 0111 aligns to these same values when it lands (0113 lands first); no shared `$ref` (decoupled)."
1439
+ }
1440
+ },
1441
+ "if": { "properties": { "supported": { "const": true } }, "required": ["supported"] },
1442
+ "then": { "required": ["supported", "tokenCounter"] }
1344
1443
  }
1345
1444
  },
1346
1445
  "additionalProperties": true
@@ -1508,7 +1607,8 @@
1508
1607
  "items": { "type": "string", "enum": ["node-pack", "workflow", "mcp", "connector", "host-extension"] },
1509
1608
  "description": "Which tool sources the catalog projects. A host advertises only the sources it actually surfaces; a consumer MUST tolerate any subset. Absent ⇒ all sources the host implements."
1510
1609
  },
1511
- "sessionLifecycle": { "type": "boolean", "description": "`true` ⇒ the host emits the RFC 0078 §D tool-session lifecycle events (`tool.session.opened`/`tool.session.closed`, content-free) bracketing the existing RFC 0064 `agent.toolCalled`/`agent.toolReturned` call events for multi-step interactions. Absent ⇒ `false` (single-shot tool calls only)." }
1610
+ "sessionLifecycle": { "type": "boolean", "description": "`true` ⇒ the host emits the RFC 0078 §D tool-session lifecycle events (`tool.session.opened`/`tool.session.closed`, content-free) bracketing the existing RFC 0064 `agent.toolCalled`/`agent.toolReturned` call events for multi-step interactions. Absent ⇒ `false` (single-shot tool calls only)." },
1611
+ "compactView": { "type": "boolean", "description": "RFC 0112. `true` ⇒ the host honors `GET /v1/tools?view=compact` + `GET /v1/tools/{toolId}?view=compact`, returning the `{ tools: CompactToolDescriptor[] }` projection (`compact-tool-descriptor.schema.json`): the heavy descriptor fields (`outputSchema`/`auth`/`egress`/`approval`/`replayPolicy`/`costHint`/`latencyHint`) are dropped and any `inputSchema` is bounded to the compact structural subset. The compact `tools[]` carries the same `toolId` set as the standard view for the same principal. Absent ⇒ the host treats `view=compact` as an unknown query param (standard view)." }
1512
1612
  }
1513
1613
  },
1514
1614
  "httpClient": {
@@ -1642,6 +1742,33 @@
1642
1742
  },
1643
1743
  "additionalProperties": false
1644
1744
  },
1745
+ "uiPlugins": {
1746
+ "type": "object",
1747
+ "description": "RFC 0117 (`Active`). Host loads SIGNED, SANDBOXED front-end plugin packs (`kind: \"frontend-plugin\"`) — canvas editors, custom artifact viewers, settings panels — in a CROSS-ORIGIN ISOLATED iframe and talks to them over the closed `ui-plugin/1` `postMessage` host-RPC boundary. The wire owns the boundary (isolation + RPC allowlist + manifest), NOT a renderer. Gated on `supported: true`; a host that does not advertise it rejects `kind: \"frontend-plugin\"` packs at registration and renders no plugin surface (graceful degradation to RFC 0071 host rendering). SECURITY invariants `frontend-plugin-isolation` / `frontend-plugin-egress` / `frontend-plugin-rpc-allowlist` / `frontend-plugin-no-byok` (RFC 0117 §Security).",
1748
+ "required": ["supported"],
1749
+ "properties": {
1750
+ "supported": { "type": "boolean", "description": "Host loads `kind: \"frontend-plugin\"` packs in a cross-origin sandbox and serves the `ui-plugin/1` host-RPC boundary." },
1751
+ "isolation": {
1752
+ "type": "string",
1753
+ "const": "cross-origin-iframe",
1754
+ "description": "The ONLY conformant isolation model. In-process / same-origin / module-federation loading is a protocol-tier MUST NOT (`frontend-plugin-isolation`). Pinned as a `const` so the advertisement cannot claim a weaker model."
1755
+ },
1756
+ "surfaces": {
1757
+ "type": "array",
1758
+ "description": "Plugin surfaces this host renders. A pack's `uiPlugins[].surface` not in this set is installable-but-inert (§Degradation).",
1759
+ "items": { "type": "string", "enum": ["artifact-viewer", "route", "settings-panel"] },
1760
+ "uniqueItems": true
1761
+ },
1762
+ "hostApi": {
1763
+ "type": "array",
1764
+ "description": "The `ui-plugin/1` host-RPC methods this host honors. A plugin call to a method not in this set (regardless of the plugin's declared `hostApi`) MUST be rejected with `method_not_allowed` (`frontend-plugin-rpc-allowlist`). A host advertising `artifact.write` MUST enforce the `version`-token optimistic concurrency (RFC 0117 §Concurrency).",
1765
+ "items": { "type": "string", "enum": ["artifact.read", "artifact.write", "host.toast", "host.navigate"] },
1766
+ "uniqueItems": true
1767
+ },
1768
+ "maxEntryBytes": { "type": "integer", "minimum": 1, "description": "Per-plugin entry-bundle byte ceiling the host will load." }
1769
+ },
1770
+ "additionalProperties": false
1771
+ },
1645
1772
  "sql": {
1646
1773
  "type": "object",
1647
1774
  "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).",
@@ -2197,6 +2324,33 @@
2197
2324
  }
2198
2325
  }
2199
2326
  }
2327
+ },
2328
+ "restTransport": {
2329
+ "type": "object",
2330
+ "additionalProperties": false,
2331
+ "description": "RFC 0115 (`Active`). Conditional-GET + Content-Encoding negotiation on run reads (`GET /v1/runs/{runId}`). Optional. Distinct from the file-egress `fileHandling.transport` (ftp/sftp/ssh) sub-capability — this advertises HTTP-layer poll economy on the run-read REST surface.",
2332
+ "properties": {
2333
+ "conditionalRunGet": {
2334
+ "type": "boolean",
2335
+ "description": "RFC 0115. Host emits a strong, event-log-sequence-derived `ETag` on `GET /v1/runs/{runId}` and honors `If-None-Match` with a `304 Not Modified` (empty body) when the validator matches the current state version."
2336
+ },
2337
+ "contentEncodings": {
2338
+ "type": "array",
2339
+ "items": { "type": "string", "enum": ["gzip", "br", "zstd"] },
2340
+ "description": "RFC 0115. Content-Encoding values the host will negotiate on run reads (advertisement of standard-HTTP behavior). `gzip` is the baseline; `br`/`zstd` are OPTIONAL — the host advertises only the subset it can actually serve. For each advertised value the decoded body MUST be byte-identical to the identity body."
2341
+ }
2342
+ }
2343
+ },
2344
+ "a2uiSurface": {
2345
+ "type": "object",
2346
+ "additionalProperties": false,
2347
+ "description": "RFC 0114 (`Active`). Host-side TRANSPORT features over the recorded `ui.a2ui-surface` envelope (RFC 0102). A transport optimization, NOT an envelope-payload kind — the recorded envelope stays the full surface. Optional.",
2348
+ "properties": {
2349
+ "deltaTransport": {
2350
+ "type": "boolean",
2351
+ "description": "RFC 0114. Host delivers RFC 6902 (`a2ui-surface-delta-frame.schema.json`) delta frames over the run event stream to subscribers that negotiate `?a2uiDelta=1`; the full surface is materialized for everyone else, the event-log read, and replay. The recorded `ui.a2ui-surface` envelope stays full. The consumer re-validates the post-patch surface against the closed `catalogVersion` catalog before render and falls back fail-closed on any apply/validation failure (the host then re-materializes full)."
2352
+ }
2353
+ }
2200
2354
  }
2201
2355
  },
2202
2356
  "additionalProperties": true
@@ -0,0 +1,41 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/channel-presence-payload.schema.json",
4
+ "title": "ChannelPresencePayload",
5
+ "description": "Payload of the OPTIONAL `channel.presence` RunEvent (RFC 0110). EPHEMERAL live presence for a `type:'channel'` conversation — who is currently present + (optionally) who is typing. A host MUST NOT persist this event to the replayable event log / transcript, and it MUST NOT affect replay or `:fork` determinism (presence is live state, the load-bearing distinction from the persisted `conversation.exchanged` turn — see replay.md). Membership-gated: every ref MUST be a current channel participant and the event MUST NOT be delivered to a non-member (the same DEFAULT-DENY visibility as the channel's messages; cross-tenant delivery is forbidden, CTI-1). NON-PII: subject refs are the opaque RFC 0041 vocabulary only — no IP/location/device. A host MAY emit; if it advertises `channelPresence.supported` it MUST. Gated on `channelPresence.supported` (capabilities.schema.json). See RFC 0110.",
6
+ "type": "object",
7
+ "required": ["conversationId", "present"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "conversationId": {
11
+ "type": "string",
12
+ "description": "The `type:'channel'` conversation this presence is for.",
13
+ "minLength": 1,
14
+ "maxLength": 256
15
+ },
16
+ "present": {
17
+ "type": "array",
18
+ "description": "Subject refs (RFC 0041 vocabulary `user:<id>` / `agent:<id>` — opaque, non-PII) of members CURRENTLY present in the channel. MUST be a subset of the channel's current participants; a non-member MUST NOT appear.",
19
+ "items": { "type": "string", "minLength": 1, "maxLength": 256 }
20
+ },
21
+ "typing": {
22
+ "type": "array",
23
+ "description": "OPTIONAL subset of `present` that is currently typing. Boolean-by-presence: a ref appears iff that member is typing. No payload beyond the subject refs (no free text, no PII).",
24
+ "items": { "type": "string", "minLength": 1, "maxLength": 256 }
25
+ }
26
+ },
27
+ "examples": [
28
+ {
29
+ "$comment": "RFC 0110 POSITIVE — two members present in a channel, one typing.",
30
+ "conversationId": "chan-eng",
31
+ "present": ["user:alice", "agent:iris"],
32
+ "typing": ["user:alice"]
33
+ },
34
+ {
35
+ "$comment": "RFC 0110 POSITIVE — typing is OPTIONAL; a presence snapshot with no one typing.",
36
+ "conversationId": "chan-eng",
37
+ "present": ["user:alice"]
38
+ }
39
+ ],
40
+ "$comment": "RFC 0110 NEGATIVE (not validatable as an examples[] entry, which must be VALID): a payload carrying any field beyond conversationId/present/typing (e.g. `ip`, `location`) MUST FAIL validation via `additionalProperties: false` — the no-PII guard; see conformance/src/scenarios/channel-presence-shape.test.ts."
41
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/compact-tool-descriptor.schema.json",
4
+ "title": "CompactToolDescriptor",
5
+ "description": "RFC 0112. A lossy, model-facing projection of `ToolDescriptor` (tool-descriptor.schema.json) returned by `GET /v1/tools?view=compact` + `GET /v1/tools/{toolId}?view=compact` when the host advertises `capabilities.toolCatalog.compactView: true`. Heavy descriptive fields (`outputSchema`, `auth`, `egress`, `approval`, `replayPolicy`, `costHint`, `latencyHint`) are dropped, and any `inputSchema` is bounded to the self-contained compact structural subset (top-level `type: \"object\"` with `properties`; no `$ref`/`oneOf`/`allOf`/`anyOf`/`not`/`patternProperties`/`dependentSchemas`). The subset is the stable structural core of RFC 0030 Tier-1, cited as rationale but pinned HERE so conformance is machine-checkable and immune to that informative table's drift. Like the full descriptor, a CompactToolDescriptor never carries credential material (SR-1) and is not an invocation path.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["toolId", "source", "safetyTier"],
9
+ "properties": {
10
+ "toolId": {
11
+ "type": "string",
12
+ "minLength": 1,
13
+ "description": "Unchanged from ToolDescriptor. Stable, host-unique tool identifier in the `<scope>:<tool-id>` form; MUST be stable across catalog reads for a given host version (tool-catalog.md §C MUST 3). The compact `tools[]` MUST carry the same `toolId` set as the standard view for the same principal (projection completeness)."
14
+ },
15
+ "source": {
16
+ "type": "string",
17
+ "enum": ["node-pack", "workflow", "mcp", "connector", "host-extension"],
18
+ "description": "Retained from ToolDescriptor so the `safetyTier: \"exec\" ⇒ source: \"host-extension\"` cross-field MUST (RFC 0069) stays expressible. Which surface backs the tool: `node-pack`/`workflow`/`mcp`/`connector`/`host-extension`."
19
+ },
20
+ "safetyTier": {
21
+ "type": "string",
22
+ "enum": ["pure", "read", "write", "exec"],
23
+ "description": "REQUIRED. The tool's DATA-EFFECT classification (`pure`/`read`/`write`/`exec`); per RFC 0069 an `exec` tool MUST be `source: \"host-extension\"`. Host-assigned, not derivable from a permission/approval/risk tier."
24
+ },
25
+ "title": { "type": "string", "description": "MAY — short human-readable label for a model-facing tool picker." },
26
+ "description": {
27
+ "type": "string",
28
+ "description": "MAY — one-line summary; the host SHOULD truncate to a model-facing summary in the compact view."
29
+ },
30
+ "inputSchema": {
31
+ "type": "object",
32
+ "description": "MAY — the tool's argument schema bounded to the compact structural subset (RFC 0112). When present it MUST have top-level `type: \"object\"` with an explicit `properties` map, and MUST NOT use `$ref`, `oneOf`, `allOf`, `anyOf`, `not`, `patternProperties`, or `dependentSchemas` AT ANY NESTING DEPTH (including inside nested property schemas). Absent ⇒ opaque/host-interpreted args. NOTE: the `propertyNames` clause below is a TOP-LEVEL structural floor only; the total any-depth constraint is enforced by the RFC 0112 conformance scenario (a schema-aware recursive walk), since pure JSON Schema cannot express it without recursion gymnastics.",
33
+ "required": ["type", "properties"],
34
+ "properties": {
35
+ "type": { "const": "object", "description": "Top-level MUST be `\"object\"`." },
36
+ "properties": { "type": "object", "description": "The argument property map (MUST be present)." }
37
+ },
38
+ "propertyNames": {
39
+ "$comment": "RFC 0112 compact structural subset (TOP-LEVEL FLOOR): forbid the heavy/non-portable keywords as top-level inputSchema keys. The total any-depth constraint is enforced by conformance.",
40
+ "not": { "enum": ["$ref", "oneOf", "allOf", "anyOf", "not", "patternProperties", "dependentSchemas"] }
41
+ }
42
+ }
43
+ },
44
+ "allOf": [
45
+ {
46
+ "$comment": "RFC 0069 / tool-catalog.md §C-1: an exec-tier tool MUST be host-extension-sourced (exec is never protocol-tier). Mirrors tool-descriptor.schema.json.",
47
+ "if": { "properties": { "safetyTier": { "const": "exec" } }, "required": ["safetyTier"] },
48
+ "then": { "properties": { "source": { "const": "host-extension" } }, "required": ["source"] }
49
+ }
50
+ ]
51
+ }
@@ -58,6 +58,16 @@
58
58
  "memoryRef": {
59
59
  "type": "string",
60
60
  "description": "Opaque memory reference per RFC 0002 §A + RFC 0004 §A. Resolves to `MemoryEntry[]` via host `MemoryAdapter` for cross-run conversation continuity."
61
+ },
62
+ "model": {
63
+ "type": "object",
64
+ "description": "RFC 0109 — OPTIONAL provenance: which model produced this `role: 'agent'` turn. NON-SECRET, NON-PII: provider + model identifiers ONLY (`additionalProperties: false` forbids any credential/endpoint/prompt leaking in — the SR-1 secret-redaction guard). Stamped at emit; read VERBATIM on `:fork` (never re-resolved). A host that stamps it MUST advertise `conversationTurnModelProvenance.supported: true`. Absent ⇒ no advertisement; clients MUST tolerate its absence.",
65
+ "additionalProperties": false,
66
+ "required": ["provider", "model"],
67
+ "properties": {
68
+ "provider": { "type": "string", "minLength": 1, "maxLength": 128, "description": "Provider identifier (e.g. `anthropic`, `openai`, `google`). Non-secret." },
69
+ "model": { "type": "string", "minLength": 1, "maxLength": 256, "description": "Model identifier as the provider names it (e.g. `claude-opus-4-8`). Non-secret." }
70
+ }
61
71
  }
62
72
  },
63
73
  "additionalProperties": true
@@ -0,0 +1,93 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openwop.dev/spec/v1/frontend-plugin-manifest.schema.json",
4
+ "title": "FrontendPluginManifest",
5
+ "description": "RFC 0117. Manifest for a published OpenWOP front-end plugin pack — `pack.json` at the pack root with `kind: \"frontend-plugin\"`. Peer to and disjoint from `node-pack-manifest.schema.json` (RFC 0003), `connection-pack-manifest.schema.json` (RFC 0095), and the other pack kinds via the `kind` discriminator. A front-end plugin pack distributes a SIGNED, SANDBOXED user-interface extension (a canvas editor, a custom artifact viewer, a settings panel) that a host loads at runtime in a CROSS-ORIGIN ISOLATED iframe and enables through its own admin surface. The wire owns only the manifest shape, the isolation model, and the `ui-plugin/1` host-RPC boundary — NOT a rendering runtime: the plugin `entry` bundle is opaque bytes the host runs in a sandbox. Carries NO secret and NO backend `runtime` (a `runtime` member is rejected — a plugin is sandboxed UI, not a node entry). Requires the host to advertise `capabilities.uiPlugins.supported: true`; degrades to nothing on hosts without it. See `spec/v1/frontend-plugin-packs.md`.",
6
+ "type": "object",
7
+ "required": ["name", "version", "kind", "engines", "uiPlugins"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "name": {
11
+ "type": "string",
12
+ "description": "Reverse-DNS pack name per `node-packs.md` §Naming. Reserved scopes are identical (`core.*` / `vendor.<org>.*` / `community.<author>.*` / `private.<host>.*`). Mirror of `connection-pack-manifest.schema.json#/properties/name`.",
13
+ "pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$",
14
+ "minLength": 1,
15
+ "maxLength": 256
16
+ },
17
+ "version": {
18
+ "type": "string",
19
+ "description": "Pack-level SemVer 2.0.0.",
20
+ "pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$"
21
+ },
22
+ "kind": {
23
+ "type": "string",
24
+ "const": "frontend-plugin",
25
+ "description": "Pack kind discriminator. MUST be the literal string `\"frontend-plugin\"` for this schema."
26
+ },
27
+ "description": { "type": "string", "maxLength": 1024 },
28
+ "author": { "type": "string" },
29
+ "license": { "type": "string", "description": "SPDX license identifier (e.g., `Apache-2.0`)." },
30
+ "homepage": { "type": "string", "format": "uri" },
31
+ "engines": {
32
+ "type": "object",
33
+ "description": "Compatibility floor. `openwop` is the spec range the pack targets; mirror of the other pack manifests.",
34
+ "required": ["openwop"],
35
+ "additionalProperties": false,
36
+ "properties": {
37
+ "openwop": {
38
+ "type": "string",
39
+ "description": "SemVer range of the OpenWOP spec this pack targets (e.g., `>=1.2.0`)."
40
+ }
41
+ }
42
+ },
43
+ "runtime": {
44
+ "not": {},
45
+ "description": "FORBIDDEN. A front-end plugin is sandboxed UI, not a backend node `runtime`. A manifest that carries `runtime` under `kind: \"frontend-plugin\"` MUST be rejected at registration with `pack_kind_invalid` (RFC 0117 §Negative). Declared here as `not: {}` so any value fails validation."
46
+ },
47
+ "uiPlugins": {
48
+ "type": "array",
49
+ "description": "The plugin surfaces this pack supplies. A pack MUST declare at least one. A host installs the pack but renders only those whose `surface` is in `capabilities.uiPlugins.surfaces` and whose `hostApi` methods are a subset of `capabilities.uiPlugins.hostApi` — the rest are installable-but-inert (§Degradation), never an error.",
50
+ "minItems": 1,
51
+ "items": { "$ref": "#/$defs/uiPlugin" }
52
+ }
53
+ },
54
+ "$defs": {
55
+ "uiPlugin": {
56
+ "type": "object",
57
+ "required": ["pluginId", "surface", "entry", "hostApi"],
58
+ "additionalProperties": false,
59
+ "properties": {
60
+ "pluginId": {
61
+ "type": "string",
62
+ "description": "Stable identifier of this plugin within the pack. A host enables/disables plugins by `pluginId` in its admin surface.",
63
+ "pattern": "^[a-z][a-z0-9-]{0,63}$"
64
+ },
65
+ "surface": {
66
+ "type": "string",
67
+ "description": "Which host surface this plugin renders into. `artifact-viewer` renders a typed artifact (RFC 0071) in a sandbox; `route` mounts a standalone admin/tool page; `settings-panel` contributes a configuration panel. A host degrades a `surface` it does not advertise to nothing (§Degradation).",
68
+ "enum": ["artifact-viewer", "route", "settings-panel"]
69
+ },
70
+ "entry": {
71
+ "type": "string",
72
+ "description": "Pack-relative path to the plugin's entry bundle (the opaque bytes loaded in the cross-origin sandbox). Flat namespace; MUST NOT contain `..` or a leading `/` (the pattern forbids both). Required — a `uiPlugins[]` entry missing `entry` fails validation (RFC 0117 §Negative).",
73
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._/-]{0,255}$"
74
+ },
75
+ "hostApi": {
76
+ "type": "array",
77
+ "description": "The closed allowlist of `ui-plugin/1` host-RPC methods this plugin is permitted to call. The host MUST reject (with an `error` response, never silent execution) any method not in BOTH this declared list AND the host's `capabilities.uiPlugins.hostApi` set (`frontend-plugin-rpc-allowlist`). The plugin holds no credentials; every method is host-mediated and authz-checked.",
78
+ "items": {
79
+ "type": "string",
80
+ "enum": ["artifact.read", "artifact.write", "host.toast", "host.navigate"]
81
+ },
82
+ "uniqueItems": true
83
+ },
84
+ "connectSrc": {
85
+ "type": "array",
86
+ "description": "OPTIONAL explicit `connect-src` CSP exceptions the plugin needs (e.g., a font CDN). Absent → the host serves the plugin under a DENY-EGRESS CSP (`frontend-plugin-egress`): no network egress beyond the host-RPC channel. Any entry here is part of the front-end-review checkpoint (`registry-operations.md`) a reviewer audits; a host MAY refuse a pack whose exceptions it will not grant.",
87
+ "items": { "type": "string", "format": "uri" },
88
+ "uniqueItems": true
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
@@ -15,6 +15,22 @@
15
15
  "minLength": 1,
16
16
  "maxLength": 256,
17
17
  "description": "Filter to entries whose `tags` array contains this string (set-membership check). Hosts MAY support more advanced filtering as a host extension under a `vendor.<host>.*` field."
18
+ },
19
+ "tokenBudget": {
20
+ "type": "integer",
21
+ "minimum": 1,
22
+ "description": "RFC 0113. Max cumulative tokens across returned entries, denominated in the unit named by `capabilities.memory.injectionBudget.tokenCounter`. The adapter MUST return a prefix of the ranked entry list whose cumulative token count does not exceed this; a single entry exceeding the budget on its own MUST be omitted (not truncated mid-entry). MAY combine with `limit` — the adapter honors whichever yields fewer entries. Requires the host to advertise `memory.injectionBudget.supported`."
23
+ },
24
+ "rank": {
25
+ "type": "string",
26
+ "enum": ["recency", "relevance"],
27
+ "description": "RFC 0113. Selection order for the (optionally `tokenBudget`-bounded) read. `recency` (default when absent) orders most-recent-first — today's behavior. `relevance` DELEGATES to the existing `memory.search` semantic mode (RFC 0080): it requires `query` AND that the host advertise `capabilities.memory.search` with `semantic` mode, and is the budgeted projection OVER that existing search surface — NOT a new ranking capability. A host that does not advertise `memory.search` semantic mode MUST reject `rank:'relevance'` (or fall back to `'recency'` per its documented behavior)."
28
+ },
29
+ "query": {
30
+ "type": "string",
31
+ "minLength": 1,
32
+ "maxLength": 4096,
33
+ "description": "RFC 0113. Free-text relevance anchor (the `memory.search` semantic query) — REQUIRED when `rank:'relevance'`, ignored otherwise."
18
34
  }
19
35
  },
20
36
  "additionalProperties": false