@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,116 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/run-event.schema.json",
|
|
4
|
+
"title": "RunEventDoc",
|
|
5
|
+
"description": "Persisted event document in the run's append-only event log. Source of truth for run state via projection fold. Canonical OpenWOP v1 event envelope.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["eventId", "runId", "type", "payload", "timestamp", "sequence"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"eventId": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Globally unique ID for this event. Generated by appendAtomic before write; consumers MUST treat as opaque.",
|
|
12
|
+
"minLength": 1,
|
|
13
|
+
"maxLength": 128
|
|
14
|
+
},
|
|
15
|
+
"runId": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Run this event belongs to.",
|
|
18
|
+
"minLength": 1,
|
|
19
|
+
"maxLength": 128
|
|
20
|
+
},
|
|
21
|
+
"nodeId": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Set for node-scoped events (node.*, approval.*, clarification.*). Lifted to top-level so projection code can key per-node state machines without payload type coercion.",
|
|
24
|
+
"minLength": 1,
|
|
25
|
+
"maxLength": 128
|
|
26
|
+
},
|
|
27
|
+
"type": {
|
|
28
|
+
"$ref": "#/$defs/RunEventType"
|
|
29
|
+
},
|
|
30
|
+
"payload": {
|
|
31
|
+
"description": "Event-type-specific payload. Servers MAY validate against per-type schemas; this top-level schema accepts any JSON value.",
|
|
32
|
+
"type": ["object", "array", "string", "number", "boolean", "null"]
|
|
33
|
+
},
|
|
34
|
+
"timestamp": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"format": "date-time",
|
|
37
|
+
"description": "ISO 8601 timestamp. IO adapters normalize between this canonical form and platform-specific representations (e.g., Firestore Timestamp)."
|
|
38
|
+
},
|
|
39
|
+
"sequence": {
|
|
40
|
+
"type": "integer",
|
|
41
|
+
"minimum": 0,
|
|
42
|
+
"description": "Monotonic per-run sequence. First event is 0; assigned atomically by appendAtomic."
|
|
43
|
+
},
|
|
44
|
+
"schemaVersion": {
|
|
45
|
+
"type": "integer",
|
|
46
|
+
"minimum": 0,
|
|
47
|
+
"description": "Per-event schema version. Bumped when an individual event type's payload contract changes. Distinct from per-run eventLogSchemaVersion (which describes the subcollection contract). Reader behavior is tolerant — unknown future versions fold best-effort."
|
|
48
|
+
},
|
|
49
|
+
"engineVersion": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"description": "Engine version that produced this event. Used by P4 versioning for compatibility checks."
|
|
52
|
+
},
|
|
53
|
+
"causationId": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"description": "Optional ID of the event that caused this one (e.g., node.completed caused by approval.received). Lets projections build causal chains without inferring from timing.",
|
|
56
|
+
"minLength": 1,
|
|
57
|
+
"maxLength": 128
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"additionalProperties": false,
|
|
61
|
+
"$defs": {
|
|
62
|
+
"RunEventType": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "Discriminator for event payload shape. Dotted naming convention. New variants extend the union — readers MUST NOT throw on unknown types (forward-compat: fold best-effort, ignore unknowns).",
|
|
65
|
+
"enum": [
|
|
66
|
+
"run.started",
|
|
67
|
+
"run.completed",
|
|
68
|
+
"run.failed",
|
|
69
|
+
"run.cancelled",
|
|
70
|
+
"run.resuming",
|
|
71
|
+
"run.paused",
|
|
72
|
+
"run.resumed",
|
|
73
|
+
"run.restored-from-snapshot",
|
|
74
|
+
"node.started",
|
|
75
|
+
"node.completed",
|
|
76
|
+
"node.failed",
|
|
77
|
+
"node.suspended",
|
|
78
|
+
"node.suspend-failed",
|
|
79
|
+
"node.resumed",
|
|
80
|
+
"node.retried",
|
|
81
|
+
"node.skipped",
|
|
82
|
+
"node.cancelled",
|
|
83
|
+
"approval.requested",
|
|
84
|
+
"approval.received",
|
|
85
|
+
"clarification.requested",
|
|
86
|
+
"clarification.resolved",
|
|
87
|
+
"interrupt.requested",
|
|
88
|
+
"interrupt.resolved",
|
|
89
|
+
"channel.written",
|
|
90
|
+
"artifact.created",
|
|
91
|
+
"output.chunk",
|
|
92
|
+
"variable.changed",
|
|
93
|
+
"log.appended",
|
|
94
|
+
"version.pinned",
|
|
95
|
+
"workflow.restored",
|
|
96
|
+
"workflow.loopback-limit",
|
|
97
|
+
"workflow.stalled",
|
|
98
|
+
"cap.breached",
|
|
99
|
+
"lease.acquired",
|
|
100
|
+
"lease.renewed",
|
|
101
|
+
"lease.lost",
|
|
102
|
+
"lease.handed-off",
|
|
103
|
+
"replay.diverged",
|
|
104
|
+
"agent.reasoned",
|
|
105
|
+
"agent.toolCalled",
|
|
106
|
+
"agent.toolReturned",
|
|
107
|
+
"agent.handoff",
|
|
108
|
+
"agent.decided",
|
|
109
|
+
"runOrchestrator.decided",
|
|
110
|
+
"conversation.opened",
|
|
111
|
+
"conversation.exchanged",
|
|
112
|
+
"conversation.closed"
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/run-options.schema.json",
|
|
4
|
+
"title": "RunOptions",
|
|
5
|
+
"description": "Per-run parameter overlay supplied by the caller in `POST /v1/runs`. See `run-options.md` for full semantics. Reserved keys are typed; unknown keys in `configurable` pass through to the engine.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"configurable": {
|
|
9
|
+
"$ref": "#/$defs/Configurable",
|
|
10
|
+
"description": "Per-run overrides that affect runtime behavior without forking the workflow definition."
|
|
11
|
+
},
|
|
12
|
+
"tags": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
15
|
+
"maxItems": 100,
|
|
16
|
+
"uniqueItems": false,
|
|
17
|
+
"description": "Free-form caller tags for filtering/correlation. Server SHOULD enforce maxItems=100 and per-tag maxLength=256."
|
|
18
|
+
},
|
|
19
|
+
"metadata": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"description": "Free-form caller metadata. Persisted on the run doc; surfaces back via `RunSnapshot.metadata`. Server MAY enforce a serialized-size limit (recommended: 50KB)."
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"additionalProperties": false,
|
|
25
|
+
"$defs": {
|
|
26
|
+
"Configurable": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"description": "Reserved keys are typed below; unknown keys pass through verbatim. Implementations declare which keys they consume via `Capabilities.configurable` when advertised.",
|
|
29
|
+
"properties": {
|
|
30
|
+
"recursionLimit": {
|
|
31
|
+
"type": "integer",
|
|
32
|
+
"minimum": 1,
|
|
33
|
+
"description": "Override the per-run node-execution ceiling. Server clamps to `Capabilities.limits.maxNodeExecutions`."
|
|
34
|
+
},
|
|
35
|
+
"model": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"minLength": 1,
|
|
38
|
+
"description": "Override the AI model for nodes that consume `ctx.config.configurable.model`. Examples: `gpt-4`, `claude-opus-4-7`, `gemini-2.5-pro`."
|
|
39
|
+
},
|
|
40
|
+
"temperature": {
|
|
41
|
+
"type": "number",
|
|
42
|
+
"minimum": 0,
|
|
43
|
+
"maximum": 2,
|
|
44
|
+
"description": "Override AI temperature. Server SHOULD enforce 0..2 range."
|
|
45
|
+
},
|
|
46
|
+
"maxTokens": {
|
|
47
|
+
"type": "integer",
|
|
48
|
+
"minimum": 1,
|
|
49
|
+
"description": "Override AI max-tokens cap."
|
|
50
|
+
},
|
|
51
|
+
"promptOverrides": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"additionalProperties": { "type": "string" },
|
|
54
|
+
"description": "Per-prompt-ID variant override. Map of canonical prompt ID -> override text."
|
|
55
|
+
},
|
|
56
|
+
"mockProvider": {
|
|
57
|
+
"$ref": "#/$defs/MockProvider"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"additionalProperties": true
|
|
61
|
+
},
|
|
62
|
+
"MockProvider": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"description": "Routes AI activity calls through a deterministic mock provider instead of real LLM APIs. Test-keys-only — server returns 403 mock_provider_forbidden on production keys. See run-options.md §Mock provider extension (closes F1).",
|
|
65
|
+
"required": ["id"],
|
|
66
|
+
"properties": {
|
|
67
|
+
"id": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"description": "Selects a mock provider from the server's catalog (advertised via `Capabilities.testing.mockProviders`). The spec-canonical baseline `stream-text` MUST be supported for OpenWOP v1.0 conformance; other providers (`tool-calls`, `error`, `usage-only`) are recommended but not required.",
|
|
70
|
+
"minLength": 1,
|
|
71
|
+
"maxLength": 128
|
|
72
|
+
},
|
|
73
|
+
"config": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"description": "Per-provider parameter shape. Each provider documents its own keys; see run-options.md §Canonical mock provider catalog. Common shapes (informative — not enforced at this schema level since providers are pluggable):\n\n - stream-text: { tokens: string[], delayMsPerToken?: integer, finishReason?: string, usage?: object, model?: string }\n - tool-calls: { toolCalls: object[], delayMsPerToken?: integer }\n - error: { code: string, message: string, retryable?: boolean, failAfterMs?: integer }\n - usage-only: { usage: object }"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"additionalProperties": false
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/run-orchestrator-decided-event.schema.json",
|
|
4
|
+
"title": "RunOrchestratorDecidedPayload",
|
|
5
|
+
"description": "Payload for the `runOrchestrator.decided` RunEvent. Emitted exactly once per orchestrator decision by a `core.orchestrator.supervisor` node (or a host-extension equivalent that advertises orchestration). Carries the deciding agent's identity and the typed decision (orchestrator-decision.schema.json). The event envelope's top-level `nodeId` field carries the supervisor node-id; the payload does NOT duplicate it. See RFC 0006 §E for ordering invariants CO-1/CO-2/CO-3.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["agentId", "decision"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"agentId": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"minLength": 3,
|
|
12
|
+
"maxLength": 256,
|
|
13
|
+
"description": "AgentRef.agentId of the orchestrator that emitted this decision. MUST equal `RunSnapshot.runOrchestrator.agentId` for the run's lifetime — the orchestrator identity is set at the first decision and does not change."
|
|
14
|
+
},
|
|
15
|
+
"decision": {
|
|
16
|
+
"$ref": "https://openwop.dev/spec/v1/orchestrator-decision.schema.json"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"additionalProperties": false
|
|
20
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/run-snapshot.schema.json",
|
|
4
|
+
"title": "RunSnapshot",
|
|
5
|
+
"description": "Projected run state returned by `GET /v1/runs/{runId}`. Source is the run's append-only event log (run-event.schema.json) folded through the `RunProjection`. Forward-compat tolerant: readers MUST ignore unknown fields rather than reject.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["runId", "workflowId", "status"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"runId": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Globally unique run identifier.",
|
|
12
|
+
"minLength": 1,
|
|
13
|
+
"maxLength": 128
|
|
14
|
+
},
|
|
15
|
+
"workflowId": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "ID of the workflow this run executes.",
|
|
18
|
+
"minLength": 1
|
|
19
|
+
},
|
|
20
|
+
"status": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"enum": [
|
|
23
|
+
"pending",
|
|
24
|
+
"running",
|
|
25
|
+
"paused",
|
|
26
|
+
"waiting-approval",
|
|
27
|
+
"waiting-input",
|
|
28
|
+
"completed",
|
|
29
|
+
"failed",
|
|
30
|
+
"cancelled"
|
|
31
|
+
],
|
|
32
|
+
"description": "Current run state. Forward-compat: future statuses MAY be added; readers SHOULD treat unknown values as terminal-unknown rather than throw."
|
|
33
|
+
},
|
|
34
|
+
"currentNodeId": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Set when the run is suspended at a specific node (`waiting-approval` / `waiting-input`) — identifies which node holds the interrupt."
|
|
37
|
+
},
|
|
38
|
+
"startedAt": { "type": "string", "format": "date-time" },
|
|
39
|
+
"completedAt": { "type": "string", "format": "date-time" },
|
|
40
|
+
"agent": {
|
|
41
|
+
"$ref": "agent-ref.schema.json",
|
|
42
|
+
"description": "Optional run-level agent identity (Multi-Agent Shift Phase 1). When the run is driven by a single agent, this field carries that agent's `AgentRef`. In supervisor-orchestrated runs (Phase 5), this field rotates as workers hand off — it always carries the active worker for the current node. See `runOrchestrator` for the run-lifetime supervisor identity. Absent for runs with no agent provenance (legacy single-actor host)."
|
|
43
|
+
},
|
|
44
|
+
"runOrchestrator": {
|
|
45
|
+
"$ref": "agent-ref.schema.json",
|
|
46
|
+
"description": "Optional orchestrator-supervisor identity (Multi-Agent Shift Phase 5). When set, this agent owns dispatch decisions across the run's lifetime; `runOrchestrator.decided` events emitted during the run carry this agent's `agentId`. Distinct from `agent` — `agent` rotates with each worker; `runOrchestrator` is set at run start (or first `core.orchestrator.supervisor` node) and MUST NOT change for the run's lifetime. In single-agent runs (no supervisor), this field is absent; in supervisor runs both fields MAY co-exist."
|
|
47
|
+
},
|
|
48
|
+
"nodeStates": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"description": "Per-node state map. Keys are nodeIds; values are implementation-shaped state objects. Spec doesn't constrain the inner shape — see version-negotiation.md §node-states."
|
|
51
|
+
},
|
|
52
|
+
"variables": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"description": "Workflow-level variables (caller-supplied inputs + per-node outputs that survive the projection)."
|
|
55
|
+
},
|
|
56
|
+
"channels": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"description": "Optional typed state channels (see channels-and-reducers.md). Present when the workflow declares channel-aware mode."
|
|
59
|
+
},
|
|
60
|
+
"error": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"description": "Set on terminal `failed`. Structured so consumers can route on code without parsing message text.",
|
|
63
|
+
"required": ["code", "message"],
|
|
64
|
+
"properties": {
|
|
65
|
+
"code": { "type": "string", "minLength": 1 },
|
|
66
|
+
"message": { "type": "string", "minLength": 1 },
|
|
67
|
+
"details": { "type": "object" }
|
|
68
|
+
},
|
|
69
|
+
"additionalProperties": false
|
|
70
|
+
},
|
|
71
|
+
"engineVersion": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"description": "Engine version the run was started under. Used by the projection for forward-compat folds."
|
|
74
|
+
},
|
|
75
|
+
"eventLogSchemaVersion": {
|
|
76
|
+
"type": "integer",
|
|
77
|
+
"minimum": 0,
|
|
78
|
+
"description": "Per-run event-log subcollection schema version. See version-negotiation.md."
|
|
79
|
+
},
|
|
80
|
+
"tags": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"items": { "type": "string", "maxLength": 256 },
|
|
83
|
+
"maxItems": 100,
|
|
84
|
+
"description": "Caller-supplied tags from `RunOptions.tags`."
|
|
85
|
+
},
|
|
86
|
+
"metadata": {
|
|
87
|
+
"type": "object",
|
|
88
|
+
"description": "Caller-supplied metadata from `RunOptions.metadata`."
|
|
89
|
+
},
|
|
90
|
+
"configurable": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"description": "Per-run overlay from `RunOptions.configurable` (see run-options.md). Reserved keys: `recursionLimit`, `model`, `temperature`, `maxTokens`, `promptOverrides`."
|
|
93
|
+
},
|
|
94
|
+
"metrics": {
|
|
95
|
+
"type": "object",
|
|
96
|
+
"description": "Aggregate run-level metrics. Forward-compat: readers MUST tolerate missing/unknown fields. Fields are populated lazily as the engine emits them — absence does NOT mean zero. Implementation-specific fields (e.g., legacy `cost: number` estimates) MAY appear alongside the spec-canonical fields below.",
|
|
97
|
+
"properties": {
|
|
98
|
+
"openwopCost": {
|
|
99
|
+
"type": "object",
|
|
100
|
+
"description": "Spec-canonical cost rollup aggregated from per-node `recordCost()` calls. Keys mirror the `openwop.cost.*` OTel attribute allowlist (see `observability.md` §Cost attribution). Named `openwopCost` rather than `cost` because some implementations carry a legacy `metrics.cost: number` estimate that predates the typed rollup; using a distinct name avoids collision. Multi-provider runs report `provider`/`model` of the LAST contributing call; SDKs that need per-call detail SHOULD subscribe to OTel spans instead of reading this rollup.",
|
|
101
|
+
"properties": {
|
|
102
|
+
"usd": { "type": "number", "minimum": 0, "description": "Total USD cost across all node-level recordCost emissions." },
|
|
103
|
+
"tokens": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"properties": {
|
|
106
|
+
"input": { "type": "integer", "minimum": 0 },
|
|
107
|
+
"output": { "type": "integer", "minimum": 0 }
|
|
108
|
+
},
|
|
109
|
+
"additionalProperties": false
|
|
110
|
+
},
|
|
111
|
+
"model": { "type": "string", "description": "Model identifier of the most recent recordCost emission (e.g., `claude-opus-4-7`). For multi-model runs, surfaces the last value." },
|
|
112
|
+
"provider": { "type": "string", "description": "Normalized provider name (e.g., `anthropic`, `openai`, `gemini`)." },
|
|
113
|
+
"duration_ms": { "type": "integer", "minimum": 0, "description": "Sum of per-call durations across all recordCost emissions in this run." }
|
|
114
|
+
},
|
|
115
|
+
"additionalProperties": false
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"additionalProperties": true
|
|
121
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openwop.dev/spec/v1/suspend-request.schema.json",
|
|
4
|
+
"title": "InterruptPayload",
|
|
5
|
+
"description": "Wire format for ctx.interrupt(payload) — the canonical HITL primitive. See spec/v1/interrupt.md for semantics. Discriminated by `kind`. The persisted `interrupt.requested` event carries this payload verbatim plus engine-assigned identity fields.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["kind", "key", "data"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"kind": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"enum": ["approval", "clarification", "external-event", "custom", "conversation.start", "conversation.exchange", "conversation.close", "low-confidence"],
|
|
12
|
+
"description": "Discriminator for resume-time routing + UI rendering + observability. Multi-Agent Shift Phase 4 adds `conversation.start` / `conversation.exchange` / `conversation.close` for multi-turn user interjections (see ConversationStartData / ConversationExchangeData / ConversationCloseData below). Phase 1 adds `low-confidence` for the confidence-escalation contract (orchestrator-supervisor suspends below threshold; see LowConfidenceData)."
|
|
13
|
+
},
|
|
14
|
+
"key": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Deterministic key used to short-circuit on re-entry after process death. If two interrupt() calls in the same run emit the same key, the second returns the cached result of the first. Recommended: ${runId}:${nodeId}:${interruptCount}.",
|
|
17
|
+
"minLength": 1,
|
|
18
|
+
"maxLength": 256
|
|
19
|
+
},
|
|
20
|
+
"resumeSchema": {
|
|
21
|
+
"description": "Optional JSON Schema for resume value. Servers SHOULD validate before returning to the suspended executor.",
|
|
22
|
+
"type": "object"
|
|
23
|
+
},
|
|
24
|
+
"timeoutMs": {
|
|
25
|
+
"type": "integer",
|
|
26
|
+
"minimum": 0,
|
|
27
|
+
"description": "Optional auto-reject after this duration. Engine throws InterruptTimeoutError when exceeded."
|
|
28
|
+
},
|
|
29
|
+
"data": {
|
|
30
|
+
"oneOf": [
|
|
31
|
+
{ "$ref": "#/$defs/ApprovalData" },
|
|
32
|
+
{ "$ref": "#/$defs/ClarificationData" },
|
|
33
|
+
{ "$ref": "#/$defs/ExternalEventData" },
|
|
34
|
+
{ "$ref": "#/$defs/CustomData" },
|
|
35
|
+
{ "$ref": "#/$defs/ConversationStartData" },
|
|
36
|
+
{ "$ref": "#/$defs/ConversationExchangeData" },
|
|
37
|
+
{ "$ref": "#/$defs/ConversationCloseData" },
|
|
38
|
+
{ "$ref": "#/$defs/LowConfidenceData" }
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": false,
|
|
43
|
+
"$defs": {
|
|
44
|
+
"ApprovalData": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"description": "Payload for kind='approval'. Uses the canonical 5-action OpenWOP approval vocabulary.",
|
|
47
|
+
"required": ["artifactId", "artifactType", "title", "actions"],
|
|
48
|
+
"properties": {
|
|
49
|
+
"artifactId": { "type": "string", "minLength": 1 },
|
|
50
|
+
"artifactType": { "type": "string", "minLength": 1 },
|
|
51
|
+
"title": { "type": "string", "minLength": 1 },
|
|
52
|
+
"description": { "type": "string" },
|
|
53
|
+
"artifactData": {},
|
|
54
|
+
"actions": {
|
|
55
|
+
"type": "array",
|
|
56
|
+
"items": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"enum": ["accept", "reject", "refine", "edit-accept", "ask"]
|
|
59
|
+
},
|
|
60
|
+
"minItems": 1,
|
|
61
|
+
"uniqueItems": true,
|
|
62
|
+
"description": "Allowed actions. Server MUST enforce on resolve. The 'ask' action does NOT exit the suspend — Q&A exchanges accumulate via askService until accept/reject/refine/edit-accept fires. See `interrupt.md` §`ApprovalResume` for the action vocabulary + per-action required fields."
|
|
63
|
+
},
|
|
64
|
+
"requiredApprovals": {
|
|
65
|
+
"type": "integer",
|
|
66
|
+
"minimum": 1,
|
|
67
|
+
"default": 1,
|
|
68
|
+
"description": "Multi-approver quorum."
|
|
69
|
+
},
|
|
70
|
+
"approversList": {
|
|
71
|
+
"type": "array",
|
|
72
|
+
"items": { "type": "string" },
|
|
73
|
+
"description": "Restrict resolution to these user IDs. Empty/unset = any user with approvals:respond scope."
|
|
74
|
+
},
|
|
75
|
+
"rejectionPolicy": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"enum": ["single-veto", "majority"],
|
|
78
|
+
"default": "single-veto"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"ClarificationData": {
|
|
83
|
+
"type": "object",
|
|
84
|
+
"description": "Payload for kind='clarification'. AI/system asks the user for missing context before continuing.",
|
|
85
|
+
"required": ["questions"],
|
|
86
|
+
"properties": {
|
|
87
|
+
"questions": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"items": {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"required": ["id", "question"],
|
|
92
|
+
"properties": {
|
|
93
|
+
"id": { "type": "string", "minLength": 1 },
|
|
94
|
+
"question": { "type": "string", "minLength": 1 },
|
|
95
|
+
"schema": { "type": "object", "description": "Optional JSON Schema for the answer." }
|
|
96
|
+
},
|
|
97
|
+
"additionalProperties": false
|
|
98
|
+
},
|
|
99
|
+
"minItems": 1
|
|
100
|
+
},
|
|
101
|
+
"contextType": { "type": "string" }
|
|
102
|
+
},
|
|
103
|
+
"additionalProperties": false
|
|
104
|
+
},
|
|
105
|
+
"ExternalEventData": {
|
|
106
|
+
"type": "object",
|
|
107
|
+
"description": "Payload for kind='external-event'. Engine waits for an external system to POST to /v1/interrupts/{token}.",
|
|
108
|
+
"required": ["eventType", "correlation"],
|
|
109
|
+
"properties": {
|
|
110
|
+
"eventType": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"description": "Stable description of what's awaited. Surfaced in admin panels. Examples: 'stripe.checkout.completed', 'calendar.event.confirmed'."
|
|
113
|
+
},
|
|
114
|
+
"correlation": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"description": "Opaque correlation payload — whatever the external system needs to match the interrupt back to its source event."
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"additionalProperties": false
|
|
120
|
+
},
|
|
121
|
+
"CustomData": {
|
|
122
|
+
"type": "object",
|
|
123
|
+
"description": "Escape hatch for kinds not yet spec'd. Servers MUST accept and persist; UI rendering and admin tooling are best-effort.",
|
|
124
|
+
"required": ["customKind"],
|
|
125
|
+
"properties": {
|
|
126
|
+
"customKind": { "type": "string", "minLength": 1 },
|
|
127
|
+
"payload": {}
|
|
128
|
+
},
|
|
129
|
+
"additionalProperties": false
|
|
130
|
+
},
|
|
131
|
+
"ConversationStartData": {
|
|
132
|
+
"type": "object",
|
|
133
|
+
"description": "Payload for kind='conversation.start' (Multi-Agent Shift Phase 4). Opens a new multi-turn conversation context. The host mints a `conversationId` and emits `conversation.opened`; subsequent `conversation.exchange` calls reuse the same id.",
|
|
134
|
+
"required": ["conversationId"],
|
|
135
|
+
"properties": {
|
|
136
|
+
"conversationId": {
|
|
137
|
+
"type": "string",
|
|
138
|
+
"minLength": 1,
|
|
139
|
+
"maxLength": 256,
|
|
140
|
+
"description": "Opaque conversation identifier. Tenant-unique invariant: hosts MUST ensure no two distinct conversations in the same tenant share an id. Cross-host portability is not normative — `conversationId`s minted by one host MUST NOT be assumed resolvable by another."
|
|
141
|
+
},
|
|
142
|
+
"title": { "type": "string", "description": "Optional human-readable conversation title surfaced in host UIs." },
|
|
143
|
+
"initialPrompt": { "type": "string", "description": "Optional system / agent message that opens the conversation. Folds into the `message` reducer as the first turn." }
|
|
144
|
+
},
|
|
145
|
+
"additionalProperties": false
|
|
146
|
+
},
|
|
147
|
+
"ConversationExchangeData": {
|
|
148
|
+
"type": "object",
|
|
149
|
+
"description": "Payload for kind='conversation.exchange' (Multi-Agent Shift Phase 4). Single-turn suspend within an open conversation; resume value MUST validate against the per-turn `outcomeSchema`.",
|
|
150
|
+
"required": ["conversationId", "prompt"],
|
|
151
|
+
"properties": {
|
|
152
|
+
"conversationId": { "type": "string", "minLength": 1, "maxLength": 256, "description": "Active conversation identifier — MUST equal the id minted by a preceding `conversation.start`." },
|
|
153
|
+
"prompt": { "type": "string", "minLength": 1, "description": "Human-targeted question for this turn." },
|
|
154
|
+
"outcomeSchema": { "type": "object", "description": "Optional JSON Schema for the resume value (the user's response). Hosts SHOULD validate before resuming the suspended executor — replay determinism + idempotency require validated outcomes per the Phase-4 H4 disposition." },
|
|
155
|
+
"turnIndex": { "type": "integer", "minimum": 0, "description": "Optional 0-indexed turn within this conversation. Strictly monotonic when populated; absent values are derived host-side from event-log fold." }
|
|
156
|
+
},
|
|
157
|
+
"additionalProperties": false
|
|
158
|
+
},
|
|
159
|
+
"ConversationCloseData": {
|
|
160
|
+
"type": "object",
|
|
161
|
+
"description": "Payload for kind='conversation.close' (Multi-Agent Shift Phase 4). Terminates an open conversation context. Final event in the conversation lifecycle; no further `conversation.exchanged` events MAY follow for this `conversationId` in this run.",
|
|
162
|
+
"required": ["conversationId"],
|
|
163
|
+
"properties": {
|
|
164
|
+
"conversationId": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
165
|
+
"reason": { "type": "string", "description": "Optional close rationale (e.g., 'goal-reached', 'max-turns', 'user-cancelled')." }
|
|
166
|
+
},
|
|
167
|
+
"additionalProperties": false
|
|
168
|
+
},
|
|
169
|
+
"LowConfidenceData": {
|
|
170
|
+
"type": "object",
|
|
171
|
+
"description": "Payload for kind='low-confidence' (Multi-Agent Shift Phase 1 — confidence-escalation contract). Emitted by an agent (typically `core.orchestrator.supervisor`) when its `agent.decided.confidence` falls below the run's resolved escalation threshold. Run transitions to `'waiting-approval'`; an operator ratifies the decision via the resume path.",
|
|
172
|
+
"required": ["agentId", "threshold", "observed"],
|
|
173
|
+
"properties": {
|
|
174
|
+
"agentId": { "type": "string", "minLength": 3, "maxLength": 256, "description": "AgentRef.agentId of the agent that suspended." },
|
|
175
|
+
"threshold": { "type": "number", "minimum": 0, "maximum": 1, "description": "Resolved escalation threshold for this run (default 0.7 per Phase-1 disposition; per-run override via `RunOptions.configurable.escalationThreshold`)." },
|
|
176
|
+
"observed": { "type": "number", "minimum": 0, "maximum": 1, "description": "The agent's reported confidence value that triggered the escalation." },
|
|
177
|
+
"decision": { "description": "Optional sketch of the decision the agent WOULD have emitted at threshold-or-above. Lets operators see the un-ratified choice. Shape mirrors `OrchestratorDecision` or the agent's domain-specific decision type." }
|
|
178
|
+
},
|
|
179
|
+
"additionalProperties": false
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|