@openwop/openwop-conformance 1.0.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/LICENSE +201 -0
- package/README.md +241 -0
- package/api/asyncapi.yaml +481 -0
- package/api/openapi.yaml +830 -0
- package/api/redocly.yaml +8 -0
- package/coverage.md +80 -0
- package/dist/cli.js +161 -0
- package/fixtures/conformance-a2a-task-roundtrip.json +27 -0
- package/fixtures/conformance-agent-identity.json +27 -0
- package/fixtures/conformance-agent-low-confidence.json +29 -0
- package/fixtures/conformance-agent-memory-cross-tenant.json +28 -0
- package/fixtures/conformance-agent-memory-redaction.json +32 -0
- package/fixtures/conformance-agent-memory-roundtrip.json +32 -0
- package/fixtures/conformance-agent-memory-ttl.json +31 -0
- package/fixtures/conformance-agent-pack-export.json +26 -0
- package/fixtures/conformance-agent-pack-install.json +26 -0
- package/fixtures/conformance-agent-pack-provenance.json +31 -0
- package/fixtures/conformance-agent-reasoning.json +29 -0
- package/fixtures/conformance-approval.json +27 -0
- package/fixtures/conformance-cancellable.json +33 -0
- package/fixtures/conformance-cap-breach.json +27 -0
- package/fixtures/conformance-capability-missing.json +23 -0
- package/fixtures/conformance-channel-ttl.json +60 -0
- package/fixtures/conformance-clarification.json +30 -0
- package/fixtures/conformance-conversation-capability-negotiation.json +23 -0
- package/fixtures/conformance-conversation-lifecycle.json +32 -0
- package/fixtures/conformance-conversation-replay.json +33 -0
- package/fixtures/conformance-conversation-vs-clarification.json +26 -0
- package/fixtures/conformance-delay.json +33 -0
- package/fixtures/conformance-dispatch-loop.json +38 -0
- package/fixtures/conformance-failure.json +23 -0
- package/fixtures/conformance-idempotent.json +30 -0
- package/fixtures/conformance-identity.json +32 -0
- package/fixtures/conformance-interrupt-auth-required.json +28 -0
- package/fixtures/conformance-interrupt-external-event.json +33 -0
- package/fixtures/conformance-interrupt-parent-child-cancel-child.json +27 -0
- package/fixtures/conformance-interrupt-parent-child-cancel.json +26 -0
- package/fixtures/conformance-interrupt-quorum.json +30 -0
- package/fixtures/conformance-mcp-tool-roundtrip.json +32 -0
- package/fixtures/conformance-message-reducer.json +31 -0
- package/fixtures/conformance-multi-node.json +21 -0
- package/fixtures/conformance-noop.json +23 -0
- package/fixtures/conformance-orchestrator-dispatch.json +47 -0
- package/fixtures/conformance-orchestrator-low-confidence.json +41 -0
- package/fixtures/conformance-orchestrator-terminate.json +44 -0
- package/fixtures/conformance-stream-text.json +26 -0
- package/fixtures/conformance-subworkflow-child.json +21 -0
- package/fixtures/conformance-subworkflow-parent.json +49 -0
- package/fixtures/conformance-version-fold.json +23 -0
- package/fixtures/conformance-wasm-pack-roundtrip.json +25 -0
- package/fixtures/pack-manifests/pack-private-example.json +26 -0
- package/fixtures.md +404 -0
- package/package.json +48 -0
- package/schemas/README.md +75 -0
- package/schemas/agent-manifest.schema.json +107 -0
- package/schemas/agent-ref.schema.json +53 -0
- package/schemas/capabilities.schema.json +287 -0
- package/schemas/channel-written-payload.schema.json +55 -0
- package/schemas/conversation-event.schema.json +120 -0
- package/schemas/conversation-turn.schema.json +72 -0
- package/schemas/debug-bundle.schema.json +196 -0
- package/schemas/dispatch-config.schema.json +46 -0
- package/schemas/error-envelope.schema.json +25 -0
- package/schemas/memory-entry.schema.json +36 -0
- package/schemas/memory-list-options.schema.json +21 -0
- package/schemas/node-pack-manifest.schema.json +235 -0
- package/schemas/orchestrator-decision.schema.json +60 -0
- package/schemas/run-event-payloads.schema.json +663 -0
- package/schemas/run-event.schema.json +116 -0
- package/schemas/run-options.schema.json +81 -0
- package/schemas/run-orchestrator-decided-event.schema.json +20 -0
- package/schemas/run-snapshot.schema.json +121 -0
- package/schemas/suspend-request.schema.json +182 -0
- package/schemas/workflow-definition.schema.json +430 -0
- package/src/cli.ts +187 -0
- package/src/lib/a2a-fake-peer.ts +233 -0
- package/src/lib/canaries.ts +186 -0
- package/src/lib/driver.ts +96 -0
- package/src/lib/env.ts +49 -0
- package/src/lib/fixtures.ts +93 -0
- package/src/lib/mcp-fake-server.ts +185 -0
- package/src/lib/multi-agent-capabilities.ts +155 -0
- package/src/lib/multiProcess.ts +141 -0
- package/src/lib/otel-collector.ts +312 -0
- package/src/lib/paths.ts +198 -0
- package/src/lib/polling.ts +81 -0
- package/src/lib/profiles.ts +258 -0
- package/src/lib/sse.ts +172 -0
- package/src/scenarios/a2a-task-roundtrip.test.ts +149 -0
- package/src/scenarios/agentConfidenceEscalation.test.ts +61 -0
- package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +54 -0
- package/src/scenarios/agentMemoryRedactionContract.test.ts +46 -0
- package/src/scenarios/agentMemoryRoundTrip.test.ts +52 -0
- package/src/scenarios/agentMemoryTtlExpiry.test.ts +47 -0
- package/src/scenarios/agentMessageReducer.test.ts +57 -0
- package/src/scenarios/agentMetadata.test.ts +56 -0
- package/src/scenarios/agentPackExport.test.ts +45 -0
- package/src/scenarios/agentPackInstall.test.ts +50 -0
- package/src/scenarios/agentPackProvenance.test.ts +53 -0
- package/src/scenarios/agentReasoningEvents.test.ts +72 -0
- package/src/scenarios/append-ordering.test.ts +91 -0
- package/src/scenarios/approval-payload.test.ts +120 -0
- package/src/scenarios/audit-log-integrity.test.ts +106 -0
- package/src/scenarios/auth.test.ts +55 -0
- package/src/scenarios/byok-roundtrip.test.ts +166 -0
- package/src/scenarios/cancellation.test.ts +68 -0
- package/src/scenarios/cap-breach.test.ts +149 -0
- package/src/scenarios/channel-ttl.test.ts +70 -0
- package/src/scenarios/configurable-schema.test.ts +76 -0
- package/src/scenarios/conversationCapabilityNegotiation.test.ts +39 -0
- package/src/scenarios/conversationLifecycle.test.ts +64 -0
- package/src/scenarios/conversationReplayDeterminism.test.ts +52 -0
- package/src/scenarios/conversationVsLegacySuspend.test.ts +46 -0
- package/src/scenarios/cost-attribution.test.ts +207 -0
- package/src/scenarios/debugBundle.test.ts +222 -0
- package/src/scenarios/discovery.test.ts +147 -0
- package/src/scenarios/dispatchLoop.test.ts +52 -0
- package/src/scenarios/errors.test.ts +144 -0
- package/src/scenarios/eventOrdering.test.ts +144 -0
- package/src/scenarios/failure-path.test.ts +46 -0
- package/src/scenarios/fixtures-gating.test.ts +137 -0
- package/src/scenarios/fixtures-valid.test.ts +140 -0
- package/src/scenarios/highConcurrency.test.ts +263 -0
- package/src/scenarios/idempotency.test.ts +83 -0
- package/src/scenarios/idempotencyRetry.test.ts +130 -0
- package/src/scenarios/identity-passthrough.test.ts +54 -0
- package/src/scenarios/interrupt-approval.test.ts +97 -0
- package/src/scenarios/interrupt-auth-required-resume.test.ts +88 -0
- package/src/scenarios/interrupt-clarification.test.ts +45 -0
- package/src/scenarios/interrupt-external-event-correlation.test.ts +113 -0
- package/src/scenarios/interrupt-parent-child-cascade.test.ts +102 -0
- package/src/scenarios/interrupt-quorum-resolution.test.ts +97 -0
- package/src/scenarios/interruptRace.test.ts +176 -0
- package/src/scenarios/maliciousManifest.test.ts +154 -0
- package/src/scenarios/mcp-discoverability.test.ts +129 -0
- package/src/scenarios/mcp-tool-roundtrip.test.ts +149 -0
- package/src/scenarios/multi-node-ordering.test.ts +60 -0
- package/src/scenarios/multi-region-idempotency.test.ts +52 -0
- package/src/scenarios/orchestratorConservativePath.test.ts +63 -0
- package/src/scenarios/orchestratorDispatch.test.ts +66 -0
- package/src/scenarios/orchestratorTermination.test.ts +54 -0
- package/src/scenarios/otel-emission.test.ts +113 -0
- package/src/scenarios/otel-trace-propagation.test.ts +90 -0
- package/src/scenarios/pack-registry-publish.test.ts +93 -0
- package/src/scenarios/pack-registry.test.ts +328 -0
- package/src/scenarios/pause-resume.test.ts +109 -0
- package/src/scenarios/policies.test.ts +162 -0
- package/src/scenarios/profileDerivation.test.ts +335 -0
- package/src/scenarios/providerPolicyEnforcement.test.ts +132 -0
- package/src/scenarios/rate-limit-envelope.test.ts +97 -0
- package/src/scenarios/redaction.test.ts +254 -0
- package/src/scenarios/redactionAdversarial.test.ts +162 -0
- package/src/scenarios/replay-fork-arbitrary.test.ts +347 -0
- package/src/scenarios/replay-fork.test.ts +216 -0
- package/src/scenarios/replayDeterminism.test.ts +171 -0
- package/src/scenarios/route-coverage.test.ts +129 -0
- package/src/scenarios/runs-lifecycle.test.ts +65 -0
- package/src/scenarios/runtime-capabilities.test.ts +118 -0
- package/src/scenarios/spec-corpus-validity.test.ts +1257 -0
- package/src/scenarios/staleClaim.test.ts +223 -0
- package/src/scenarios/stream-modes-buffer.test.ts +148 -0
- package/src/scenarios/stream-modes-mixed.test.ts +149 -0
- package/src/scenarios/stream-modes.test.ts +139 -0
- package/src/scenarios/streamReconnect.test.ts +162 -0
- package/src/scenarios/subworkflow.test.ts +126 -0
- package/src/scenarios/version-negotiation.test.ts +157 -0
- package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +47 -0
- package/src/scenarios/wasm-pack-invoke-completed.test.ts +69 -0
- package/src/scenarios/wasm-pack-invoke-suspended.test.ts +74 -0
- package/src/scenarios/wasm-pack-load.test.ts +75 -0
- package/src/scenarios/wasm-pack-memory-cap.test.ts +43 -0
- package/src/scenarios/wasm-pack-replay-determinism.test.ts +61 -0
- package/src/scenarios/webhook-sig-algorithm.test.ts +61 -0
- package/src/setup.ts +173 -0
- package/vitest.config.ts +17 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/agent-ref.schema.json",
|
|
4
|
+
"title": "AgentRef",
|
|
5
|
+
"description": "Slim wire-shape projection of an agent's identity. Carried on `RunSnapshot.agent` (the active worker for a run), `RunSnapshot.runOrchestrator` (the supervisor identity for the run's lifetime), `WorkflowNode.agent` (compile-time pinning), and the `agent.*` event family's payload `agentId` field. Distinct from `AgentManifest` (which is the pack-distribution descriptor for an agent definition — see `agent-manifest.schema.json`); `AgentRef` is the runtime projection, not the definition.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["agentId"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"agentId": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"minLength": 3,
|
|
12
|
+
"maxLength": 256,
|
|
13
|
+
"description": "Globally-unique agent identifier. Naming convention mirrors `node-packs.md` §Naming — `<tier>.<org>.<pack>.<agent>` for vendor/community packs (`vendor.acme.support.tier1`); `core.<name>` for spec-canonical agents (`core.chat`, `core.research`); `host:<id>` slim-runtime form for host-internal AgentRefs that don't ship as packs. Host-internal `host:<id>` agents are valid AgentRefs at the wire level but MUST NOT be published as manifests (the `host:` namespace is reserved for runtime synthesis)."
|
|
14
|
+
},
|
|
15
|
+
"name": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"maxLength": 256,
|
|
18
|
+
"description": "Optional human-readable display name. Surfaces in audit / debug-bundle UIs alongside `agentId`. Hosts MAY populate from the agent's `AgentManifest.name` field at projection time; consumers MUST treat absence as 'name unknown' rather than synthesize."
|
|
19
|
+
},
|
|
20
|
+
"modelClass": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"enum": [
|
|
23
|
+
"reasoning",
|
|
24
|
+
"tool-using",
|
|
25
|
+
"chat",
|
|
26
|
+
"code",
|
|
27
|
+
"vision",
|
|
28
|
+
"multimodal",
|
|
29
|
+
"embedding",
|
|
30
|
+
"classification",
|
|
31
|
+
"retrieval"
|
|
32
|
+
],
|
|
33
|
+
"description": "Closed enum describing the agent's model-class envelope. Hosts that advertise `capabilities.agents.modelClasses` use this enum to filter agents at install / dispatch time. Vendor extensions ship under `<vendor>.<class>` per `host-extensions.md` (e.g., `openwop.creative-writing`) — vendor-extension values are NOT in the closed enum and MUST surface as unknown to strict consumers."
|
|
34
|
+
},
|
|
35
|
+
"memoryRef": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"minLength": 1,
|
|
38
|
+
"maxLength": 256,
|
|
39
|
+
"description": "Optional host-defined opaque reference to this agent's long-term memory. Resolved by the host's `MemoryAdapter` (see `agent-memory.md`); the wire-level shape is host-defined per the L2 disposition (mirrors `host-extensions.md` namespace conventions). Hosts that don't implement long-term memory MUST NOT emit this field; hosts that do MUST honor cross-tenant isolation (CTI-1)."
|
|
40
|
+
},
|
|
41
|
+
"version": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"maxLength": 64,
|
|
44
|
+
"description": "Optional version pin for the agent definition (matches `AgentManifest.version`). Lets audit consumers trace which version of an agent definition was active for a given run. Pinning is encouraged for replay determinism; absent values mean 'host's current resolution of the agent'."
|
|
45
|
+
},
|
|
46
|
+
"sourceManifestId": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"maxLength": 256,
|
|
49
|
+
"description": "Optional provenance pointer back to the `AgentManifest.agentId` this AgentRef was projected from. Lets pack-aware hosts trace runtime AgentRefs back to their distribution origin. Absent for host-internal `host:<id>` agents (which have no manifest)."
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"additionalProperties": false
|
|
53
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/capabilities.schema.json",
|
|
4
|
+
"title": "Capabilities",
|
|
5
|
+
"description": "openwop capability declaration returned from `GET /.well-known/openwop`. Required v1 fields identify the protocol version, envelope catalog, schema versions, and base limits. Optional v1 fields have stable shapes but MAY be omitted when unsupported.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["protocolVersion", "supportedEnvelopes", "schemaVersions", "limits"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"protocolVersion": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "openwop protocol version the server speaks (e.g., `1.0`). Independent of `engineVersion` (which gates persisted-doc compatibility per version-negotiation.md).",
|
|
12
|
+
"minLength": 1
|
|
13
|
+
},
|
|
14
|
+
"supportedEnvelopes": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"items": { "type": "string", "minLength": 1 },
|
|
17
|
+
"description": "Envelope `type` strings the engine recognizes (e.g., `prd.create`, `theme.create`, `tasks.create`, `clarification.request`)."
|
|
18
|
+
},
|
|
19
|
+
"schemaVersions": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"additionalProperties": { "type": "integer", "minimum": 0 },
|
|
22
|
+
"description": "Active schema version per envelope type (e.g., `{ \"prd.create\": 2 }`)."
|
|
23
|
+
},
|
|
24
|
+
"limits": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["clarificationRounds", "schemaRounds", "envelopesPerTurn"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"clarificationRounds": {
|
|
29
|
+
"type": "integer",
|
|
30
|
+
"minimum": 0,
|
|
31
|
+
"description": "Maximum total clarification envelopes per task before the engine forces the LLM to proceed."
|
|
32
|
+
},
|
|
33
|
+
"schemaRounds": {
|
|
34
|
+
"type": "integer",
|
|
35
|
+
"minimum": 0,
|
|
36
|
+
"description": "Maximum total schema-validation rounds per envelope before the node fails."
|
|
37
|
+
},
|
|
38
|
+
"envelopesPerTurn": {
|
|
39
|
+
"type": "integer",
|
|
40
|
+
"minimum": 0,
|
|
41
|
+
"description": "Maximum total envelopes the LLM may emit in a single chat turn."
|
|
42
|
+
},
|
|
43
|
+
"maxNodeExecutions": {
|
|
44
|
+
"type": "integer",
|
|
45
|
+
"minimum": 1,
|
|
46
|
+
"description": "Engine-side ceiling on total node executions per run. Acts as the upper bound for `RunOptions.configurable.recursionLimit`. Optional in v1; hosts that advertise it MUST enforce it."
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"description": "Hard limits enforced by the engine. See capabilities.md §3."
|
|
51
|
+
},
|
|
52
|
+
"extensions": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"description": "Optional per-canvas-type or per-workflow extensions (additional envelope types, override limits)."
|
|
55
|
+
},
|
|
56
|
+
"implementation": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"description": "Optional v1 server identity (name + version + vendor).",
|
|
59
|
+
"properties": {
|
|
60
|
+
"name": { "type": "string" },
|
|
61
|
+
"version": { "type": "string" },
|
|
62
|
+
"vendor": { "type": "string" }
|
|
63
|
+
},
|
|
64
|
+
"additionalProperties": false
|
|
65
|
+
},
|
|
66
|
+
"engineVersion": {
|
|
67
|
+
"type": "integer",
|
|
68
|
+
"minimum": 0,
|
|
69
|
+
"description": "Optional v1 server engineVersion (see version-negotiation.md). Independent of `protocolVersion`."
|
|
70
|
+
},
|
|
71
|
+
"eventLogSchemaVersion": {
|
|
72
|
+
"type": "integer",
|
|
73
|
+
"minimum": 0,
|
|
74
|
+
"description": "Optional v1 server eventLogSchemaVersion (per-run subcollection contract)."
|
|
75
|
+
},
|
|
76
|
+
"supportedTransports": {
|
|
77
|
+
"type": "array",
|
|
78
|
+
"items": { "type": "string", "enum": ["rest", "mcp", "a2a", "grpc"] },
|
|
79
|
+
"description": "Optional v1 transport advertisement. REST is required whether or not this field is present."
|
|
80
|
+
},
|
|
81
|
+
"configurable": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"description": "Optional v1 per-run overlay schema — what `RunOptions.configurable` keys this server honors."
|
|
84
|
+
},
|
|
85
|
+
"observability": {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"description": "Optional v1 hints about emitted spans/metrics/logs. See observability.md."
|
|
88
|
+
},
|
|
89
|
+
"minClientVersion": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"description": "Optional v1 minimum client SDK version the server interops with."
|
|
92
|
+
},
|
|
93
|
+
"secrets": {
|
|
94
|
+
"type": "object",
|
|
95
|
+
"description": "Optional v1 secret/credential resolution advertisement. Clients gate BYOK flows on this. Hosts that don't store credentials return `supported: false`.",
|
|
96
|
+
"required": ["supported"],
|
|
97
|
+
"properties": {
|
|
98
|
+
"supported": {
|
|
99
|
+
"type": "boolean",
|
|
100
|
+
"description": "Host has any secret-resolution at all."
|
|
101
|
+
},
|
|
102
|
+
"scopes": {
|
|
103
|
+
"type": "array",
|
|
104
|
+
"items": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"enum": ["tenant", "user", "run"]
|
|
107
|
+
},
|
|
108
|
+
"uniqueItems": true,
|
|
109
|
+
"description": "Subset of scopes the host implements. Tenant-scoped secrets are workspace-shared; user-scoped are per-end-user; run-scoped are ephemeral per-run."
|
|
110
|
+
},
|
|
111
|
+
"resolution": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"enum": ["host-managed"],
|
|
114
|
+
"description": "Resolution mode. v1.x supports only `host-managed` (clients reference stored secrets via opaque ids); reserved for future modes."
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"additionalProperties": false
|
|
118
|
+
},
|
|
119
|
+
"runtimeCapabilities": {
|
|
120
|
+
"type": "array",
|
|
121
|
+
"items": { "type": "string", "minLength": 1 },
|
|
122
|
+
"uniqueItems": true,
|
|
123
|
+
"description": "Optional v1 host-advertised opaque capability ids that NodeModules may declare in `NodeModule.requires`. Naming convention: dotted, domain-scoped (`chat.sendPrompt`, `canvas.write`, `secrets.byok`). Provider value shapes are documented per-capability alongside consumers, NOT in the protocol package — the protocol owns the *check*, not domain provider contracts. A client that submits a workflow whose nodes declare a `requires` entry SHOULD first verify the host advertises that capability; a host that lacks a capability MUST refuse to dispatch nodes that declare it, terminating the run with `RunSnapshot.error.code = 'capability_not_provided'`. See `capabilities.md` §\"Runtime capabilities\"."
|
|
124
|
+
},
|
|
125
|
+
"aiProviders": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"description": "Optional v1 companion to `secrets`. Advertises which AI providers the host's AI-proxy can route to and which permit BYOK.",
|
|
128
|
+
"properties": {
|
|
129
|
+
"supported": {
|
|
130
|
+
"type": "array",
|
|
131
|
+
"items": { "type": "string", "minLength": 1 },
|
|
132
|
+
"uniqueItems": true,
|
|
133
|
+
"description": "Provider ids the host's AI-proxy can route to. Conventional ids: `anthropic`, `openai`, `gemini`, `mistral`, `cohere`, `vertex`, `bedrock`. Hosts MAY add vendor-prefixed extensions."
|
|
134
|
+
},
|
|
135
|
+
"byok": {
|
|
136
|
+
"type": "array",
|
|
137
|
+
"items": { "type": "string", "minLength": 1 },
|
|
138
|
+
"uniqueItems": true,
|
|
139
|
+
"description": "Subset of `supported` for which BYOK is permitted. Empty array → all calls use platform-managed keys; non-empty → clients MAY pass `ai.credentialRef` in `RunOptions.configurable` for matching providers."
|
|
140
|
+
},
|
|
141
|
+
"policies": {
|
|
142
|
+
"type": "object",
|
|
143
|
+
"description": "Optional v1 host-side policy enforcement modes for per-provider gating. Omitted → no enforcement; clients see only `optional` semantics. When present, MUST declare `modes` — an empty `{}` is not a valid third state. See `capabilities.md` §`aiProviders.policies`.",
|
|
144
|
+
"required": ["modes"],
|
|
145
|
+
"properties": {
|
|
146
|
+
"modes": {
|
|
147
|
+
"type": "array",
|
|
148
|
+
"items": { "type": "string", "enum": ["disabled", "optional", "required", "restricted"] },
|
|
149
|
+
"uniqueItems": true,
|
|
150
|
+
"description": "Subset of policy modes this host can enforce. `disabled` = provider may not be used; `optional` = no restriction (default); `required` = BYOK required; `restricted` = model must match the policy's `allowedModels` glob list. Hosts MAY support a subset; clients MUST tolerate any subset."
|
|
151
|
+
},
|
|
152
|
+
"scopes": {
|
|
153
|
+
"type": "array",
|
|
154
|
+
"items": { "type": "string", "minLength": 1 },
|
|
155
|
+
"uniqueItems": true,
|
|
156
|
+
"description": "Resolution layers the host evaluates. Conventional ids: `workspace`, `project`, `canvas-type`. Precedence is host-defined and SHOULD be documented per-deployment."
|
|
157
|
+
},
|
|
158
|
+
"errorCode": {
|
|
159
|
+
"type": "string",
|
|
160
|
+
"minLength": 1,
|
|
161
|
+
"description": "Wire-format error code returned on denial. Defaults to `provider_policy_denied`. Reserved for hosts that need a vendor-prefixed alias."
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"additionalProperties": false
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"additionalProperties": false
|
|
168
|
+
},
|
|
169
|
+
"testing": {
|
|
170
|
+
"type": "object",
|
|
171
|
+
"description": "Testing-mode capabilities advertised to clients (closes F1). Lets conformance suites + dev clients discover which mock providers the server supports and which API-key prefix denotes test keys (since mock providers are test-keys-only).",
|
|
172
|
+
"properties": {
|
|
173
|
+
"mockProviders": {
|
|
174
|
+
"type": "array",
|
|
175
|
+
"items": { "type": "string", "minLength": 1 },
|
|
176
|
+
"uniqueItems": true,
|
|
177
|
+
"description": "Mock-provider IDs this server recognizes via `RunOptions.configurable.mockProvider.id`. Servers claiming OpenWOP v1.0 conformance MUST include `stream-text`. Other canonical providers (`tool-calls`, `error`, `usage-only`) are recommended. Implementations MAY add their own (vendor-prefixed)."
|
|
178
|
+
},
|
|
179
|
+
"testKeyPrefix": {
|
|
180
|
+
"type": "string",
|
|
181
|
+
"description": "API-key prefix that marks a key as test-mode. Mock providers and other test-only surfaces gate on this prefix.",
|
|
182
|
+
"minLength": 1,
|
|
183
|
+
"maxLength": 32
|
|
184
|
+
},
|
|
185
|
+
"forceEngineVersionRange": {
|
|
186
|
+
"type": "object",
|
|
187
|
+
"description": "Range of engine versions the server can be forced into via the `X-Force-Engine-Version` request header (test-keys-only). Used by the conformance suite to verify forward-compat fold-best-effort across the version-negotiation matrix. Closes F5.",
|
|
188
|
+
"required": ["min", "max"],
|
|
189
|
+
"properties": {
|
|
190
|
+
"min": { "type": "integer", "minimum": 0, "description": "Lowest forceable engine version. Typically (current - 1) so back-compat is exercised." },
|
|
191
|
+
"max": { "type": "integer", "minimum": 0, "description": "Highest forceable engine version. Typically (current + 1) so forward-compat is exercised." }
|
|
192
|
+
},
|
|
193
|
+
"additionalProperties": false
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"additionalProperties": true
|
|
197
|
+
},
|
|
198
|
+
"fixtures": {
|
|
199
|
+
"type": "array",
|
|
200
|
+
"items": { "type": "string", "minLength": 1 },
|
|
201
|
+
"uniqueItems": true,
|
|
202
|
+
"description": "Optional v1 fixture workflow IDs the host has seeded. The conformance suite uses this list to decide which fixture-dependent scenarios run vs. skip. Each ID matches a stub at `node_modules/@openwop/openwop-conformance/fixtures/{id}.json`. Hosts MAY advertise vendor-prefixed IDs; clients MUST tolerate unknown IDs. Empty array or absent means the host advertises no fixtures."
|
|
203
|
+
},
|
|
204
|
+
"agents": {
|
|
205
|
+
"type": "object",
|
|
206
|
+
"description": "Multi-Agent Shift capability block (Phases 1-6). Hosts that implement any multi-agent surface declare it here; pre-MAS hosts omit the block entirely (consumers treat as 'no agent support'). Each field gates a specific conformance scenario class; conformance scenarios skip honestly when the host's advertisement doesn't claim the capability.",
|
|
207
|
+
"properties": {
|
|
208
|
+
"supported": {
|
|
209
|
+
"type": "boolean",
|
|
210
|
+
"description": "Master switch for agent identity (Phase 1). When `true`, host accepts run-level `RunSnapshot.agent` / `runOrchestrator` fields, emits `agent.*` events, and honors the confidence-escalation contract."
|
|
211
|
+
},
|
|
212
|
+
"profile": {
|
|
213
|
+
"type": "string",
|
|
214
|
+
"description": "Optional named capability profile (e.g., `wop-agents-phase-1-skeleton` for identity-only, `wop-agents-full` for orchestrator+dispatch+memory+conversation). Clients pattern-match on profile to gate feature usage."
|
|
215
|
+
},
|
|
216
|
+
"modelClasses": {
|
|
217
|
+
"type": "array",
|
|
218
|
+
"items": {
|
|
219
|
+
"type": "string",
|
|
220
|
+
"enum": ["reasoning", "tool-using", "chat", "code", "vision", "multimodal", "embedding", "classification", "retrieval"]
|
|
221
|
+
},
|
|
222
|
+
"uniqueItems": true,
|
|
223
|
+
"description": "Optional list of `AgentRef.modelClass` values this host supports for hosted agents (Phase 2). Pack manifests whose `modelClass` is not in this list MUST refuse install with `unsupported_model_class`."
|
|
224
|
+
},
|
|
225
|
+
"orchestratorPattern": {
|
|
226
|
+
"type": "string",
|
|
227
|
+
"description": "Optional orchestration pattern (Phase 2). Canonical: `single`, `delegate`, `delegate.smart`. Vendor extensions ship under `vendor.<host>.<pattern>`."
|
|
228
|
+
},
|
|
229
|
+
"memoryBackends": {
|
|
230
|
+
"type": "array",
|
|
231
|
+
"items": { "type": "string", "enum": ["long-term"] },
|
|
232
|
+
"uniqueItems": true,
|
|
233
|
+
"description": "Optional list of memory backends (Phase 3). `long-term` means the host implements `ExecutionHost.memory` against a durable store with the SR-1 redaction invariant intact end-to-end. Hosts that don't wire `MemoryAdapter` omit this field."
|
|
234
|
+
},
|
|
235
|
+
"orchestrator": {
|
|
236
|
+
"type": "boolean",
|
|
237
|
+
"description": "Phase 5. When `true`, host advertises that it implements the `core.orchestrator.supervisor` node typeId AND honors the conservative-path suspend semantics (CP-1: low-confidence suspend via `node.suspended { reason: 'low-confidence' }`)."
|
|
238
|
+
},
|
|
239
|
+
"dispatch": {
|
|
240
|
+
"type": "boolean",
|
|
241
|
+
"description": "Phase 6. When `true`, host advertises that it implements the `core.dispatch` Core typeId AND honors the conservative-path commitment CP-2 (`core.dispatch` MUST NOT mutate the run's DAG mid-run). Implies (but does NOT require) `agents.orchestrator: true`."
|
|
242
|
+
},
|
|
243
|
+
"reasoning": {
|
|
244
|
+
"type": "object",
|
|
245
|
+
"description": "Phase 1 reasoning-event verbosity configuration.",
|
|
246
|
+
"properties": {
|
|
247
|
+
"verbosity": {
|
|
248
|
+
"type": "string",
|
|
249
|
+
"enum": ["summary", "full", "off"],
|
|
250
|
+
"description": "Default reasoning verbosity for `agent.reasoned` events when the run does not override via `RunOptions.configurable.reasoningVerbosity`."
|
|
251
|
+
},
|
|
252
|
+
"tokenLimit": {
|
|
253
|
+
"type": "integer",
|
|
254
|
+
"minimum": 0,
|
|
255
|
+
"description": "Effective cap on reasoning trace length when verbosity is `summary`. Default 512 tokens."
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
"additionalProperties": false
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
"additionalProperties": true
|
|
262
|
+
},
|
|
263
|
+
"conversationPrimitive": {
|
|
264
|
+
"type": "boolean",
|
|
265
|
+
"description": "Multi-Agent Shift Phase 4. When `true`, host advertises that it implements the `core.conversationGate` typeId AND honors the `conversation.start` / `conversation.exchange` / `conversation.close` suspend variants. Hosts that don't claim this fall back to `clarification.requested` interrupts for multi-turn user interjections."
|
|
266
|
+
},
|
|
267
|
+
"compliance": {
|
|
268
|
+
"type": "object",
|
|
269
|
+
"description": "Privacy / compliance behavior advertised to clients (closes O5). Lets consumers know what masking mode is in effect so a `\"[REDACTED]\"` value in event log payloads is recognizable as a server-side mask vs an upstream null.",
|
|
270
|
+
"properties": {
|
|
271
|
+
"defaultMode": {
|
|
272
|
+
"type": "string",
|
|
273
|
+
"enum": ["mask", "omit", "hash", "passthrough"],
|
|
274
|
+
"description": "Server's default masking mode for fields marked sensitive. `mask` (default): replace with `\"[REDACTED]\"`. `omit`: drop the field entirely. `hash`: replace with `\"sha256:<hex>\"` for audit-only equality. `passthrough`: record as-is (NOT recommended for production). Workflow authors MAY override per-workflow via `metadata.complianceConfig.maskingMode`. See observability.md §Privacy classification."
|
|
275
|
+
},
|
|
276
|
+
"supportedClasses": {
|
|
277
|
+
"type": "array",
|
|
278
|
+
"items": { "type": "string", "enum": ["public", "pii", "phi", "pci", "regulated"] },
|
|
279
|
+
"uniqueItems": true,
|
|
280
|
+
"description": "Compliance classes this server applies special handling for. A server MAY accept all five classes but only enforce stricter retention on a subset; this field declares the operational reality so workflow authors can choose a server appropriately."
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
"additionalProperties": true
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
"additionalProperties": true
|
|
287
|
+
}
|
|
@@ -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,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/conversation-event.schema.json",
|
|
4
|
+
"title": "ConversationEvent",
|
|
5
|
+
"description": "Discriminated payload for the three new run events RFC 0005 introduces: `conversation.opened`, `conversation.exchanged`, `conversation.closed`. Discriminator is the parent `RunEventDoc.type` field (see `run-event.schema.json`); this schema constrains `payload` shape. The `ConversationTurn` shape is inlined under `$defs` (rather than $ref'd to `conversation-turn.schema.json`) for self-containment under per-file Ajv compile order — keep in sync with `conversation-turn.schema.json`. See RFC 0005 §B.",
|
|
6
|
+
"oneOf": [
|
|
7
|
+
{ "$ref": "#/$defs/ConversationOpenedPayload" },
|
|
8
|
+
{ "$ref": "#/$defs/ConversationExchangedPayload" },
|
|
9
|
+
{ "$ref": "#/$defs/ConversationClosedPayload" }
|
|
10
|
+
],
|
|
11
|
+
"$defs": {
|
|
12
|
+
"ConversationTurn": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"description": "One turn in a multi-turn conversation. Mirror of `conversation-turn.schema.json`; kept in sync. Structural superset of `ConversationMessage` (RFC 0002 §G).",
|
|
15
|
+
"required": ["messageId", "from", "content", "ts", "role", "turnIndex"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"messageId": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
18
|
+
"from": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
19
|
+
"to": { "type": "string", "maxLength": 256 },
|
|
20
|
+
"groupId": { "type": "string", "maxLength": 256 },
|
|
21
|
+
"content": {
|
|
22
|
+
"type": ["object", "array", "string", "number", "boolean", "null"]
|
|
23
|
+
},
|
|
24
|
+
"ts": { "type": "integer", "minimum": 0 },
|
|
25
|
+
"role": { "type": "string", "enum": ["user", "agent", "system"] },
|
|
26
|
+
"agent": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"properties": {
|
|
29
|
+
"agentId": { "type": "string", "minLength": 1 },
|
|
30
|
+
"agentSharing": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"enum": ["isolated", "shared", "shared:%shared%"]
|
|
33
|
+
},
|
|
34
|
+
"memoryRef": { "type": "string" }
|
|
35
|
+
},
|
|
36
|
+
"additionalProperties": true
|
|
37
|
+
},
|
|
38
|
+
"turnIndex": { "type": "integer", "minimum": 0 }
|
|
39
|
+
},
|
|
40
|
+
"additionalProperties": true
|
|
41
|
+
},
|
|
42
|
+
"ConversationOpenedPayload": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"description": "Payload for `RunEventType: 'conversation.opened'`. Emitted when a node calls `ctx.interrupt({kind: 'conversation', operation: 'start', ...})`. Causation: this event's `causationId` MUST equal the `eventId` of the accompanying `node.suspended`.",
|
|
45
|
+
"required": ["conversationId", "initialTurn"],
|
|
46
|
+
"properties": {
|
|
47
|
+
"conversationId": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": "Host-assigned conversation identifier. Deterministic for replay (typically `${runId}:${nodeId}:${attempt}`). Persists across all turns of this conversation.",
|
|
50
|
+
"minLength": 1,
|
|
51
|
+
"maxLength": 256
|
|
52
|
+
},
|
|
53
|
+
"agentId": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"description": "Optional. The agent driving this conversation (RFC 0002 §A1). For multi-agent conversations, identifies the conversation owner.",
|
|
56
|
+
"minLength": 1,
|
|
57
|
+
"maxLength": 256
|
|
58
|
+
},
|
|
59
|
+
"initialTurn": { "$ref": "#/$defs/ConversationTurn" },
|
|
60
|
+
"schema": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"description": "Optional JSON Schema for resume turn validation. Servers SHOULD validate subsequent `conversation.exchanged` turns against this schema."
|
|
63
|
+
},
|
|
64
|
+
"capabilities": {
|
|
65
|
+
"type": "array",
|
|
66
|
+
"description": "Informative host capability hints. Future RFCs may normate values; current namespace is open.",
|
|
67
|
+
"items": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"enum": ["multi-turn", "streaming"]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"additionalProperties": false
|
|
74
|
+
},
|
|
75
|
+
"ConversationExchangedPayload": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"description": "Payload for `RunEventType: 'conversation.exchanged'`. Emitted on each `operation: 'exchange'` turn round-trip. The `turnIndex` MUST monotonically increase within a `conversationId`.",
|
|
78
|
+
"required": ["conversationId", "turnIndex", "turn"],
|
|
79
|
+
"properties": {
|
|
80
|
+
"conversationId": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "MUST match the `conversation.opened` payload's `conversationId`.",
|
|
83
|
+
"minLength": 1,
|
|
84
|
+
"maxLength": 256
|
|
85
|
+
},
|
|
86
|
+
"turnIndex": {
|
|
87
|
+
"type": "integer",
|
|
88
|
+
"minimum": 1,
|
|
89
|
+
"description": "1-based index within the conversation. `conversation.opened.initialTurn` is `turnIndex: 0`; the first `exchanged` is `turnIndex: 1`; etc."
|
|
90
|
+
},
|
|
91
|
+
"turn": { "$ref": "#/$defs/ConversationTurn" }
|
|
92
|
+
},
|
|
93
|
+
"additionalProperties": false
|
|
94
|
+
},
|
|
95
|
+
"ConversationClosedPayload": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"description": "Payload for `RunEventType: 'conversation.closed'`. Emitted on `operation: 'close'`. Terminates the conversation; no further events MAY carry this `conversationId`.",
|
|
98
|
+
"required": ["conversationId", "turnIndex", "finalTurn"],
|
|
99
|
+
"properties": {
|
|
100
|
+
"conversationId": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"description": "MUST match the `conversation.opened` payload's `conversationId`.",
|
|
103
|
+
"minLength": 1,
|
|
104
|
+
"maxLength": 256
|
|
105
|
+
},
|
|
106
|
+
"turnIndex": {
|
|
107
|
+
"type": "integer",
|
|
108
|
+
"minimum": 0,
|
|
109
|
+
"description": "Total turn count — equal to the closing turn's index."
|
|
110
|
+
},
|
|
111
|
+
"finalTurn": { "$ref": "#/$defs/ConversationTurn" },
|
|
112
|
+
"outcome": {
|
|
113
|
+
"description": "Opaque host-defined goal value. The conversation's resolved output. Spec does not constrain the shape; hosts MAY layer their own schemas via the `schema` field on `conversation.opened`.",
|
|
114
|
+
"type": ["object", "array", "string", "number", "boolean", "null"]
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"additionalProperties": false
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/conversation-turn.schema.json",
|
|
4
|
+
"title": "ConversationTurn",
|
|
5
|
+
"description": "One turn in a multi-turn conversation. Structural superset of `ConversationMessage` (RFC 0002 §G — append-only, idempotent on `messageId`, replay-deterministic). Adds `role`, optional `agent` reference, and `turnIndex` discriminators. Folds through the existing `applyMessage` reducer unchanged. See RFC 0005 §C.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["messageId", "from", "content", "ts", "role", "turnIndex"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"messageId": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Dedup key. Producers MUST construct a deterministic id so re-folding is idempotent. Recommended encoding: `${conversationId}:${turnIndex}:${role}` (e.g., `run-abc:n1:0:7:agent`).",
|
|
12
|
+
"minLength": 1,
|
|
13
|
+
"maxLength": 256
|
|
14
|
+
},
|
|
15
|
+
"from": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Sender identity. Either an agent ID (RFC 0002 §A1) or the literal `'user'` for human-originated turns.",
|
|
18
|
+
"minLength": 1,
|
|
19
|
+
"maxLength": 256
|
|
20
|
+
},
|
|
21
|
+
"to": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Optional addressee. Absent = broadcast to the conversation's group (or the run's default participants).",
|
|
24
|
+
"maxLength": 256
|
|
25
|
+
},
|
|
26
|
+
"groupId": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "Optional shared-agent group ID. When set, the turn is scoped to the `shared:<groupId>` agent context per RFC 0002 §A8.",
|
|
29
|
+
"maxLength": 256
|
|
30
|
+
},
|
|
31
|
+
"content": {
|
|
32
|
+
"description": "Opaque turn content. Protocol does not standardize the shape; hosts use Zod schemas at the application layer for structured turns. Pass-through for any JSON value.",
|
|
33
|
+
"type": ["object", "array", "string", "number", "boolean", "null"]
|
|
34
|
+
},
|
|
35
|
+
"ts": {
|
|
36
|
+
"type": "integer",
|
|
37
|
+
"minimum": 0,
|
|
38
|
+
"description": "Turn timestamp (ms epoch). Caller's clock; replay carries through unchanged for determinism."
|
|
39
|
+
},
|
|
40
|
+
"role": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"enum": ["user", "agent", "system"],
|
|
43
|
+
"description": "Discriminator distinguishing human-originated turns (`'user'`), AI-agent-originated turns (`'agent'`), and host-emitted procedural turns (`'system'` — e.g., `'conversation timed out'`)."
|
|
44
|
+
},
|
|
45
|
+
"agent": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"description": "Optional agent reference (RFC 0002 §A1 `AgentRef`) for `role: 'agent'` turns. The same shape RFC 0002 reserves on `RunSnapshot.agent` and `node.started.payload.agent` — propagated through inheritance.",
|
|
48
|
+
"properties": {
|
|
49
|
+
"agentId": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"minLength": 1
|
|
52
|
+
},
|
|
53
|
+
"agentSharing": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"enum": ["isolated", "shared", "shared:%shared%"],
|
|
56
|
+
"description": "Per RFC 0002 §A8 — `shared:<groupId>` requires a `groupId` field on the message."
|
|
57
|
+
},
|
|
58
|
+
"memoryRef": {
|
|
59
|
+
"type": "string",
|
|
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
|
+
},
|
|
63
|
+
"additionalProperties": true
|
|
64
|
+
},
|
|
65
|
+
"turnIndex": {
|
|
66
|
+
"type": "integer",
|
|
67
|
+
"minimum": 0,
|
|
68
|
+
"description": "0-based index within the conversation. `conversation.opened.initialTurn` carries `turnIndex: 0`; subsequent `conversation.exchanged` turns increment monotonically; `conversation.closed.finalTurn` carries the highest index."
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"additionalProperties": true
|
|
72
|
+
}
|