@openwop/spec-artifacts 2.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CORPUS-STAMP.json +234 -0
- package/LICENSE +201 -0
- package/README.md +7 -0
- package/api/.redocly.lint-ignore.yaml +22 -0
- package/api/asyncapi.yaml +834 -0
- package/api/grpc/openwop.proto +251 -0
- package/api/openapi.yaml +2693 -0
- package/api/redocly.yaml +23 -0
- package/api/seams-v2.yaml +680 -0
- package/api/v2/asyncapi.yaml +528 -0
- package/api/v2/openapi.yaml +3139 -0
- package/api/v2/redocly.yaml +16 -0
- package/package.json +10 -0
- package/schemas/README.md +144 -0
- package/schemas/a2a-task-state.schema.json +78 -0
- package/schemas/a2ui-surface-delta-frame.schema.json +48 -0
- package/schemas/agent-deployment-transition.schema.json +49 -0
- package/schemas/agent-deployment.schema.json +54 -0
- package/schemas/agent-eval-suite.schema.json +140 -0
- package/schemas/agent-inventory-response.schema.json +115 -0
- package/schemas/agent-manifest.schema.json +156 -0
- package/schemas/agent-org-chart.schema.json +82 -0
- package/schemas/agent-ref.schema.json +63 -0
- package/schemas/agent-roster-entry.schema.json +81 -0
- package/schemas/agent-roster-response.schema.json +21 -0
- package/schemas/ai-envelope.schema.json +134 -0
- package/schemas/annotation-create.schema.json +37 -0
- package/schemas/annotation.schema.json +56 -0
- package/schemas/artifact-type-pack-manifest.schema.json +244 -0
- package/schemas/audit-verify-result.schema.json +90 -0
- package/schemas/budget-policy.schema.json +18 -0
- package/schemas/capabilities.schema.json +4160 -0
- package/schemas/certification-bundle-v2.schema.json +118 -0
- package/schemas/channel-presence-payload.schema.json +41 -0
- package/schemas/channel-written-payload.schema.json +55 -0
- package/schemas/chat-card-pack-manifest.schema.json +253 -0
- package/schemas/compact-tool-descriptor.schema.json +51 -0
- package/schemas/compensation-policy.schema.json +64 -0
- package/schemas/conformance-certification-bundle.schema.json +86 -0
- package/schemas/connection-pack-manifest.schema.json +353 -0
- package/schemas/conversation-event.schema.json +168 -0
- package/schemas/conversation-turn.schema.json +117 -0
- package/schemas/core-conformance-mock-agent-config.schema.json +152 -0
- package/schemas/credential-provenance.schema.json +18 -0
- package/schemas/credential-reference.schema.json +21 -0
- package/schemas/debug-bundle.schema.json +196 -0
- package/schemas/dispatch-config.schema.json +112 -0
- package/schemas/envelopes/clarification.request.schema.json +52 -0
- package/schemas/envelopes/error.schema.json +30 -0
- package/schemas/envelopes/media.audio.schema.json +38 -0
- package/schemas/envelopes/media.file.schema.json +37 -0
- package/schemas/envelopes/media.image.schema.json +33 -0
- package/schemas/envelopes/schema.request.schema.json +26 -0
- package/schemas/envelopes/schema.response.schema.json +22 -0
- package/schemas/envelopes/ui.a2ui-surface.schema.json +154 -0
- package/schemas/error-envelope.schema.json +25 -0
- package/schemas/eval-summary.schema.json +92 -0
- package/schemas/export-bundle.schema.json +66 -0
- package/schemas/form-content-pack-manifest.schema.json +243 -0
- package/schemas/frontend-plugin-manifest.schema.json +109 -0
- package/schemas/goal.schema.json +104 -0
- package/schemas/heartbeat-evaluated.schema.json +14 -0
- package/schemas/heartbeat-state-changed.schema.json +14 -0
- package/schemas/localized-content-language-settings.schema.json +26 -0
- package/schemas/localized-content-page-response.schema.json +60 -0
- package/schemas/localized-content-page.schema.json +62 -0
- package/schemas/localized-content-section.schema.json +51 -0
- package/schemas/memory-entry.schema.json +36 -0
- package/schemas/memory-list-options.schema.json +37 -0
- package/schemas/node-pack-manifest.schema.json +654 -0
- package/schemas/orchestrator-decision.schema.json +90 -0
- package/schemas/org-chart-responsibility-view.schema.json +26 -0
- package/schemas/pack-lockfile.schema.json +108 -0
- package/schemas/prompt-kind.schema.json +8 -0
- package/schemas/prompt-pack-manifest.schema.json +117 -0
- package/schemas/prompt-ref.schema.json +40 -0
- package/schemas/prompt-template.schema.json +149 -0
- package/schemas/proposal.schema.json +84 -0
- package/schemas/registry-version-manifest.schema.json +208 -0
- package/schemas/residency.schema.json +16 -0
- package/schemas/run-ancestry-response.schema.json +54 -0
- package/schemas/run-diff-response.schema.json +64 -0
- package/schemas/run-event-payloads.schema.json +4452 -0
- package/schemas/run-event.schema.json +211 -0
- package/schemas/run-options.schema.json +80 -0
- package/schemas/run-orchestrator-decided-event.schema.json +20 -0
- package/schemas/run-snapshot.schema.json +148 -0
- package/schemas/security-advisory.schema.json +109 -0
- package/schemas/self-hosted-runner-dispatch-frame.schema.json +79 -0
- package/schemas/self-hosted-runner-registration.schema.json +53 -0
- package/schemas/self-hosted-runner-result-frame.schema.json +38 -0
- package/schemas/subject.schema.json +56 -0
- package/schemas/suspend-request.schema.json +207 -0
- package/schemas/tool-descriptor.schema.json +63 -0
- package/schemas/trigger-event.schema.json +222 -0
- package/schemas/trigger-subscription-registration.schema.json +67 -0
- package/schemas/trigger-subscription.schema.json +26 -0
- package/schemas/ui-plugin-message.schema.json +90 -0
- package/schemas/v2/a2a-task-state.schema.json +102 -0
- package/schemas/v2/a2ui-surface-delta-frame.schema.json +62 -0
- package/schemas/v2/agent-deployment-transition.schema.json +67 -0
- package/schemas/v2/agent-deployment.schema.json +68 -0
- package/schemas/v2/agent-eval-suite.schema.json +214 -0
- package/schemas/v2/agent-inventory-response.schema.json +172 -0
- package/schemas/v2/agent-manifest.schema.json +218 -0
- package/schemas/v2/agent-org-chart.schema.json +149 -0
- package/schemas/v2/agent-ref.schema.json +67 -0
- package/schemas/v2/agent-roster-entry.schema.json +104 -0
- package/schemas/v2/agent-roster-response.schema.json +27 -0
- package/schemas/v2/ai-envelope.schema.json +167 -0
- package/schemas/v2/annotation-create.schema.json +109 -0
- package/schemas/v2/annotation.schema.json +142 -0
- package/schemas/v2/artifact-type-pack-manifest.schema.json +245 -0
- package/schemas/v2/audit-verify-result.schema.json +110 -0
- package/schemas/v2/budget-policy.schema.json +61 -0
- package/schemas/v2/capabilities.schema.json +7644 -0
- package/schemas/v2/certification-bundle.schema.json +401 -0
- package/schemas/v2/channel-presence-payload.schema.json +60 -0
- package/schemas/v2/channel-written-payload.schema.json +64 -0
- package/schemas/v2/chat-card-pack-manifest.schema.json +254 -0
- package/schemas/v2/compact-tool-descriptor.schema.json +108 -0
- package/schemas/v2/compensation-policy.schema.json +91 -0
- package/schemas/v2/compensation-projection.schema.json +95 -0
- package/schemas/v2/configurable.schema.json +110 -0
- package/schemas/v2/connection-pack-manifest.schema.json +354 -0
- package/schemas/v2/conversation-event.schema.json +281 -0
- package/schemas/v2/conversation-turn.schema.json +165 -0
- package/schemas/v2/credential-provenance.schema.json +62 -0
- package/schemas/v2/credential-reference.schema.json +28 -0
- package/schemas/v2/debug-bundle.schema.json +312 -0
- package/schemas/v2/dispatch-config.schema.json +147 -0
- package/schemas/v2/effect-ledger-projection.schema.json +71 -0
- package/schemas/v2/effect-seam-manifest.schema.json +98 -0
- package/schemas/v2/envelopes/clarification.request.schema.json +59 -0
- package/schemas/v2/envelopes/error.schema.json +35 -0
- package/schemas/v2/envelopes/media.audio.schema.json +41 -0
- package/schemas/v2/envelopes/media.file.schema.json +40 -0
- package/schemas/v2/envelopes/media.image.schema.json +36 -0
- package/schemas/v2/envelopes/schema.request.schema.json +29 -0
- package/schemas/v2/envelopes/schema.response.schema.json +26 -0
- package/schemas/v2/envelopes/ui.a2ui-surface.schema.json +292 -0
- package/schemas/v2/error-envelope.schema.json +229 -0
- package/schemas/v2/eval-summary.schema.json +166 -0
- package/schemas/v2/export-bundle.schema.json +94 -0
- package/schemas/v2/form-content-pack-manifest.schema.json +241 -0
- package/schemas/v2/frontend-plugin-manifest.schema.json +152 -0
- package/schemas/v2/goal.schema.json +188 -0
- package/schemas/v2/heartbeat-evaluated.schema.json +34 -0
- package/schemas/v2/heartbeat-state-changed.schema.json +31 -0
- package/schemas/v2/ids.schema.json +31 -0
- package/schemas/v2/localized-content-language-settings.schema.json +34 -0
- package/schemas/v2/localized-content-page-response.schema.json +94 -0
- package/schemas/v2/localized-content-page.schema.json +87 -0
- package/schemas/v2/localized-content-section.schema.json +63 -0
- package/schemas/v2/memory-entry.schema.json +45 -0
- package/schemas/v2/memory-list-options.schema.json +41 -0
- package/schemas/v2/node-pack-manifest.schema.json +657 -0
- package/schemas/v2/orchestrator-decision.schema.json +120 -0
- package/schemas/v2/org-chart-responsibility-view.schema.json +36 -0
- package/schemas/v2/pack-lockfile.schema.json +139 -0
- package/schemas/v2/prompt-kind.schema.json +14 -0
- package/schemas/v2/prompt-pack-manifest.schema.json +118 -0
- package/schemas/v2/prompt-ref.schema.json +42 -0
- package/schemas/v2/prompt-template.schema.json +209 -0
- package/schemas/v2/proposal.schema.json +145 -0
- package/schemas/v2/registry-version-manifest.schema.json +292 -0
- package/schemas/v2/residency.schema.json +19 -0
- package/schemas/v2/run-ancestry-response.schema.json +68 -0
- package/schemas/v2/run-diff-response.schema.json +80 -0
- package/schemas/v2/run-event-payloads.schema.json +4588 -0
- package/schemas/v2/run-event.schema.json +324 -0
- package/schemas/v2/run-options.schema.json +90 -0
- package/schemas/v2/run-orchestrator-decided-event.schema.json +22 -0
- package/schemas/v2/run-snapshot.schema.json +207 -0
- package/schemas/v2/security-advisory.schema.json +138 -0
- package/schemas/v2/self-hosted-runner-dispatch-frame.schema.json +119 -0
- package/schemas/v2/self-hosted-runner-registration.schema.json +74 -0
- package/schemas/v2/self-hosted-runner-result-frame.schema.json +47 -0
- package/schemas/v2/subject-link.schema.json +69 -0
- package/schemas/v2/subject.schema.json +597 -0
- package/schemas/v2/suspend-request.schema.json +370 -0
- package/schemas/v2/tool-descriptor.schema.json +150 -0
- package/schemas/v2/trigger-event.schema.json +508 -0
- package/schemas/v2/trigger-subscription-registration.schema.json +99 -0
- package/schemas/v2/trigger-subscription.schema.json +79 -0
- package/schemas/v2/ui-plugin-message.schema.json +149 -0
- package/schemas/v2/workflow-chain-pack-manifest.schema.json +610 -0
- package/schemas/v2/workflow-definition.schema.json +864 -0
- package/schemas/v2/workload-identity.schema.json +115 -0
- package/schemas/v2/workspace-file-create.schema.json +23 -0
- package/schemas/v2/workspace-file.schema.json +45 -0
- package/schemas/workflow-chain-pack-manifest.schema.json +615 -0
- package/schemas/workflow-definition.schema.json +850 -0
- package/schemas/workload-identity.schema.json +79 -0
- package/schemas/workspace-file-create.schema.json +20 -0
- package/schemas/workspace-file.schema.json +39 -0
- package/spec/v1/alias-detectors.json +283 -0
- package/spec/v1/capability-declaration-classes.json +176 -0
- package/spec/v1/core-standard-manifest.json +423 -0
- package/spec/v1/deprecations.json +854 -0
- package/spec/v1/deprecations.schema.json +187 -0
- package/spec/v1/event-codemap.json +1089 -0
- package/spec/v1/event-codemap.schema.json +41 -0
- package/spec/v1/extensions.json +1273 -0
- package/spec/v1/gaps.json +14581 -0
- package/spec/v1/gaps.schema.json +55 -0
- package/spec/v1/migrations.json +1822 -0
- package/spec/v1/migrations.schema.json +44 -0
- package/spec/v1/operation-path-manifest.json +462 -0
- package/spec/v1/spec-gaps.json +4823 -0
- package/spec/v2/declaration.json +1862 -0
- package/spec/v2/declaration.schema.json +305 -0
- package/spec/v2/errors.json +837 -0
- package/spec/v2/event-codemap.json +1099 -0
- package/spec/v2/event-codemap.schema.json +114 -0
- package/spec/v2/ext/provider-idempotency/registry.json +6 -0
- package/spec/v2/facets/a2a.schema.json +51 -0
- package/spec/v2/facets/aiProviders.schema.json +56 -0
- package/spec/v2/facets/auth.schema.json +91 -0
- package/spec/v2/facets/heartbeat.schema.json +20 -0
- package/spec/v2/facets/interrupt.schema.json +29 -0
- package/spec/v2/facets/mcp.schema.json +81 -0
- package/spec/v2/facets/replay.schema.json +34 -0
- package/spec/v2/facets/sandbox.schema.json +31 -0
- package/spec/v2/facets/webhooks.schema.json +18 -0
- package/spec/v2/path-manifest.json +279 -0
- package/spec/v2/peer-dependency-aliases.json +165 -0
- package/spec/v2/profiles.json +45 -0
- package/spec/v2/release.json +6 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/certification-bundle-v2.schema.json",
|
|
4
|
+
"title": "OpenWOP certification bundle, version 2 (RFC 0148 §C)",
|
|
5
|
+
"description": "A certification bundle that records what EXECUTED, not merely what passed.\n\nThe v1 bundle carried `{passed, failed, skipped}` scenario-file lists. Those three cannot express the distinction the whole program turns on: a scenario file counted as `passed` whether its assertions ran or its runner returned early, and a `skipped` entry could mean 'the operator excluded this', 'the requirement does not apply', or 'we could not check' — three claims with different consequences flattened into one word.\n\nv2 replaces the lists with per-requirement dispositions from RFC 0148 §A, and adds the counts §A.4 requires so a reader can tell a thorough run from a thin one without re-running it. `blocked` is its own total precisely because it is the one that invalidates a claim.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": true,
|
|
8
|
+
"required": ["bundleVersion", "suite", "host", "discovery", "claimedProfiles", "results", "scenarioManifestSha256", "targetConfigurationSha256"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"bundleVersion": { "const": "2" },
|
|
11
|
+
"suite": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"required": ["package", "version"],
|
|
15
|
+
"properties": {
|
|
16
|
+
"package": { "type": "string", "minLength": 1 },
|
|
17
|
+
"version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" }
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"host": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"additionalProperties": true,
|
|
23
|
+
"required": ["name", "version"],
|
|
24
|
+
"properties": {
|
|
25
|
+
"name": { "type": "string", "minLength": 1 },
|
|
26
|
+
"version": { "type": "string", "minLength": 1 },
|
|
27
|
+
"vendor": { "type": "string" },
|
|
28
|
+
"commit": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"minLength": 1,
|
|
31
|
+
"description": "OPTIONAL, self-reported source revision the host was built from. NOT sufficient to attribute the bundle: a commit does not determine the artifact unless the build is deterministic — a tier-2 host measured 283/22 vs 303/2 from two builds of one commit whose container image ran `npm install` rather than `npm ci` (conformance-certification.md §\"What a bundle attributes to\")."
|
|
32
|
+
},
|
|
33
|
+
"build": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"minLength": 1,
|
|
36
|
+
"description": "OPTIONAL, self-reported identifier of what ACTUALLY EXECUTED — this, not `commit`, is what a verifier needs to re-run the same thing. Which identifier is reachable depends on when the bundle is written: a container image digest is strongest but does not exist yet for a bundle baked INTO the image at certify time; a builder-assigned build id is available during the build; a digest of the resolved dependency manifest (lockfile or resolved tree) is available at certify time and pins the closure that actually varied in the measurement behind this field. State which kind it is rather than reaching for the strongest-sounding one. See conformance-certification.md §\"A bundle attributes to a BUILD\"."
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"discovery": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"additionalProperties": true,
|
|
43
|
+
"required": ["sha256", "document"],
|
|
44
|
+
"properties": {
|
|
45
|
+
"url": { "type": "string" },
|
|
46
|
+
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
47
|
+
"document": { "type": "object" }
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"claimedProfiles": {
|
|
51
|
+
"type": "array",
|
|
52
|
+
"minItems": 1,
|
|
53
|
+
"uniqueItems": true,
|
|
54
|
+
"items": { "type": "string", "pattern": "^openwop-[a-z0-9-]+$" },
|
|
55
|
+
"description": "RFC 0155 §E — canonical profile IDs only. Deprecated aliases go in `aliases`, never here, so a badge cannot be substantiated by a name that no longer means what it did."
|
|
56
|
+
},
|
|
57
|
+
"aliases": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"uniqueItems": true,
|
|
60
|
+
"items": { "type": "string" }
|
|
61
|
+
},
|
|
62
|
+
"results": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"additionalProperties": false,
|
|
65
|
+
"required": ["totals", "requirements"],
|
|
66
|
+
"properties": {
|
|
67
|
+
"totals": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"additionalProperties": false,
|
|
70
|
+
"required": ["executedPass", "executedFail", "skipped", "inapplicable", "blocked"],
|
|
71
|
+
"description": "RFC 0148 §A/§C. All five are REQUIRED — an omitted total is indistinguishable from zero, and `blocked: 0` asserted is a different claim from `blocked` unstated.",
|
|
72
|
+
"properties": {
|
|
73
|
+
"executedPass": { "type": "integer", "minimum": 0 },
|
|
74
|
+
"executedFail": { "type": "integer", "minimum": 0 },
|
|
75
|
+
"skipped": { "type": "integer", "minimum": 0 },
|
|
76
|
+
"inapplicable": { "type": "integer", "minimum": 0 },
|
|
77
|
+
"blocked": { "type": "integer", "minimum": 0 }
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"requirements": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"minItems": 1,
|
|
83
|
+
"description": "Per-requirement dispositions. Non-empty: a bundle with no requirement rows records no execution, and RFC 0148 §C exists because an empty evidence set was reading as proof.",
|
|
84
|
+
"items": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"additionalProperties": false,
|
|
87
|
+
"required": ["requirementId", "scenarioId", "disposition"],
|
|
88
|
+
"properties": {
|
|
89
|
+
"requirementId": { "type": "string", "minLength": 1 },
|
|
90
|
+
"scenarioId": { "type": "string", "minLength": 1 },
|
|
91
|
+
"disposition": { "enum": ["executed-pass", "executed-fail", "skipped", "inapplicable", "blocked"] },
|
|
92
|
+
"assertionCount": {
|
|
93
|
+
"type": "integer",
|
|
94
|
+
"minimum": 0,
|
|
95
|
+
"description": "How many assertions actually ran. `executed-pass` with `assertionCount: 0` is the vacuous pass this program exists to close, and a reader can now see it."
|
|
96
|
+
},
|
|
97
|
+
"witnessSha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
98
|
+
"detail": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"description": "Required by RFC 0148 §A for any disposition other than `executed-pass` — an unexplained `blocked` is an outcome nobody can act on."
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"scenarioManifestSha256": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"pattern": "^[0-9a-f]{64}$",
|
|
110
|
+
"description": "Digest of the scenario set this run executed. Without it a bundle cannot be distinguished from one produced against a different, smaller suite."
|
|
111
|
+
},
|
|
112
|
+
"targetConfigurationSha256": {
|
|
113
|
+
"type": "string",
|
|
114
|
+
"pattern": "^[0-9a-f]{64}$",
|
|
115
|
+
"description": "RFC 0147 §A.4 — configuration identity. Two runs of the same suite against differently-configured hosts are different evidence, and this is what says so."
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -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,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/channel-written-payload.schema.json",
|
|
4
|
+
"title": "ChannelWrittenPayload",
|
|
5
|
+
"description": "Payload of the `channel.written` RunEvent. Carries the WRITE INPUT (pre-reduction), NOT the post-reduction channel state. Replay reconstructs the post-reduction state by folding all `channel.written` events through the declared reducer. See channels-and-reducers.md §Channel write event.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["channel", "value", "reducer", "writtenAt"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"channel": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Channel name. MUST match a key declared under `WorkflowDefinition.channels`.",
|
|
12
|
+
"minLength": 1,
|
|
13
|
+
"maxLength": 256
|
|
14
|
+
},
|
|
15
|
+
"value": {
|
|
16
|
+
"description": "The write payload (any JSON value). Validated against `ChannelDeclaration.schema` if declared. The semantic shape depends on the channel's reducer — see per-reducer §Examples below.",
|
|
17
|
+
"type": ["object", "array", "string", "number", "boolean", "null"]
|
|
18
|
+
},
|
|
19
|
+
"reducer": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "The reducer name in effect at write time. Persisted on the event so replays remain deterministic even if the channel's reducer is later renamed/swapped. Canonical names: `replace`, `append`, `merge`, `counter`, `votes`, `feedback`. Custom reducers MUST use `vendor.<org>.<name>`.",
|
|
22
|
+
"pattern": "^(replace|append|merge|counter|votes|feedback|vendor\\.[a-z][a-z0-9_-]*\\.[a-z][a-z0-9_-]*)$"
|
|
23
|
+
},
|
|
24
|
+
"nodeId": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Optional — node that performed the write. Set for writes from `ctx.channels.write(...)`; absent for system-initiated writes.",
|
|
27
|
+
"minLength": 1,
|
|
28
|
+
"maxLength": 128
|
|
29
|
+
},
|
|
30
|
+
"writtenAt": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"format": "date-time",
|
|
33
|
+
"description": "ISO 8601 timestamp. The engine MAY normalize between this canonical form and platform-specific representations (e.g., Firestore Timestamp)."
|
|
34
|
+
},
|
|
35
|
+
"schemaVersion": {
|
|
36
|
+
"type": "integer",
|
|
37
|
+
"minimum": 1,
|
|
38
|
+
"description": "The `ChannelDeclaration.schemaVersion` that was live at write time. Persisted on the event so replays + later folds can detect breaking schema changes — the engine validates each event's value against the channel's CURRENT schema and refuses with `channel_schema_breaking_change` when the event's version is older than `current` AND not in `compatibleWith`. Defaults to 1 when omitted (back-compat with pre-C4 servers that didn't track versions). See channels-and-reducers.md §Channel schema migration. (closes C4)"
|
|
39
|
+
},
|
|
40
|
+
"sourceEngineId": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "Identifier of the engine instance that performed the write. Set when the writing engine differs from the engine that owns the parent run (e.g., a sub-workflow / cross-canvas-invoke writing to a parent's channel). Absent when the parent's own engine wrote. The fold side uses this to disambiguate origins for `channel-write` trigger filtering (`onlyFrom: 'child'` matches when this field is set and references a known child engine). See channels-and-reducers.md §Distributed reducers. (closes C2)",
|
|
43
|
+
"minLength": 1,
|
|
44
|
+
"maxLength": 256
|
|
45
|
+
},
|
|
46
|
+
"sourceRunId": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "RunId of the cross-engine writer when `sourceEngineId` is set. Absent for owner-engine writes. Lets debugging surfaces (Run Timeline View per replay.md) link the write back to the originating sub-workflow / invoke run. (closes C2)",
|
|
49
|
+
"minLength": 1,
|
|
50
|
+
"maxLength": 128
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"additionalProperties": false,
|
|
54
|
+
"$comment": "Per-reducer expectations on the `value` field (informative — not enforced at this schema level; servers SHOULD validate against `ChannelDeclaration.schema` if declared):\n\n - `replace`: any value; supersedes prior state.\n - `append`: any value; appended to the channel's array. Bounded by `ChannelDeclaration.maxSize` if set.\n - `merge`: MUST be a JSON object (`{...}`); shallow-merged into prior state.\n - `counter`: MUST be a number; added to the running total. Negative allowed.\n - `votes`: typically `{voter: string, vote: string|boolean|number, ...}`; the reducer accumulates per-voter latest-wins.\n - `feedback`: typically `{author: string, text: string, timestamp: string, ...}`; the reducer appends bounded by `maxSize`.\n - `vendor.*`: implementation-defined; external clients SHOULD treat as `replace` if the reducer name is unknown."
|
|
55
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
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": [
|
|
8
|
+
"name",
|
|
9
|
+
"version",
|
|
10
|
+
"kind",
|
|
11
|
+
"engines",
|
|
12
|
+
"cards"
|
|
13
|
+
],
|
|
14
|
+
"additionalProperties": false,
|
|
15
|
+
"properties": {
|
|
16
|
+
"kind": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"const": "card",
|
|
19
|
+
"description": "Pack kind discriminator. MUST be the literal string `\"card\"`."
|
|
20
|
+
},
|
|
21
|
+
"name": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Reverse-DNS pack name per `node-packs.md` §Naming. Reserved scopes are identical (`core.*` / `vendor.<org>.*` / `community.<author>.*` / `private.<host>.*`).",
|
|
24
|
+
"pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$",
|
|
25
|
+
"minLength": 1,
|
|
26
|
+
"maxLength": 256
|
|
27
|
+
},
|
|
28
|
+
"version": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "Pack-level SemVer 2.0.0.",
|
|
31
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$"
|
|
32
|
+
},
|
|
33
|
+
"description": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"maxLength": 1024
|
|
36
|
+
},
|
|
37
|
+
"author": {
|
|
38
|
+
"type": "string"
|
|
39
|
+
},
|
|
40
|
+
"license": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "SPDX license identifier (e.g., `Apache-2.0`, `MIT`)."
|
|
43
|
+
},
|
|
44
|
+
"homepage": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"format": "uri"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"format": "uri"
|
|
51
|
+
},
|
|
52
|
+
"keywords": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"items": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"maxLength": 64
|
|
57
|
+
},
|
|
58
|
+
"maxItems": 50
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"required": [
|
|
63
|
+
"openwop"
|
|
64
|
+
],
|
|
65
|
+
"properties": {
|
|
66
|
+
"openwop": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"description": "Semver range — which openwop protocol versions this pack works against."
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"additionalProperties": true
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"additionalProperties": {
|
|
76
|
+
"type": "string"
|
|
77
|
+
},
|
|
78
|
+
"description": "Other packs this pack depends on (e.g., the artifact-type pack declaring this card's `outputArtifactType`). Map of pack name → semver range."
|
|
79
|
+
},
|
|
80
|
+
"peerDependencies": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"additionalProperties": {
|
|
83
|
+
"type": "string"
|
|
84
|
+
},
|
|
85
|
+
"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."
|
|
86
|
+
},
|
|
87
|
+
"cards": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"minItems": 1,
|
|
90
|
+
"items": {
|
|
91
|
+
"$ref": "#/$defs/Card"
|
|
92
|
+
},
|
|
93
|
+
"description": "AI chat card definitions this pack contributes. Each MUST have a unique `cardTypeId` within the pack."
|
|
94
|
+
},
|
|
95
|
+
"signing": {
|
|
96
|
+
"$ref": "#/$defs/Signing"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"$defs": {
|
|
100
|
+
"Card": {
|
|
101
|
+
"type": "object",
|
|
102
|
+
"required": [
|
|
103
|
+
"cardTypeId",
|
|
104
|
+
"prompt"
|
|
105
|
+
],
|
|
106
|
+
"additionalProperties": false,
|
|
107
|
+
"properties": {
|
|
108
|
+
"cardTypeId": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"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.*`.",
|
|
111
|
+
"pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$",
|
|
112
|
+
"minLength": 1,
|
|
113
|
+
"maxLength": 256
|
|
114
|
+
},
|
|
115
|
+
"schemaVersion": {
|
|
116
|
+
"type": "integer",
|
|
117
|
+
"minimum": 0,
|
|
118
|
+
"description": "Non-negative integer card-schema version (the envelope/artifact integer-version axis). Absent ⇒ treated as 0."
|
|
119
|
+
},
|
|
120
|
+
"prompt": {
|
|
121
|
+
"$ref": "#/$defs/PromptSpec"
|
|
122
|
+
},
|
|
123
|
+
"inputs": {
|
|
124
|
+
"type": "array",
|
|
125
|
+
"items": {
|
|
126
|
+
"$ref": "#/$defs/InputField"
|
|
127
|
+
},
|
|
128
|
+
"description": "Typed input fields the card collects. The portable subset only; host-specific widget kinds extend via `vendor.*`/`x-` prefixed `type` values."
|
|
129
|
+
},
|
|
130
|
+
"outputArtifactType": {
|
|
131
|
+
"type": "string",
|
|
132
|
+
"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.",
|
|
133
|
+
"pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$"
|
|
134
|
+
},
|
|
135
|
+
"outputSchemaRef": {
|
|
136
|
+
"type": "string",
|
|
137
|
+
"minLength": 1,
|
|
138
|
+
"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`."
|
|
139
|
+
},
|
|
140
|
+
"requiredModelCapabilities": {
|
|
141
|
+
"type": "array",
|
|
142
|
+
"items": {
|
|
143
|
+
"type": "string",
|
|
144
|
+
"pattern": "^([a-z][a-z0-9-]*|x-host-[a-z][a-z0-9-]*-[a-z][a-z0-9-]*)$"
|
|
145
|
+
},
|
|
146
|
+
"uniqueItems": true,
|
|
147
|
+
"maxItems": 32,
|
|
148
|
+
"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>-`)."
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"patternProperties": {
|
|
152
|
+
"^(x-|vendor\\.)": {
|
|
153
|
+
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md` §\"Vendor-prefixed namespaces\", which requires a client receiving an unknown vendor-prefixed field to treat it as opaque — a field that `additionalProperties: false` previously made impossible to carry. SECURITY: an extension value is PACK-AUTHORED, therefore untrusted; \"ignore\" means ignore. See `node-packs.md` §\"Vendor extensions on pack manifests\" and the `pack-manifest-extension-opaque` invariant."
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"PromptSpec": {
|
|
158
|
+
"type": "object",
|
|
159
|
+
"required": [
|
|
160
|
+
"template",
|
|
161
|
+
"placeholderMapping"
|
|
162
|
+
],
|
|
163
|
+
"additionalProperties": false,
|
|
164
|
+
"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).",
|
|
165
|
+
"properties": {
|
|
166
|
+
"template": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"minLength": 1,
|
|
169
|
+
"description": "Prompt body with `{{placeholder}}` slots."
|
|
170
|
+
},
|
|
171
|
+
"systemPrompt": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"description": "Optional system prompt."
|
|
174
|
+
},
|
|
175
|
+
"placeholderMapping": {
|
|
176
|
+
"type": "object",
|
|
177
|
+
"additionalProperties": {
|
|
178
|
+
"type": "string"
|
|
179
|
+
},
|
|
180
|
+
"description": "Map of `{{placeholder}}` name → input path (e.g. `\"spec\": \"inputs.spec\"`)."
|
|
181
|
+
},
|
|
182
|
+
"temperature": {
|
|
183
|
+
"type": "number",
|
|
184
|
+
"minimum": 0,
|
|
185
|
+
"maximum": 2
|
|
186
|
+
},
|
|
187
|
+
"maxTokens": {
|
|
188
|
+
"type": "integer",
|
|
189
|
+
"minimum": 1
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
"InputField": {
|
|
194
|
+
"type": "object",
|
|
195
|
+
"required": [
|
|
196
|
+
"id",
|
|
197
|
+
"type"
|
|
198
|
+
],
|
|
199
|
+
"additionalProperties": false,
|
|
200
|
+
"properties": {
|
|
201
|
+
"id": {
|
|
202
|
+
"type": "string",
|
|
203
|
+
"minLength": 1,
|
|
204
|
+
"pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$"
|
|
205
|
+
},
|
|
206
|
+
"type": {
|
|
207
|
+
"type": "string",
|
|
208
|
+
"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.",
|
|
209
|
+
"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-]*)$"
|
|
210
|
+
},
|
|
211
|
+
"label": {
|
|
212
|
+
"type": "string"
|
|
213
|
+
},
|
|
214
|
+
"required": {
|
|
215
|
+
"type": "boolean"
|
|
216
|
+
},
|
|
217
|
+
"default": {},
|
|
218
|
+
"options": {
|
|
219
|
+
"type": "array",
|
|
220
|
+
"items": {
|
|
221
|
+
"type": "string"
|
|
222
|
+
},
|
|
223
|
+
"description": "Choices for `type: \"select\"`."
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
"Signing": {
|
|
228
|
+
"type": "object",
|
|
229
|
+
"description": "Optional signing metadata. See node-packs.md §signing.",
|
|
230
|
+
"additionalProperties": false,
|
|
231
|
+
"properties": {
|
|
232
|
+
"publicKeyRef": {
|
|
233
|
+
"type": "string"
|
|
234
|
+
},
|
|
235
|
+
"signatureRef": {
|
|
236
|
+
"type": "string"
|
|
237
|
+
},
|
|
238
|
+
"method": {
|
|
239
|
+
"type": "string",
|
|
240
|
+
"enum": [
|
|
241
|
+
"manual",
|
|
242
|
+
"sigstore"
|
|
243
|
+
]
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
"patternProperties": {
|
|
249
|
+
"^(x-|vendor\\.)": {
|
|
250
|
+
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md` §\"Vendor-prefixed namespaces\", which requires a client receiving an unknown vendor-prefixed field to treat it as opaque — a field that `additionalProperties: false` previously made impossible to carry. SECURITY: an extension value is PACK-AUTHORED, therefore untrusted; \"ignore\" means ignore. See `node-packs.md` §\"Vendor extensions on pack manifests\" and the `pack-manifest-extension-opaque` invariant."
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/compensation-policy.schema.json",
|
|
4
|
+
"title": "CompensationPolicy",
|
|
5
|
+
"description": "RFC 0151 §B — the workflow-level compensation policy, carried as the reserved `settings.compensation` key on `WorkflowDefinition` (`workflow-definition.schema.json` §WorkflowSettings). Node-level `compensation` declarations say WHAT the inverse action is; this policy says WHEN the host starts an unwind and HOW it runs one — which failures qualify (`triggers`), the ordering model, retry/timeout defaults for inverse actions, what happens when an inverse action exhausts its retries, whether approval is escalated, and what a parent cancel does to an active unwind. Closed and additive. A host that advertises `capabilities.compensation` MUST validate the policy at workflow registration and refuse one that names an `orderingModel` or `profileVersion` it does not advertise (`validation_error`). A host that does NOT advertise the family MUST refuse a workflow that carries this key with `capability_required` (`details.requiredCapability: \"compensation\"`, per `capabilities.md` §\"Unsupported capability — refusal contract\") rather than accept it silently: accepting a policy the host will never honour tells the author an unwind will happen when it will not, which is RFC 0148 §B's advertise-and-opt-out failure with the sign flipped. Nothing here can WEAKEN a node's own declaration — a node's `compensation.requiresApproval: true` and `retry` bounds always win over the policy's defaults. Policy is authored, not per-run: there is deliberately no run-options overlay, because a per-run caller who could lower approval scope or drop a trigger would be authorizing their own unwind (RFC 0147 R9).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["triggers"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"profileVersion": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^[1-9][0-9]*$",
|
|
13
|
+
"description": "The RFC 0151 profile version this policy was authored against. OPTIONAL; when present it MUST equal the host's advertised `capabilities.compensation.profileVersion` or registration fails, because `profileVersion` is part of the inverse-action identity (§C) and a workflow authored under one ordering rule MUST NOT mint identities under another."
|
|
14
|
+
},
|
|
15
|
+
"orderingModel": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"enum": ["reverse-completion", "dependency-graph"],
|
|
18
|
+
"default": "reverse-completion",
|
|
19
|
+
"description": "RFC 0151 §A/§C. `reverse-completion` (default) orders inverse actions by descending durable forward-completion sequence; `dependency-graph` MUST be a DAG and preserve reverse dependency order. The value MUST be one of the host's advertised `capabilities.compensation.orderingModels[]`; a host MUST refuse the workflow at registration otherwise, so an unwind never discovers at failure time that its ordering rule is unimplemented."
|
|
20
|
+
},
|
|
21
|
+
"triggers": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"minItems": 1,
|
|
24
|
+
"uniqueItems": true,
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"enum": ["node-failure", "run-cancel", "cap-breach", "operator-request"]
|
|
28
|
+
},
|
|
29
|
+
"description": "Which events start an unwind. REQUIRED and closed — a policy that names no trigger is not a policy, and an open string here would let a host and an author disagree about a word. `node-failure`: a node reaches terminal failure after its own RFC 0009 retry policy and the run cannot continue. `run-cancel`: an RFC 0094 cancel is accepted while committed effects exist. `cap-breach`: an RFC 0058 / RFC 0084 `cap.breached` hard stop. `operator-request`: an authorized operator explicitly requests an unwind (§E) — MUST be authorized per RFC 0049, binding tenant, principal, action, and plan version. A trigger not listed here does NOT start an unwind; the run ends with its committed effects in place and `compensationStatus: none`, which is the honest shape — no generic rollback is inferred from an undeclared trigger (RFC 0151 §Compatibility)."
|
|
30
|
+
},
|
|
31
|
+
"retry": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"additionalProperties": false,
|
|
34
|
+
"description": "Defaults for inverse actions whose node-level `compensation.retry` is absent. A node's own bounds always win. Retries reuse the inverse action's stable identity (§C) — never a fresh one, or a transient refund failure mints a second refund.",
|
|
35
|
+
"properties": {
|
|
36
|
+
"maxAttempts": { "type": "integer", "minimum": 1 },
|
|
37
|
+
"backoffMs": { "type": "integer", "minimum": 0 }
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"timeoutMs": {
|
|
41
|
+
"type": "integer",
|
|
42
|
+
"minimum": 1,
|
|
43
|
+
"description": "Wall-clock ceiling per inverse-action attempt, in milliseconds. A timed-out attempt counts against `retry.maxAttempts`; it is not a distinct outcome."
|
|
44
|
+
},
|
|
45
|
+
"exhaustedDisposition": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"enum": ["record-outcome", "manual-intervention"],
|
|
48
|
+
"default": "record-outcome",
|
|
49
|
+
"description": "RFC 0151 §E — what the host does when an inverse action exhausts its retries. Either way the run MUST route to RFC 0053 dead-letter handling and the run's `compensationStatus` follows the §D fold (`partial` or `failed`). `record-outcome` (default): record the failure and continue unwinding the remaining actions. `manual-intervention`: additionally emit `compensation.manual_intervention_required` and stop issuing further inverse actions until an authorized operator retries, skips with recorded justification, substitutes a registered action, or terminates as uncompensated — the rollup reads `manual` while that is unresolved. Requires `capabilities.compensation.manualIntervention: true`; a host that does not advertise it MUST refuse a policy that selects it."
|
|
50
|
+
},
|
|
51
|
+
"approvalScope": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"enum": ["declared", "all"],
|
|
54
|
+
"default": "declared",
|
|
55
|
+
"description": "RFC 0151 §E. `declared` (default): only inverse actions whose node sets `compensation.requiresApproval: true` create an RFC 0051 approval interrupt. `all`: every inverse action does, regardless of the node's declaration. This value can only ESCALATE — there is no `none`, because a policy MUST NOT strip an approval a node declared for itself (RFC 0147 R9: an inverse action can itself be harmful)."
|
|
56
|
+
},
|
|
57
|
+
"onParentCancel": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"enum": ["continue", "pause", "manual"],
|
|
60
|
+
"default": "continue",
|
|
61
|
+
"description": "RFC 0151 §C — cancellation of the parent run MUST NOT silently abandon an active compensation. `continue`: the unwind runs to its terminal rollup. `pause`: emit `compensation.paused` and hold for authorized intervention (the run's own `status` carries the wait). `manual`: emit `compensation.manual_intervention_required` and stop; rollup reads `manual`. `pause`/`manual` require `capabilities.compensation.manualIntervention: true`."
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|